├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── admob-lib ├── admob.gd └── icon.png ├── admob-plugin ├── .gitignore ├── .idea │ ├── .name │ ├── codeStyles │ │ └── Project.xml │ ├── compiler.xml │ ├── gradle.xml │ ├── jarRepositories.xml │ ├── misc.xml │ └── vcs.xml ├── .project ├── .settings │ └── org.eclipse.buildship.core.prefs ├── app │ ├── .classpath │ ├── .gitignore │ ├── .project │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── shinnil │ │ │ └── godot │ │ │ └── plugin │ │ │ └── android │ │ │ └── godotadmob │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── shinnil │ │ └── godot │ │ └── plugin │ │ └── android │ │ └── godotadmob │ │ └── ExampleUnitTest.java ├── build.gradle ├── godot-lib.release │ ├── .gitignore │ ├── .project │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── build.gradle │ └── put-godot-lib.release.aar.here ├── godotadmob │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── shinnil │ │ │ └── godot │ │ │ └── plugin │ │ │ └── android │ │ │ └── godotadmob │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── shinnil │ │ │ └── godot │ │ │ └── plugin │ │ │ └── android │ │ │ └── godotadmob │ │ │ ├── Banner.java │ │ │ ├── CMP.java │ │ │ ├── GodotAdMob.java │ │ │ ├── GodotRewardedVideo.java │ │ │ ├── Interstitial.java │ │ │ ├── RewardedInterstitial.java │ │ │ └── RewardedVideo.java │ │ └── test │ │ └── java │ │ └── shinnil │ │ └── godot │ │ └── plugin │ │ └── android │ │ └── godotadmob │ │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── version.gradle ├── config └── GodotAdMob.gdap ├── demo ├── admob-lib │ ├── admob.gd │ └── icon.png ├── export_presets.cfg ├── icon.png ├── main.gd ├── main.tscn └── project.godot ├── images ├── properties.png ├── search_node.png └── signals.png ├── issue_template.md ├── showcase.md └── tools └── package_build.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | #github: [Shin-NiL]# Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | #patreon: # Replace with a single Patreon username 5 | #open_collective: # Replace with a single Open Collective username 6 | ko_fi: shinnil 7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | #liberapay: # Replace with a single Liberapay username 10 | #issuehunt: # Replace with a single IssueHunt username 11 | #otechie: # Replace with a single Otechie username 12 | # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | custom: ['https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=3MJE3M4FMJYGN&lc=BR&item_name=Shin%2dNiL%27s%20Github&item_number=Github¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted'] 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | *.import 4 | .import 5 | .mono 6 | .idea 7 | *.o 8 | demo/android 9 | tools/releases 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Shin-NiL 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 | # GodotAdMob 2 | 3 | This is an Android AdMob plugin for Godot Engine (https://github.com/okamstudio/godot) 3.2.2 or higher. For Godot < 3.2 or iOS support you can use the [old module](https://github.com/kloder-games/godot-admob). 4 | 5 | Currently, this plugin supports: 6 | - Banner 7 | - Interstitial 8 | - Rewarded Video 9 | - [Rewarded Interstitial](https://developers.google.com/admob/android/rewarded-interstitial) 10 | 11 | ## Setup 12 | 13 | ### Video Guide 14 | Our friend *dQuigz* created a [nice video tutorial on how to use this plugin](https://youtu.be/0a6EvlNgLL0). 15 | 16 | ### Text Instructions 17 | - Configure, install and enable the "Android Custom Template" for your project, just follow the [official documentation](https://docs.godotengine.org/en/stable/getting_started/workflow/export/android_custom_build.html); 18 | - go to the [release tab](https://github.com/Shin-NiL/Godot-Android-AdMob-Plugin/releases), choose a version and download the respective ```GodotAdMobPlugin-x.x.x.zip``` package; 19 | - extract the content of the ```admob-plugin``` directory (```GodotAdmob.gdap``` and ```GodotAdmob.release.aar``` from the zip package) inside the ```res://android/plugins``` directory on your Godot project. 20 | - extract the ```admob-lib``` directory (from the zip package) inside the ```res://``` directory on your Godot project. 21 | - on the Project -> Export... -> Android -> Options -> 22 | - Permissions: check the permissions for _Access Network State_ and _Internet_ 23 | - Custom Template: check the _Use Custom Build_ 24 | - Plugins: check the _Godot Ad Mob_ (this plugin) 25 | - edit the file ```res://android/build/AndroidManifest.xml``` to add your App ID as described [here](https://developers.google.com/admob/android/quick-start#update_your_androidmanifestxml). For the demo project, for example, you should use: 26 | ``` 27 | 30 | ``` 31 | One good place to add this metadata is just below these lines, inside of the **application** tag: 32 | ``` 33 | 34 | ... 35 | 36 | 37 | 38 | 39 | Here 40 | 41 | ``` 42 | - To avoid the mergeDex error, enable the multidex support. Edit the file ```res://android/build/build.gradle``` and insert the line `multiDexEnabled = true` inside the `android` => `defaultConfig`: 43 | ``` 44 | android { 45 | defaultConfig { 46 | ... 47 | multiDexEnabled = true 48 | ... 49 | } 50 | } 51 | ``` 52 | 53 | 54 | **NOTE**: everytime you install a new version of the Android Build Template this step must be done again, as the ```AndroidManifest.xml``` file will be overriden. 55 | 56 | 57 | Now you'll be able to add an AdMob Node to your scene (**only one node should be added per scene**) 58 | 59 | ![Searching AdMob node](images/search_node.png) 60 | 61 | Edit its properties 62 | 63 | ![AdMob properties](images/properties.png) 64 | 65 | And connect its signals 66 | 67 | ![AdMob signals](images/signals.png) 68 | 69 | ## Sample Code 70 | 71 | In the demo directory you'll find a working sample project where you can see how the things works on the scripting side. 72 | 73 | __REMEMBER__: You still need to configure the project as described in the "Setup" section to be able to run the demo project. 74 | 75 | ## Showcase 76 | 77 | Does this plugin really work? Yes sir! You can see a list of published games [here](showcase.md). 78 | 79 | 80 | ## Donations 81 | 82 | Was this project useful for you? Wanna make a donation? These are the options: 83 | 84 | ### Paypal 85 | 86 | My [Paypal donation link](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=3MJE3M4FMJYGN&lc=BR&item_name=Shin%2dNiL%27s%20Github&item_number=Github¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted) 87 | 88 | ### Brave Browser 89 | If you're a Brave browser user, please consider donating some BATs ;) 90 | 91 | 92 | ## API Reference 93 | 94 | ### Properties 95 | ```python 96 | # If true use your real ad, if false use test ads. Make sure to only set it to true with your published apk, otherwise you can be banned by Google 97 | # type bool, default false 98 | is_real 99 | 100 | # If true, displays banner on the top of the screen, if false displays on the bottom 101 | # type bool, default true 102 | banner_on_top 103 | 104 | # The banner size constants 105 | # Valid values are: "ADAPTIVE_BANNER", "SMART_BANNER", "BANNER", "LARGE_BANNER", "MEDIUM_RECTANGLE", "FULL_BANNER", "LEADERBOARD" 106 | banner_size 107 | 108 | # Your app banner ad ID 109 | # type String, optional 110 | banner_id 111 | 112 | # Your app interstitial ad ID 113 | # type String, optional 114 | interstitial_id 115 | 116 | # Your app rewarded video ad ID 117 | # type String, optional 118 | rewarded_id 119 | 120 | # Your app rewarded interstitial ad ID 121 | # type String, optional 122 | rewarded_interstitial_id 123 | 124 | # If true, set the ads to children directed. If true, max_ad_content_rate will be ignored (your max_ad_content_rate would can not be other than "G") 125 | # type bool, default false 126 | child_directed 127 | 128 | # If ads should be personalized. In the European Economic Area, GDPR requires ad personalization to be opt-in. 129 | # type bool, default true 130 | is_personalized 131 | 132 | # Its value must be "G", "PG", "T" or "MA". If the rating of your app in Play Console and your config of max_ad_content_rate in AdMob are not matched, your app can be banned by Google 133 | # type String, default G 134 | max_ad_content_rate 135 | 136 | # If true, ads should be displayed after consent verification (calling method request_consent_info_update()) 137 | ads_using_consent 138 | 139 | # True for testing purpose only 140 | # False for production environment 141 | testing_consent 142 | ``` 143 | 144 | ### Methods 145 | ```python 146 | 147 | # Initialize AdMob on a background thread for optimization 148 | initialize_on_background_thread() 149 | 150 | # Load the banner (and show inmediatly) 151 | load_banner() 152 | 153 | # Load the interstitial ad 154 | load_interstitial() 155 | 156 | # Load the rewarded video ad 157 | load_rewarded_video() 158 | 159 | # Load the rewarded interstitial ad 160 | load_rewarded_interstitial() 161 | 162 | # Show the banner ad 163 | show_banner() 164 | 165 | # Hide the banner ad 166 | hide_banner() 167 | 168 | # Move banner after loaded 169 | move_banner(on_top: bool) 170 | 171 | # Show the interstitial ad 172 | show_interstitial() 173 | 174 | # Show the rewarded video ad 175 | show_rewarded_video() 176 | 177 | # Show the rewarded interstitial ad 178 | show_rewarded_interstitial() 179 | 180 | # Check if the interstitial ad is loaded 181 | # @return bool true if is loaded 182 | is_interstitial_loaded() 183 | 184 | # Check if the rewarded video ad is loaded 185 | # @return bool true if is loaded 186 | is_rewarded_video_loaded() 187 | 188 | # Check if the rewarded interstitial ad is loaded 189 | # @return bool true if is loaded 190 | is_rewarded_interstitial_loaded() 191 | 192 | # Resize the banner (useful when the orientation changes for example) 193 | banner_resize() 194 | 195 | # Get the current banner dimension 196 | # @return Vector2 (width, height) 197 | get_banner_dimension() 198 | 199 | # Verify consent status 200 | request_consent_info_update() 201 | 202 | # Reset consent status 203 | # Should by called only for testing purpose OR 'if you decide to remove the UMP SDK completely from your project.' 204 | # See: https://developers.google.com/admob/android/privacy#reset_consent_state 205 | reset_consent() 206 | 207 | ``` 208 | ### Signals 209 | ```python 210 | # AdMob Background Initialization Success 211 | admob_initialized 212 | 213 | # Banner ad was loaded with success 214 | banner_loaded 215 | 216 | # Banner ad has failed to load 217 | # @param int error_code the error code 218 | banner_failed_to_load(error_code) 219 | 220 | # Interstitial ad was loaded with success 221 | interstitial_loaded 222 | 223 | # Interstitial ad was opened 224 | interstitial_opened 225 | 226 | # Interstitial ad was closed 227 | interstitial_closed 228 | 229 | # Interstitial ad has failed to load 230 | # @param int error_code the error code 231 | interstitial_failed_to_load(error_code) 232 | 233 | # Interstitial ad has been clicked 234 | interstitial_clicked 235 | 236 | # The user has provided an interstitial impression. 237 | interstitial_impression 238 | 239 | # Rewarded video ad was loaded with success 240 | rewarded_video_loaded 241 | 242 | # Rewarded video ad was opened 243 | rewarded_video_opened 244 | 245 | # Rewarded video ad was closed 246 | rewarded_video_closed 247 | 248 | # Rewarded video ad has failed to load 249 | # @param int error_code the error code 250 | rewarded_video_failed_to_load(error_code) 251 | 252 | # Rewarded interstitial ad was loaded with success 253 | rewarded_interstitial_loaded 254 | 255 | # Rewarded interstitial ad was opened 256 | rewarded_interstitial_opened 257 | 258 | # Rewarded interstitial ad was closed 259 | rewarded_interstitial_closed 260 | 261 | # Rewarded interstitial ad has failed to load 262 | # @param int error_code the error code 263 | rewarded_interstitial_failed_to_load(error_code) 264 | 265 | # Rewarded interstitial ad has failed to show 266 | # @param int error_code the error code 267 | rewarded_interstitial_failed_to_show(error_code) 268 | 269 | # Rewarded video/interstitial ad was watched and will reward the user 270 | # @param String currency The reward item description, ex: coin 271 | # @param int amount The reward item amount 272 | rewarded(currency, amount) 273 | 274 | # Rewarded video was clicked 275 | rewarded_clicked 276 | 277 | # The user has given an impression for a rewarded video. 278 | rewarded_impression 279 | 280 | # Good for nothing. Only used for debug information. 281 | consent_info_update_success 282 | 283 | # Error on consent verification 284 | consent_info_update_failure(error_code, error_message) 285 | 286 | # Now you can show ads to user 287 | consent_app_can_request_ad(consent_status) 288 | ``` 289 | 290 | ## Compiling the Plugin (optional) 291 | 292 | If you want to compile the plugin by yourself, it's very easy: 293 | 1. Clone this repository; 294 | 2. Checkout the desired version; 295 | 3. Download the AAR library for Android plugin from the official Godot website; 296 | 4. Copy the downloaded AAR file into the `admob-plugin/godot-lib.release/` directory and rename it to `godot-lib.release.aar`; 297 | 5. Configure the demo project according to the "Setup" instructions. Install the "Android Custom Template" for the demo project. This generates the required build configuration. 298 | 6. Using command line go to the `admob-plugin/` directory; 299 | 7. Run `gradlew build`. 300 | 301 | If everything goes fine, you'll find the `.aar` files at `admob-plugin/godotadmob/build/outputs/aar/`. 302 | 303 | ## Troubleshooting 304 | 305 | * First of all, please make sure you're able to compile the custom build for Android without the AdMob plugin, this way we can isolate the cause of the issue. 306 | 307 | * Using logcat for Android is the best way to troubleshoot most issues. You can filter Godot only messages with logcat using the command: 308 | ``` 309 | adb logcat -s godot 310 | ``` 311 | * _AdMob Java Singleton not found_: 312 | 1. this plugin is Android only, so the AdMob Java singleton will only exists on the Android platform. In other words, you will be able to run it on an Android device (or emulator) only, it will not work on editor or on another platform; 313 | 2. make sure you checked the _Use Custom Build_ and _Godot Ad Mob_ options in the export window. 314 | 315 | * App is crashing at startup: use the `logcat` to check what's happening. It can be caused for many different reasons, if you forgot to or not configure correctly you App ID on the `AndroidManifest.xml`, for example, your app will crash. 316 | 317 | * Error code 3 (_ERROR_CODE_NO_FILL_) is a common issue with Admob, but out of the scope to this plugin. Here's the description on the API page: [ERROR_CODE_NO_FILL: The ad request was successful, but no ad was returned due to lack of ad inventory.](https://developers.google.com/android/reference/com/google/android/gms/ads/AdRequest.html#ERROR_CODE_NO_FILL) 318 | 319 | * Any other error code: you can find more information about the error codes [here](https://support.google.com/admob/thread/3494603). Please don't open issues on this repository asking for help about that, as we can't provide any, sorry. 320 | 321 | * Banner sizes: [Adaptive Banners](https://developers.google.com/admob/android/banner/adaptive) and [Smart Banners](https://developers.google.com/admob/android/banner/smart) uses dynamic banner sizes, the [other options](https://developers.google.com/admob/android/banner) uses fixed sizes, please check its respectives documentation for more details. Smart banners are deprecated and may not work properly. 322 | 323 | ## Optimize AdMob 324 | 325 | To prevent ["Application Not Responding" (ANR)](https://developer.android.com/topic/performance/vitals/anr) issues, you can follow some of the optimization strategies mentioned below. 326 | 327 | Before making any method calls to load ads, call `initialize_on_background_thread()` to initialize AdMob. The signal `admob_initialized` will be triggered, indicating that the initialization was successful. 328 | 329 | Alternatively, you can update your manifest with the optimization flag `OPTIMIZE_INITIALIZATION` to improve initialization without using a background thread. 330 | 331 | You can also use the `OPTIMIZE_AD_LOADING` flag to improve ad loading performance. 332 | 333 | Learn more at [Optimize initialization and ad loading (Beta)](https://developers.google.com/admob/android/optimize-initialization). 334 | 335 | 336 | ## References 337 | 338 | Based on the works of: 339 | * https://github.com/Mavhod/GodotAdmob 340 | * https://github.com/kloder-games/godot-admob 341 | 342 | ## License 343 | 344 | MIT license 345 | -------------------------------------------------------------------------------- /admob-lib/admob.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | class_name AdMob, "res://admob-lib/icon.png" 4 | 5 | # signals 6 | signal admob_initialized 7 | 8 | signal banner_loaded 9 | signal banner_failed_to_load(error_code) 10 | signal interstitial_failed_to_load(error_code) 11 | signal interstitial_loaded 12 | signal interstitial_opened 13 | signal interstitial_closed 14 | signal interstitial_clicked 15 | signal interstitial_impression 16 | signal rewarded_video_opened 17 | signal rewarded_video_loaded 18 | signal rewarded_video_closed 19 | signal rewarded_video_failed_to_load(error_code) 20 | signal rewarded_interstitial_opened 21 | signal rewarded_interstitial_loaded 22 | signal rewarded_interstitial_closed 23 | signal rewarded_interstitial_failed_to_load(error_code) 24 | signal rewarded_interstitial_failed_to_show(error_code) 25 | signal rewarded(currency, amount) 26 | signal rewarded_clicked 27 | signal rewarded_impression 28 | 29 | signal consent_info_update_success 30 | signal consent_info_update_failure(error_code, error_message) 31 | signal consent_app_can_request_ad(consent_status) 32 | 33 | 34 | # properties 35 | export var is_real:bool setget is_real_set 36 | export var banner_on_top:bool = true 37 | # SMART_BANNER is deprecated 38 | export(String, "ADAPTIVE_BANNER", "SMART_BANNER", "BANNER", "LARGE_BANNER", "MEDIUM_RECTANGLE", "FULL_BANNER", "LEADERBOARD") var banner_size = "ADAPTIVE_BANNER" 39 | export var banner_id:String 40 | export var interstitial_id:String 41 | export var rewarded_id:String 42 | export var rewarded_interstitial_id:String 43 | export var child_directed:bool = false setget child_directed_set 44 | export var is_personalized:bool = true setget is_personalized_set 45 | export(String, "G", "PG", "T", "MA") var max_ad_content_rate = "G" setget max_ad_content_rate_set 46 | 47 | # Testing consent flag 48 | export var ads_using_consent:bool setget ads_using_consent 49 | export var testing_consent:bool setget testing_consent_set 50 | 51 | 52 | # "private" properties 53 | var _admob_singleton = null 54 | var _is_interstitial_loaded:bool = false 55 | var _is_rewarded_video_loaded:bool = false 56 | var _is_rewarded_interstitial_loaded:bool = false 57 | 58 | 59 | func _enter_tree(): 60 | if not init(): 61 | print("AdMob Java Singleton not found. This plugin will only work on Android") 62 | 63 | # setters 64 | func is_real_set(new_val) -> void: 65 | is_real = new_val 66 | # warning-ignore:return_value_discarded 67 | init() 68 | 69 | func testing_consent_set(new_val) -> void: 70 | testing_consent = new_val 71 | 72 | func ads_using_consent(new_val) -> void: 73 | ads_using_consent = new_val 74 | 75 | func child_directed_set(new_val) -> void: 76 | child_directed = new_val 77 | # warning-ignore:return_value_discarded 78 | init() 79 | 80 | func is_personalized_set(new_val) -> void: 81 | is_personalized = new_val 82 | # warning-ignore:return_value_discarded 83 | init() 84 | 85 | func max_ad_content_rate_set(new_val) -> void: 86 | if new_val != "G" and new_val != "PG" \ 87 | and new_val != "T" and new_val != "MA": 88 | 89 | max_ad_content_rate = "G" 90 | print("Invalid max_ad_content_rate, using 'G'") 91 | else: 92 | max_ad_content_rate = new_val 93 | init() 94 | 95 | 96 | # initialization 97 | func init() -> bool: 98 | if(Engine.has_singleton("GodotAdMob")): 99 | _admob_singleton = Engine.get_singleton("GodotAdMob") 100 | 101 | # check if one signal is already connected 102 | if not _admob_singleton.is_connected("on_admob_ad_loaded", self, "_on_admob_ad_loaded"): 103 | connect_signals() 104 | 105 | _admob_singleton.initWithContentRating( 106 | is_real, 107 | child_directed, 108 | is_personalized, 109 | max_ad_content_rate 110 | ) 111 | return true 112 | return false 113 | 114 | # connect the AdMob Java signals 115 | func connect_signals() -> void: 116 | _admob_singleton.connect("on_admob_initialized", self, "_on_admob_initialized") 117 | 118 | _admob_singleton.connect("on_admob_ad_loaded", self, "_on_admob_ad_loaded") 119 | _admob_singleton.connect("on_admob_banner_failed_to_load", self, "_on_admob_banner_failed_to_load") 120 | _admob_singleton.connect("on_interstitial_failed_to_load", self, "_on_interstitial_failed_to_load") 121 | _admob_singleton.connect("on_interstitial_opened", self, "_on_interstitial_opened") 122 | _admob_singleton.connect("on_interstitial_loaded", self, "_on_interstitial_loaded") 123 | _admob_singleton.connect("on_interstitial_close", self, "_on_interstitial_close") 124 | _admob_singleton.connect("on_interstitial_clicked", self, "_on_interstitial_clicked") 125 | _admob_singleton.connect("on_interstitial_impression", self, "_on_interstitial_impression") 126 | _admob_singleton.connect("on_rewarded_video_ad_loaded", self, "_on_rewarded_video_ad_loaded") 127 | _admob_singleton.connect("on_rewarded_video_ad_opened", self, "_on_rewarded_video_ad_opened") 128 | _admob_singleton.connect("on_rewarded_video_ad_closed", self, "_on_rewarded_video_ad_closed") 129 | _admob_singleton.connect("on_rewarded_video_ad_failed_to_load", self, "_on_rewarded_video_ad_failed_to_load") 130 | _admob_singleton.connect("on_rewarded_interstitial_ad_loaded", self, "_on_rewarded_interstitial_ad_loaded") 131 | _admob_singleton.connect("on_rewarded_interstitial_ad_opened", self, "_on_rewarded_interstitial_ad_opened") 132 | _admob_singleton.connect("on_rewarded_interstitial_ad_closed", self, "_on_rewarded_interstitial_ad_closed") 133 | _admob_singleton.connect("on_rewarded_interstitial_ad_failed_to_load", self, "_on_rewarded_interstitial_ad_failed_to_load") 134 | _admob_singleton.connect("on_rewarded_interstitial_ad_failed_to_show", self, "_on_rewarded_interstitial_ad_failed_to_show") 135 | _admob_singleton.connect("on_rewarded", self, "_on_rewarded") 136 | _admob_singleton.connect("on_rewarded_clicked", self, "_on_rewarded_clicked") 137 | _admob_singleton.connect("on_rewarded_impression", self, "_on_rewarded_impression") 138 | 139 | _admob_singleton.connect("on_consent_info_update_success", self, "_on_consent_info_update_success") 140 | _admob_singleton.connect("on_consent_info_update_failure", self, "_on_consent_info_update_failure") 141 | _admob_singleton.connect("on_app_can_request_ads", self, "_on_app_can_request_ads") 142 | 143 | 144 | # initialize 145 | func initialize_on_background_thread() -> void: 146 | if _admob_singleton != null: 147 | _admob_singleton.initializeOnBackgroundThread() 148 | 149 | # load 150 | 151 | func load_banner() -> void: 152 | if _admob_singleton != null: 153 | _admob_singleton.loadBanner(banner_id, banner_on_top, banner_size) 154 | 155 | func load_interstitial() -> void: 156 | if _admob_singleton != null: 157 | _admob_singleton.loadInterstitial(interstitial_id) 158 | 159 | func is_interstitial_loaded() -> bool: 160 | if _admob_singleton != null: 161 | return _is_interstitial_loaded 162 | return false 163 | 164 | func load_rewarded_video() -> void: 165 | if _admob_singleton != null: 166 | _admob_singleton.loadRewardedVideo(rewarded_id) 167 | 168 | func is_rewarded_video_loaded() -> bool: 169 | if _admob_singleton != null: 170 | return _is_rewarded_video_loaded 171 | return false 172 | 173 | func load_rewarded_interstitial() -> void: 174 | if _admob_singleton != null: 175 | _admob_singleton.loadRewardedInterstitial(rewarded_interstitial_id) 176 | 177 | func is_rewarded_interstitial_loaded() -> bool: 178 | if _admob_singleton != null: 179 | return _is_rewarded_interstitial_loaded 180 | return false 181 | 182 | # show / hide 183 | 184 | func show_banner() -> void: 185 | if _admob_singleton != null: 186 | _admob_singleton.showBanner() 187 | 188 | func hide_banner() -> void: 189 | if _admob_singleton != null: 190 | _admob_singleton.hideBanner() 191 | 192 | func move_banner(on_top: bool) -> void: 193 | if _admob_singleton != null: 194 | banner_on_top = on_top 195 | _admob_singleton.move(banner_on_top) 196 | 197 | func show_interstitial() -> void: 198 | if _admob_singleton != null: 199 | _admob_singleton.showInterstitial() 200 | _is_interstitial_loaded = false 201 | 202 | func show_rewarded_video() -> void: 203 | if _admob_singleton != null: 204 | _admob_singleton.showRewardedVideo() 205 | _is_rewarded_video_loaded = false 206 | 207 | func show_rewarded_interstitial() -> void: 208 | if _admob_singleton != null: 209 | _admob_singleton.showRewardedInterstitial() 210 | _is_rewarded_interstitial_loaded = false 211 | 212 | # resize 213 | 214 | func banner_resize() -> void: 215 | if _admob_singleton != null: 216 | _admob_singleton.resize() 217 | 218 | # dimension 219 | func get_banner_dimension() -> Vector2: 220 | if _admob_singleton != null: 221 | return Vector2(_admob_singleton.getBannerWidth(), _admob_singleton.getBannerHeight()) 222 | return Vector2() 223 | 224 | func request_consent_info_update() -> void: 225 | if _admob_singleton != null: 226 | _admob_singleton.requestConsentInfoUpdate(testing_consent) 227 | 228 | func reset_consent() -> void: 229 | if _admob_singleton != null: 230 | _admob_singleton.resetConsentInformation() 231 | 232 | # callbacks 233 | 234 | func _on_admob_initialized() -> void: 235 | emit_signal("admob_initialized") 236 | 237 | func _on_admob_ad_loaded() -> void: 238 | emit_signal("banner_loaded") 239 | 240 | func _on_admob_banner_failed_to_load(error_code:int) -> void: 241 | emit_signal("banner_failed_to_load", error_code) 242 | 243 | func _on_interstitial_failed_to_load(error_code:int) -> void: 244 | _is_interstitial_loaded = false 245 | emit_signal("interstitial_failed_to_load", error_code) 246 | 247 | func _on_interstitial_opened() -> void: 248 | emit_signal("interstitial_opened") 249 | 250 | func _on_interstitial_loaded() -> void: 251 | _is_interstitial_loaded = true 252 | emit_signal("interstitial_loaded") 253 | 254 | func _on_interstitial_close() -> void: 255 | emit_signal("interstitial_closed") 256 | 257 | func _on_interstitial_clicked() -> void: 258 | emit_signal("interstitial_clicked") 259 | 260 | func _on_interstitial_impression() -> void: 261 | emit_signal("interstitial_impression") 262 | 263 | func _on_rewarded_video_ad_loaded() -> void: 264 | _is_rewarded_video_loaded = true 265 | emit_signal("rewarded_video_loaded") 266 | 267 | func _on_rewarded_video_ad_opened() -> void: 268 | emit_signal("rewarded_video_opened") 269 | 270 | func _on_rewarded_video_ad_closed() -> void: 271 | emit_signal("rewarded_video_closed") 272 | 273 | func _on_rewarded_video_ad_failed_to_load(error_code:int) -> void: 274 | _is_rewarded_video_loaded = false 275 | emit_signal("rewarded_video_failed_to_load", error_code) 276 | 277 | func _on_rewarded_interstitial_ad_opened() -> void: 278 | emit_signal("rewarded_interstitial_opened") 279 | 280 | func _on_rewarded_interstitial_ad_loaded() -> void: 281 | _is_rewarded_interstitial_loaded = true 282 | emit_signal("rewarded_interstitial_loaded") 283 | 284 | func _on_rewarded_interstitial_ad_closed() -> void: 285 | emit_signal("rewarded_interstitial_closed") 286 | 287 | func _on_rewarded_interstitial_ad_failed_to_load(error_code:int) -> void: 288 | _is_rewarded_interstitial_loaded = false 289 | emit_signal("rewarded_interstitial_failed_to_load", error_code) 290 | 291 | func _on_rewarded_interstitial_ad_failed_to_show(error_code:int) -> void: 292 | _is_rewarded_interstitial_loaded = false 293 | emit_signal("rewarded_interstitial_failed_to_show", error_code) 294 | 295 | func _on_rewarded(currency:String, amount:int) -> void: 296 | emit_signal("rewarded", currency, amount) 297 | 298 | func _on_rewarded_clicked() -> void: 299 | emit_signal("rewarded_clicked") 300 | 301 | func _on_rewarded_impression() -> void: 302 | emit_signal("rewarded_impression") 303 | 304 | func _on_consent_info_update_success() -> void: 305 | emit_signal("consent_info_update_success") 306 | 307 | func _on_consent_info_update_failure(error_code:int, error_message:String) -> void: 308 | emit_signal("consent_info_update_failure", error_code, error_message) 309 | 310 | func _on_app_can_request_ads(consent_status:int) -> void: 311 | emit_signal("consent_app_can_request_ad", consent_status) 312 | -------------------------------------------------------------------------------- /admob-lib/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-lib/icon.png -------------------------------------------------------------------------------- /admob-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | local.properties 4 | .DS_Store 5 | /build 6 | /captures 7 | .externalNativeBuild 8 | .cxx 9 | *.aar -------------------------------------------------------------------------------- /admob-plugin/.idea/.name: -------------------------------------------------------------------------------- 1 | GodotAdMob -------------------------------------------------------------------------------- /admob-plugin/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /admob-plugin/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /admob-plugin/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | -------------------------------------------------------------------------------- /admob-plugin/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /admob-plugin/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /admob-plugin/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /admob-plugin/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | GodotAdmob 4 | Project admob-plugin created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | 19 | 1659094184453 20 | 21 | 30 22 | 23 | org.eclipse.core.resources.regexFilterMatcher 24 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /admob-plugin/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | arguments= 2 | auto.sync=false 3 | build.scans.enabled=false 4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) 5 | connection.project.dir= 6 | eclipse.preferences.version=1 7 | gradle.user.home= 8 | java.home=C\:/Program Files/Java/jdk1.8.0_151 9 | jvm.arguments= 10 | offline.mode=false 11 | override.workspace.settings=true 12 | show.console.view=true 13 | show.executions.view=true 14 | -------------------------------------------------------------------------------- /admob-plugin/app/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /admob-plugin/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /admob-plugin/app/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app 4 | Project app created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | 25 | 1659094184463 26 | 27 | 30 28 | 29 | org.eclipse.core.resources.regexFilterMatcher 30 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /admob-plugin/app/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | arguments= 2 | auto.sync=false 3 | build.scans.enabled=false 4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(6.3)) 5 | connection.project.dir=.. 6 | eclipse.preferences.version=1 7 | gradle.user.home= 8 | java.home=C\:/Program Files/Java/jdk1.8.0_151 9 | jvm.arguments= 10 | offline.mode=false 11 | override.workspace.settings=true 12 | show.console.view=true 13 | show.executions.view=true 14 | -------------------------------------------------------------------------------- /admob-plugin/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdk versions.compileSdk 5 | buildToolsVersion versions.buildTools 6 | 7 | defaultConfig { 8 | applicationId "shinnil.godot.plugin.android.godotadmob" 9 | minSdkVersion versions.minSdk 10 | targetSdkVersion versions.targetSdk 11 | versionCode pluginVersionCode 12 | versionName pluginVersionName 13 | 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(dir: 'libs', include: ['*.jar']) 28 | 29 | implementation "androidx.appcompat:appcompat:${appcompatVersion}" 30 | testImplementation 'junit:junit:4.13.2' 31 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 32 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 33 | } 34 | -------------------------------------------------------------------------------- /admob-plugin/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /admob-plugin/app/src/androidTest/java/shinnil/godot/plugin/android/godotadmob/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package shinnil.godot.plugin.android.godotadmob; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("shinnil.godot.plugin.android.godotadmob", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /admob-plugin/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | 7 | -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | GodotAdMob 3 | 4 | -------------------------------------------------------------------------------- /admob-plugin/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /admob-plugin/app/src/test/java/shinnil/godot/plugin/android/godotadmob/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package shinnil.godot.plugin.android.godotadmob; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /admob-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | // TODO: Copy config.gradle from the build template of a specific Godot version. 3 | apply from: "../demo/android/build/config.gradle" 4 | apply from: 'version.gradle' 5 | 6 | buildscript { 7 | apply from: "../demo/android/build/config.gradle" 8 | 9 | repositories { 10 | google() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | classpath "com.android.tools.build:gradle:${versions.androidGradlePlugin}" 16 | 17 | 18 | // NOTE: Do not place your application dependencies here; they belong 19 | // in the individual module build.gradle files 20 | } 21 | } 22 | 23 | allprojects { 24 | repositories { 25 | google() 26 | mavenCentral() 27 | } 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /admob-plugin/godot-lib.release/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /admob-plugin/godot-lib.release/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | godot-lib.release 4 | Project godot-lib.release created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /admob-plugin/godot-lib.release/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | arguments= 2 | auto.sync=false 3 | build.scans.enabled=false 4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(6.3)) 5 | connection.project.dir=.. 6 | eclipse.preferences.version=1 7 | gradle.user.home= 8 | java.home=C\:/Program Files/Java/jdk1.8.0_151 9 | jvm.arguments= 10 | offline.mode=false 11 | override.workspace.settings=true 12 | show.console.view=true 13 | show.executions.view=true 14 | -------------------------------------------------------------------------------- /admob-plugin/godot-lib.release/build.gradle: -------------------------------------------------------------------------------- 1 | configurations.maybeCreate("default") 2 | artifacts.add("default", file('godot-lib.release.aar')) -------------------------------------------------------------------------------- /admob-plugin/godot-lib.release/put-godot-lib.release.aar.here: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/godot-lib.release/put-godot-lib.release.aar.here -------------------------------------------------------------------------------- /admob-plugin/godotadmob/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /admob-plugin/godotadmob/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | compileSdk versions.compileSdk 7 | buildToolsVersion versions.buildTools 8 | 9 | defaultConfig { 10 | minSdkVersion versions.minSdk 11 | targetSdkVersion versions.targetSdk 12 | versionCode pluginVersionCode 13 | versionName pluginVersionName 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | consumerProguardFiles 'consumer-rules.pro' 17 | } 18 | 19 | libraryVariants.all { variant -> 20 | variant.outputs.all { output -> 21 | output.outputFileName = "GodotAdMob.$pluginVersionName.${variant.name}.aar" 22 | } 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | 32 | } 33 | 34 | dependencies { 35 | implementation fileTree(dir: 'libs', include: ['*.jar']) 36 | 37 | compileOnly project(':godot-lib.release') 38 | implementation "com.google.android.gms:play-services-ads:${playServicesAdsVersion}" 39 | 40 | implementation "androidx.appcompat:appcompat:${appcompatVersion}" 41 | testImplementation 'junit:junit:4.13.2' 42 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 44 | 45 | implementation 'com.google.android.ump:user-messaging-platform:2.0.0' 46 | } 47 | -------------------------------------------------------------------------------- /admob-plugin/godotadmob/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/godotadmob/consumer-rules.pro -------------------------------------------------------------------------------- /admob-plugin/godotadmob/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /admob-plugin/godotadmob/src/androidTest/java/shinnil/godot/plugin/android/godotadmob/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package shinnil.godot.plugin.android.godotadmob; 2 | 3 | import android.content.Context; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | import androidx.test.ext.junit.runners.AndroidJUnit4; 9 | import androidx.test.platform.app.InstrumentationRegistry; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("shinnil.godot.plugin.android.godotadmob.test", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /admob-plugin/godotadmob/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /admob-plugin/godotadmob/src/main/java/shinnil/godot/plugin/android/godotadmob/Banner.java: -------------------------------------------------------------------------------- 1 | package shinnil.godot.plugin.android.godotadmob; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Color; 5 | import android.util.DisplayMetrics; 6 | import android.util.Log; 7 | import android.view.Display; 8 | import android.view.Gravity; 9 | import android.view.View; 10 | import android.widget.FrameLayout; 11 | 12 | import androidx.annotation.NonNull; 13 | 14 | import com.google.android.gms.ads.AdListener; 15 | import com.google.android.gms.ads.AdRequest; 16 | import com.google.android.gms.ads.AdSize; 17 | import com.google.android.gms.ads.AdView; 18 | import com.google.android.gms.ads.LoadAdError; 19 | 20 | interface BannerListener { 21 | void onBannerLoaded(); 22 | void onBannerFailedToLoad(int errorCode); 23 | } 24 | 25 | public class Banner { 26 | private AdView adView = null; // Banner view 27 | private final FrameLayout layout; 28 | private FrameLayout.LayoutParams adParams = null; 29 | private final AdRequest adRequest; 30 | private final Activity activity; 31 | private final String bannerSize; 32 | 33 | 34 | public Banner(final String id, final AdRequest adRequest, final Activity activity, final BannerListener defaultBannerListener, final boolean isOnTop, final FrameLayout layout, final String bannerSize) { 35 | this.activity = activity; 36 | this.layout = layout; 37 | this.adRequest = adRequest; 38 | this.bannerSize = bannerSize; 39 | 40 | AddBanner(id, (isOnTop ? Gravity.TOP : Gravity.BOTTOM), getAdSize(bannerSize), new AdListener() { 41 | @Override 42 | public void onAdLoaded() { 43 | Log.w("godot", "AdMob: onAdLoaded: banner"); 44 | defaultBannerListener.onBannerLoaded(); 45 | } 46 | 47 | @Override 48 | public void onAdFailedToLoad(@NonNull LoadAdError adError) { 49 | Log.w("godot", "AdMob: onAdFailedToLoad. errorCode: " + adError.getCode()); 50 | defaultBannerListener.onBannerFailedToLoad(adError.getCode()); 51 | } 52 | }); 53 | } 54 | 55 | public void show() { 56 | if (adView == null) { 57 | Log.w("w", "AdMob: showBanner - banner not loaded"); 58 | return; 59 | } 60 | 61 | if (adView.getVisibility() == View.VISIBLE) { 62 | return; 63 | } 64 | 65 | adView.setVisibility(View.VISIBLE); 66 | adView.resume(); 67 | Log.d("godot", "AdMob: Show Banner"); 68 | } 69 | 70 | public void move(final boolean isOnTop) 71 | { 72 | if (layout == null || adView == null || adParams == null) { 73 | return; 74 | } 75 | 76 | layout.removeView(adView); // Remove the old view 77 | 78 | AdListener adListener = adView.getAdListener(); 79 | String id = adView.getAdUnitId(); 80 | AddBanner(id, (isOnTop ? Gravity.TOP : Gravity.BOTTOM), adView.getAdSize(), adListener); 81 | 82 | Log.d("godot", "AdMob: Banner Moved"); 83 | } 84 | 85 | public void resize() { 86 | if (layout == null || adView == null || adParams == null) { 87 | return; 88 | } 89 | 90 | layout.removeView(adView); // Remove the old view 91 | 92 | AdListener adListener = adView.getAdListener(); 93 | String id = adView.getAdUnitId(); 94 | AddBanner(id, adParams.gravity, getAdSize(bannerSize), adListener); 95 | 96 | Log.d("godot", "AdMob: Banner Resized"); 97 | } 98 | 99 | private void AddBanner(final String id, final int gravity, final AdSize size, final AdListener listener) { 100 | adParams = new FrameLayout.LayoutParams( 101 | FrameLayout.LayoutParams.MATCH_PARENT, 102 | FrameLayout.LayoutParams.WRAP_CONTENT 103 | ); 104 | adParams.gravity = gravity; 105 | 106 | // Create new view & set old params 107 | adView = new AdView(activity); 108 | adView.setAdUnitId(id); 109 | adView.setBackgroundColor(Color.TRANSPARENT); 110 | adView.setAdSize(size); 111 | adView.setAdListener(listener); 112 | 113 | // Add to layout and load ad 114 | layout.addView(adView, adParams); 115 | 116 | // Request 117 | adView.loadAd(adRequest); 118 | } 119 | 120 | public void remove() { 121 | if (adView != null) { 122 | layout.removeView(adView); // Remove the old view 123 | } 124 | } 125 | 126 | public void hide() { 127 | if (adView.getVisibility() == View.GONE) return; 128 | adView.setVisibility(View.GONE); 129 | adView.pause(); 130 | Log.d("godot", "AdMob: Hide Banner"); 131 | } 132 | 133 | private AdSize getAdaptiveAdSize() { 134 | // Determine the screen width (less decorations) to use for the ad width. 135 | Display display = activity.getWindowManager().getDefaultDisplay(); 136 | DisplayMetrics outMetrics = new DisplayMetrics(); 137 | display.getMetrics(outMetrics); 138 | 139 | float widthPixels = outMetrics.widthPixels; 140 | float density = outMetrics.density; 141 | 142 | int adWidth = (int) (widthPixels / density); 143 | 144 | // Get adaptive ad size and return for setting on the ad view. 145 | return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(activity, adWidth); 146 | } 147 | 148 | private AdSize getAdSize(final String bannerSize) { 149 | switch (bannerSize) { 150 | case "SMART_BANNER": 151 | //noinspection deprecation 152 | return AdSize.SMART_BANNER; 153 | case "BANNER": 154 | return AdSize.BANNER; 155 | case "LARGE_BANNER": 156 | return AdSize.LARGE_BANNER; 157 | case "MEDIUM_RECTANGLE": 158 | return AdSize.MEDIUM_RECTANGLE; 159 | case "FULL_BANNER": 160 | return AdSize.FULL_BANNER; 161 | case "LEADERBOARD": 162 | return AdSize.LEADERBOARD; 163 | default: 164 | return getAdaptiveAdSize(); 165 | } 166 | } 167 | 168 | public int getWidth() { 169 | return getAdSize(bannerSize).getWidthInPixels(activity); 170 | } 171 | 172 | public int getHeight() { 173 | return getAdSize(bannerSize).getHeightInPixels(activity); 174 | } 175 | 176 | 177 | } -------------------------------------------------------------------------------- /admob-plugin/godotadmob/src/main/java/shinnil/godot/plugin/android/godotadmob/CMP.java: -------------------------------------------------------------------------------- 1 | package shinnil.godot.plugin.android.godotadmob; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | 9 | import com.google.android.gms.ads.AdRequest; 10 | import com.google.android.gms.ads.MobileAds; 11 | import com.google.android.ump.ConsentDebugSettings; 12 | import com.google.android.ump.ConsentForm; 13 | import com.google.android.ump.ConsentInformation; 14 | import com.google.android.ump.ConsentRequestParameters; 15 | import com.google.android.ump.FormError; 16 | import com.google.android.ump.UserMessagingPlatform; 17 | 18 | import org.godotengine.godot.GodotLib; 19 | 20 | interface CMPListener { 21 | void onConsentInfoUpdateSuccess(); 22 | void onConsentInfoUpdateFailure(int errorCode, String errorMessage); 23 | 24 | void onAppCanRequestAds(int consentStatus); 25 | } 26 | 27 | public class CMP { 28 | private ConsentInformation consentInformation; 29 | private ConsentForm consentForm; 30 | 31 | private final Activity activity; 32 | private final CMPListener defaultCMPListener; 33 | 34 | public CMP(Activity activity, 35 | final boolean testingConsent, 36 | final String testingDeviceId, 37 | CMPListener defaultCMPListener) { 38 | this.activity = activity; 39 | this.defaultCMPListener = defaultCMPListener; 40 | 41 | consentInformation = UserMessagingPlatform.getConsentInformation(this.activity); 42 | 43 | // Set tag for under age of consent. false means users are not under 44 | // age. 45 | ConsentRequestParameters params = new ConsentRequestParameters 46 | .Builder() 47 | .setTagForUnderAgeOfConsent(false) 48 | .build(); 49 | 50 | Log.w("godot", "Consent status: " + 51 | String.valueOf(consentInformation.getConsentStatus())); 52 | 53 | if(testingConsent) { 54 | 55 | ConsentDebugSettings debugSettings = new ConsentDebugSettings.Builder(this.activity) 56 | .setDebugGeography(ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_EEA) 57 | .addTestDeviceHashedId(testingDeviceId) 58 | .build(); 59 | 60 | params = new ConsentRequestParameters 61 | .Builder() 62 | .setConsentDebugSettings(debugSettings) 63 | .build(); 64 | 65 | Log.w("godot", "Consent status: " + 66 | String.valueOf(consentInformation.getConsentStatus())); 67 | } 68 | 69 | consentInformation.requestConsentInfoUpdate( 70 | this.activity, 71 | params, 72 | new ConsentInformation.OnConsentInfoUpdateSuccessListener() { 73 | @Override 74 | public void onConsentInfoUpdateSuccess() { 75 | // The consent information state was updated. 76 | // You are now ready to check if a form is available. 77 | Log.w("godot", "AdMob: onConsentInfoUpdateSuccess"); 78 | 79 | defaultCMPListener.onConsentInfoUpdateSuccess(); 80 | 81 | if (consentInformation.isConsentFormAvailable()) { 82 | Log.w("godot", "AdMob: Consent information available."); 83 | loadForm(); 84 | }else{ 85 | Log.w("godot", "AdMob: No consent information."); 86 | Log.w("godot", "Consent status: " + 87 | String.valueOf(consentInformation.getConsentStatus())); 88 | 89 | if(ConsentInformation.ConsentStatus.NOT_REQUIRED == 90 | consentInformation.getConsentStatus()){ 91 | defaultCMPListener.onAppCanRequestAds(consentInformation.getConsentStatus()); 92 | } 93 | } 94 | } 95 | }, 96 | new ConsentInformation.OnConsentInfoUpdateFailureListener() { 97 | @Override 98 | public void onConsentInfoUpdateFailure(FormError formError) { 99 | // Handle the error. 100 | Log.w("godot", "AdMob: onConsentInfoUpdateFailure: " 101 | + formError.getErrorCode() 102 | + " - " 103 | + formError.getMessage() 104 | ); 105 | defaultCMPListener.onConsentInfoUpdateFailure(formError.getErrorCode(), 106 | formError.getMessage()); 107 | } 108 | }); 109 | } 110 | 111 | public void resetConsentInformation(){ 112 | if(consentInformation != null) { 113 | consentInformation.reset(); 114 | } 115 | } 116 | 117 | public void loadForm() { 118 | // Loads a consent form. Must be called on the main thread. 119 | UserMessagingPlatform.loadConsentForm( 120 | this.activity, 121 | new UserMessagingPlatform.OnConsentFormLoadSuccessListener() { 122 | @Override 123 | public void onConsentFormLoadSuccess(ConsentForm consentForm) { 124 | CMP.this.consentForm = consentForm; 125 | Log.w("godot", "AdMob: onConsentFormLoadSuccess"); 126 | if (consentInformation.getConsentStatus() == ConsentInformation.ConsentStatus.REQUIRED) { 127 | consentForm.show( 128 | CMP.this.activity, 129 | new ConsentForm.OnConsentFormDismissedListener() { 130 | @Override 131 | public void onConsentFormDismissed(@Nullable FormError formError) { 132 | if (consentInformation.getConsentStatus() == ConsentInformation.ConsentStatus.OBTAINED) { 133 | // App can start requesting ads. 134 | Log.w("godot", "AdMob: App can start requesting ads."); 135 | defaultCMPListener.onAppCanRequestAds(consentInformation.getConsentStatus()); 136 | } 137 | 138 | // Handle dismissal by reloading form. 139 | loadForm(); 140 | } 141 | }); 142 | }else if(consentInformation.getConsentStatus() == ConsentInformation.ConsentStatus.OBTAINED){ 143 | Log.w("godot", "AdMob: App can start requesting ads."); 144 | defaultCMPListener.onAppCanRequestAds(consentInformation.getConsentStatus()); 145 | } 146 | } 147 | }, 148 | new UserMessagingPlatform.OnConsentFormLoadFailureListener() { 149 | @Override 150 | public void onConsentFormLoadFailure(FormError formError) { 151 | // Handle the error. 152 | Log.w("godot", "AdMob: onConsentInfoUpdateFailure: " 153 | + formError.getErrorCode() 154 | + " - " 155 | + formError.getMessage() 156 | ); 157 | } 158 | } 159 | ); 160 | } 161 | 162 | } -------------------------------------------------------------------------------- /admob-plugin/godotadmob/src/main/java/shinnil/godot/plugin/android/godotadmob/GodotAdMob.java: -------------------------------------------------------------------------------- 1 | package shinnil.godot.plugin.android.godotadmob; 2 | 3 | import static com.google.android.gms.ads.RequestConfiguration.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE; 4 | 5 | import android.annotation.SuppressLint; 6 | import android.app.Activity; 7 | import android.os.Bundle; 8 | import android.provider.Settings; 9 | import android.util.Log; 10 | import android.view.View; 11 | import android.widget.FrameLayout; 12 | 13 | import androidx.annotation.NonNull; 14 | import androidx.collection.ArraySet; 15 | 16 | import com.google.ads.mediation.admob.AdMobAdapter; 17 | import com.google.android.gms.ads.AdRequest; 18 | import com.google.android.gms.ads.MobileAds; 19 | import com.google.android.gms.ads.RequestConfiguration; 20 | import com.google.android.gms.ads.initialization.InitializationStatus; 21 | import com.google.android.gms.ads.initialization.OnInitializationCompleteListener; 22 | 23 | import org.godotengine.godot.Godot; 24 | import org.godotengine.godot.GodotLib; 25 | import org.godotengine.godot.plugin.GodotPlugin; 26 | import org.godotengine.godot.plugin.SignalInfo; 27 | import org.godotengine.godot.plugin.UsedByGodot; 28 | 29 | import java.security.MessageDigest; 30 | import java.security.NoSuchAlgorithmException; 31 | import java.util.Arrays; 32 | import java.util.List; 33 | import java.util.Locale; 34 | import java.util.Set; 35 | 36 | @SuppressWarnings("unused") 37 | public class GodotAdMob extends GodotPlugin { 38 | private final Activity activity; // The main activity of the game 39 | 40 | private boolean isReal = false; // Store if is real or not 41 | private boolean isForChildDirectedTreatment = false; // Store if is children directed treatment desired 42 | private boolean isPersonalized = true; // ads are personalized by default, GDPR compliance within the European Economic Area may require you to disable personalization. 43 | private String maxAdContentRating = ""; // Store maxAdContentRating ("G", "PG", "T" or "MA") 44 | private Bundle extras = null; 45 | 46 | private FrameLayout layout = null; // Store the layout 47 | 48 | private RewardedVideo rewardedVideo = null; // Rewarded Video object 49 | private RewardedInterstitial rewardedInterstitial = null; // Rewarded Interstitial object 50 | private Interstitial interstitial = null; // Interstitial object 51 | private Banner banner = null; // Banner object 52 | private CMP cmp; // Google Consent Management Platform (CMP) 53 | 54 | 55 | public GodotAdMob(Godot godot) { 56 | super(godot); 57 | this.activity = getActivity(); 58 | } 59 | 60 | // create and add a new layout to Godot 61 | @Override 62 | public View onMainCreate(Activity activity) { 63 | layout = new FrameLayout(activity); 64 | return layout; 65 | } 66 | 67 | @NonNull 68 | @Override 69 | public String getPluginName() { 70 | return "GodotAdMob"; 71 | } 72 | 73 | @NonNull 74 | @Override 75 | public Set getPluginSignals() { 76 | Set signals = new ArraySet<>(); 77 | 78 | signals.add(new SignalInfo("on_admob_initialized")); 79 | 80 | signals.add(new SignalInfo("on_admob_ad_loaded")); 81 | signals.add(new SignalInfo("on_admob_banner_failed_to_load", Integer.class)); 82 | 83 | signals.add(new SignalInfo("on_interstitial_loaded")); 84 | signals.add(new SignalInfo("on_interstitial_failed_to_load", Integer.class)); 85 | signals.add(new SignalInfo("on_interstitial_close")); 86 | signals.add(new SignalInfo("on_interstitial_opened")); 87 | signals.add(new SignalInfo("on_interstitial_clicked")); 88 | signals.add(new SignalInfo("on_interstitial_impression")); 89 | 90 | signals.add(new SignalInfo("on_rewarded_video_ad_closed")); 91 | signals.add(new SignalInfo("on_rewarded_video_ad_failed_to_load", Integer.class)); 92 | signals.add(new SignalInfo("on_rewarded_video_ad_loaded")); 93 | signals.add(new SignalInfo("on_rewarded_video_ad_opened")); 94 | 95 | signals.add(new SignalInfo("on_rewarded_interstitial_ad_loaded")); 96 | signals.add(new SignalInfo("on_rewarded_interstitial_ad_opened")); 97 | signals.add(new SignalInfo("on_rewarded_interstitial_ad_closed")); 98 | signals.add(new SignalInfo("on_rewarded_interstitial_ad_failed_to_load", Integer.class)); 99 | signals.add(new SignalInfo("on_rewarded_interstitial_ad_failed_to_show", Integer.class)); 100 | 101 | signals.add(new SignalInfo("on_rewarded", String.class, Integer.class)); 102 | signals.add(new SignalInfo("on_rewarded_clicked")); 103 | signals.add(new SignalInfo("on_rewarded_impression")); 104 | 105 | signals.add(new SignalInfo("on_consent_info_update_success")); 106 | signals.add( 107 | new SignalInfo("on_consent_info_update_failure", 108 | Integer.class, String.class)); 109 | signals.add(new SignalInfo("on_app_can_request_ads", Integer.class)); 110 | 111 | return signals; 112 | } 113 | 114 | /* Init 115 | * ********************************************************************** */ 116 | 117 | /** 118 | * Prepare for work with AdMob 119 | * 120 | * @param isReal Tell if the environment is for real or test 121 | */ 122 | @UsedByGodot 123 | public void init(boolean isReal) { 124 | this.initWithContentRating(isReal, false, true, ""); 125 | } 126 | 127 | /** 128 | * Init with content rating additional options 129 | * 130 | * @param isReal Tell if the environment is for real or test 131 | * @param isForChildDirectedTreatment Target audience is children. 132 | * @param isPersonalized If ads should be personalized or not. 133 | * GDPR compliance within the European Economic Area requires that you 134 | * disable ad personalization if the user does not wish to opt into 135 | * ad personalization. 136 | * @param maxAdContentRating must be "G", "PG", "T" or "MA" 137 | */ 138 | @UsedByGodot 139 | public void initWithContentRating( 140 | boolean isReal, 141 | boolean isForChildDirectedTreatment, 142 | boolean isPersonalized, 143 | String maxAdContentRating) { 144 | 145 | this.isReal = isReal; 146 | this.isForChildDirectedTreatment = isForChildDirectedTreatment; 147 | this.isPersonalized = isPersonalized; 148 | this.maxAdContentRating = maxAdContentRating; 149 | 150 | this.setRequestConfigurations(); 151 | 152 | if (!isPersonalized) { 153 | // https://developers.google.com/admob/android/eu-consent#forward_consent_to_the_google_mobile_ads_sdk 154 | if (extras == null) { 155 | extras = new Bundle(); 156 | } 157 | extras.putString("npa", "1"); 158 | } 159 | 160 | Log.d("godot", "AdMob: init with content rating options"); 161 | } 162 | 163 | 164 | private void setRequestConfigurations() { 165 | if (!this.isReal) { 166 | List testDeviceIds = Arrays.asList(AdRequest.DEVICE_ID_EMULATOR, getAdMobDeviceId()); 167 | RequestConfiguration requestConfiguration = MobileAds.getRequestConfiguration() 168 | .toBuilder() 169 | .setTestDeviceIds(testDeviceIds) 170 | .build(); 171 | MobileAds.setRequestConfiguration(requestConfiguration); 172 | } 173 | 174 | if (this.isForChildDirectedTreatment) { 175 | RequestConfiguration requestConfiguration = MobileAds.getRequestConfiguration() 176 | .toBuilder() 177 | .setTagForChildDirectedTreatment(TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE) 178 | .build(); 179 | MobileAds.setRequestConfiguration(requestConfiguration); 180 | } 181 | 182 | // StringEquality false positive 183 | //noinspection StringEquality 184 | if (this.maxAdContentRating != null && this.maxAdContentRating != "") { 185 | RequestConfiguration requestConfiguration = MobileAds.getRequestConfiguration() 186 | .toBuilder() 187 | .setMaxAdContentRating(this.maxAdContentRating) 188 | .build(); 189 | MobileAds.setRequestConfiguration(requestConfiguration); 190 | } 191 | } 192 | 193 | 194 | /** 195 | * Returns AdRequest object constructed considering the extras. 196 | * 197 | * @return AdRequest object 198 | */ 199 | private AdRequest getAdRequest() { 200 | AdRequest.Builder adBuilder = new AdRequest.Builder(); 201 | AdRequest adRequest; 202 | if (!this.isForChildDirectedTreatment && extras != null) { 203 | adBuilder.addNetworkExtrasBundle(AdMobAdapter.class, extras); 204 | } 205 | 206 | adRequest = adBuilder.build(); 207 | return adRequest; 208 | } 209 | 210 | /** 211 | * To Initializes AdMob on a background thread to improve performance. 212 | */ 213 | @UsedByGodot 214 | public void initializeOnBackgroundThread() { 215 | new Thread(() -> { 216 | MobileAds.initialize(activity, new OnInitializationCompleteListener() { 217 | @Override 218 | public void onInitializationComplete(InitializationStatus initializationStatus) { 219 | emitSignal("on_admob_initialized"); 220 | } 221 | }); 222 | }).start(); 223 | } 224 | 225 | /* Rewarded Video 226 | * ********************************************************************** */ 227 | 228 | /** 229 | * Load a Rewarded Video 230 | * 231 | * @param id AdMod Rewarded video ID 232 | */ 233 | @UsedByGodot 234 | public void loadRewardedVideo(final String id) { 235 | activity.runOnUiThread(() -> { 236 | rewardedVideo = new RewardedVideo(activity, new RewardedVideoListener() { 237 | @Override 238 | public void onRewardedVideoLoaded() { 239 | emitSignal("on_rewarded_video_ad_loaded"); 240 | } 241 | 242 | @Override 243 | public void onRewardedVideoFailedToLoad(int errorCode) { 244 | emitSignal("on_rewarded_video_ad_failed_to_load", errorCode); 245 | } 246 | 247 | @Override 248 | public void onRewardedVideoOpened() { 249 | emitSignal("on_rewarded_video_ad_opened"); 250 | } 251 | 252 | @Override 253 | public void onRewardedVideoClosed() { 254 | emitSignal("on_rewarded_video_ad_closed"); 255 | } 256 | 257 | @Override 258 | public void onRewarded(String type, int amount) { 259 | emitSignal("on_rewarded", type, amount); 260 | } 261 | 262 | @Override 263 | public void onRewardedClicked() { 264 | emitSignal("on_rewarded_clicked"); 265 | } 266 | 267 | @Override 268 | public void onRewardedAdImpression() { 269 | emitSignal("on_rewarded_impression"); 270 | } 271 | }); 272 | rewardedVideo.load(id, getAdRequest()); 273 | }); 274 | } 275 | 276 | /** 277 | * Show a Rewarded Video 278 | */ 279 | @UsedByGodot 280 | public void showRewardedVideo() { 281 | activity.runOnUiThread(() -> { 282 | if (rewardedVideo == null) { 283 | return; 284 | } 285 | rewardedVideo.show(); 286 | }); 287 | } 288 | 289 | /* Rewarded Interstitial 290 | * ********************************************************************** */ 291 | 292 | /** 293 | * Load a Rewarded Interstitial 294 | * 295 | * @param id AdMod Rewarded interstitial ID 296 | */ 297 | @UsedByGodot 298 | public void loadRewardedInterstitial(final String id) { 299 | activity.runOnUiThread(() -> { 300 | rewardedInterstitial = new RewardedInterstitial(activity, new RewardedInterstitialListener() { 301 | @Override 302 | public void onRewardedInterstitialLoaded() { 303 | emitSignal("on_rewarded_interstitial_ad_loaded"); 304 | } 305 | 306 | @Override 307 | public void onRewardedInterstitialOpened() { 308 | emitSignal("on_rewarded_interstitial_ad_opened"); 309 | } 310 | 311 | @Override 312 | public void onRewardedInterstitialClosed() { 313 | emitSignal("on_rewarded_interstitial_ad_closed"); 314 | } 315 | 316 | @Override 317 | public void onRewardedInterstitialFailedToLoad(int errorCode) { 318 | emitSignal("on_rewarded_interstitial_ad_failed_to_load", errorCode); 319 | } 320 | 321 | @Override 322 | public void onRewardedInterstitialFailedToShow(int errorCode) { 323 | emitSignal("on_rewarded_interstitial_ad_failed_to_show", errorCode); 324 | } 325 | 326 | @Override 327 | public void onRewarded(String type, int amount) { 328 | emitSignal("on_rewarded", type, amount); 329 | } 330 | 331 | @Override 332 | public void onRewardedClicked() { 333 | emitSignal("on_rewarded_clicked"); 334 | } 335 | 336 | @Override 337 | public void onRewardedAdImpression() { 338 | emitSignal("on_rewarded_impression"); 339 | } 340 | }); 341 | rewardedInterstitial.load(id, getAdRequest()); 342 | }); 343 | } 344 | 345 | /** 346 | * Show a Rewarded Interstitial 347 | */ 348 | @UsedByGodot 349 | public void showRewardedInterstitial() { 350 | activity.runOnUiThread(() -> { 351 | if (rewardedInterstitial == null) { 352 | return; 353 | } 354 | rewardedInterstitial.show(); 355 | }); 356 | } 357 | 358 | 359 | /* Banner 360 | * ********************************************************************** */ 361 | 362 | /** 363 | * Load a banner 364 | * 365 | * @param id AdMod Banner ID 366 | * @param isOnTop To made the banner top or bottom 367 | */ 368 | @UsedByGodot 369 | public void loadBanner(final String id, final boolean isOnTop, final String bannerSize) { 370 | activity.runOnUiThread(() -> { 371 | if (banner != null) banner.remove(); 372 | banner = new Banner(id, getAdRequest(), activity, new BannerListener() { 373 | @Override 374 | public void onBannerLoaded() { 375 | emitSignal("on_admob_ad_loaded"); 376 | } 377 | 378 | @Override 379 | public void onBannerFailedToLoad(int errorCode) { 380 | emitSignal("on_admob_banner_failed_to_load", errorCode); 381 | } 382 | }, isOnTop, layout, bannerSize); 383 | }); 384 | } 385 | 386 | /** 387 | * Show the banner 388 | */ 389 | @UsedByGodot 390 | public void showBanner() { 391 | activity.runOnUiThread(() -> { 392 | if (banner != null) { 393 | banner.show(); 394 | } 395 | }); 396 | } 397 | 398 | /** 399 | * Resize the banner 400 | * @param isOnTop To made the banner top or bottom 401 | */ 402 | @UsedByGodot 403 | public void move(final boolean isOnTop) { 404 | activity.runOnUiThread(() -> { 405 | if (banner != null) { 406 | banner.move(isOnTop); 407 | } 408 | }); 409 | } 410 | 411 | /** 412 | * Resize the banner 413 | */ 414 | @UsedByGodot 415 | public void resize() { 416 | activity.runOnUiThread(() -> { 417 | if (banner != null) { 418 | banner.resize(); 419 | } 420 | }); 421 | } 422 | 423 | 424 | /** 425 | * Hide the banner 426 | */ 427 | @UsedByGodot 428 | public void hideBanner() { 429 | activity.runOnUiThread(() -> { 430 | if (banner != null) { 431 | banner.hide(); 432 | } 433 | }); 434 | } 435 | 436 | /** 437 | * Get the banner width 438 | * 439 | * @return int Banner width 440 | */ 441 | @UsedByGodot 442 | public int getBannerWidth() { 443 | if (banner != null) { 444 | return banner.getWidth(); 445 | } 446 | return 0; 447 | } 448 | 449 | /** 450 | * Get the banner height 451 | * 452 | * @return int Banner height 453 | */ 454 | @UsedByGodot 455 | public int getBannerHeight() { 456 | if (banner != null) { 457 | return banner.getHeight(); 458 | } 459 | return 0; 460 | } 461 | 462 | /* Interstitial 463 | * ********************************************************************** */ 464 | 465 | /** 466 | * Load a interstitial 467 | * 468 | * @param id AdMod Interstitial ID 469 | */ 470 | @UsedByGodot 471 | public void loadInterstitial(final String id) { 472 | activity.runOnUiThread(() -> interstitial = new Interstitial(id, getAdRequest(), activity, new InterstitialListener() { 473 | @Override 474 | public void onInterstitialLoaded() { 475 | emitSignal("on_interstitial_loaded"); 476 | } 477 | 478 | @Override 479 | public void onInterstitialFailedToLoad(int errorCode) { 480 | emitSignal("on_interstitial_failed_to_load", errorCode); 481 | } 482 | 483 | @Override 484 | public void onInterstitialOpened() { 485 | // Not Implemented 486 | emitSignal("on_interstitial_opened"); 487 | } 488 | 489 | @Override 490 | public void onInterstitialClosed() { 491 | emitSignal("on_interstitial_close"); 492 | } 493 | 494 | @Override 495 | public void onInterstitialClicked() { 496 | emitSignal("on_interstitial_clicked"); 497 | } 498 | 499 | @Override 500 | public void onInterstitialImpression() { 501 | emitSignal("on_interstitial_impression"); 502 | } 503 | })); 504 | } 505 | 506 | /** 507 | * Show the interstitial 508 | */ 509 | @UsedByGodot 510 | public void showInterstitial() { 511 | activity.runOnUiThread(() -> { 512 | if (interstitial != null) { 513 | interstitial.show(); 514 | } 515 | }); 516 | } 517 | 518 | /* ConsentInformation 519 | * ********************************************************************** */ 520 | @UsedByGodot 521 | public void requestConsentInfoUpdate(final boolean testingConsent){ 522 | 523 | activity.runOnUiThread(() -> cmp = new CMP(activity, 524 | testingConsent, 525 | testingConsent ? getAdMobDeviceId() : "", 526 | new CMPListener() { 527 | @Override 528 | public void onConsentInfoUpdateSuccess() { 529 | emitSignal("on_consent_info_update_success"); 530 | } 531 | 532 | @Override 533 | public void onConsentInfoUpdateFailure(int errorCode, String errorMessage) { 534 | emitSignal("on_consent_info_update_failure", errorCode, errorMessage); 535 | } 536 | 537 | @Override 538 | public void onAppCanRequestAds(int consentStatus) { 539 | emitSignal("on_app_can_request_ads", consentStatus); 540 | } 541 | })); 542 | } 543 | 544 | @UsedByGodot 545 | public void resetConsentInformation(){ 546 | Log.w("godot", "Removing consent: "); 547 | if(cmp != null) { 548 | cmp.resetConsentInformation(); 549 | } 550 | } 551 | 552 | /* Utils 553 | * ********************************************************************** */ 554 | 555 | /** 556 | * Generate MD5 for the deviceID 557 | * 558 | * @param s The string to generate de MD5 559 | * @return String The MD5 generated 560 | */ 561 | private String md5(final String s) { 562 | try { 563 | // Create MD5 Hash 564 | MessageDigest digest = MessageDigest.getInstance("MD5"); 565 | digest.update(s.getBytes()); 566 | byte[] messageDigest = digest.digest(); 567 | 568 | // Create Hex String 569 | StringBuilder hexString = new StringBuilder(); 570 | for (int i = 0; i < messageDigest.length; i++) { 571 | String h = Integer.toHexString(0xFF & messageDigest[i]); 572 | while (h.length() < 2) h = "0" + h; 573 | hexString.append(h); 574 | } 575 | return hexString.toString(); 576 | } catch (NoSuchAlgorithmException e) { 577 | //Logger.logStackTrace(TAG,e); 578 | } 579 | return ""; 580 | } 581 | 582 | /** 583 | * Get the Device ID for AdMob 584 | * 585 | * @return String Device ID 586 | */ 587 | private String getAdMobDeviceId() { 588 | @SuppressLint("HardwareIds") String android_id = Settings.Secure.getString(activity.getContentResolver(), Settings.Secure.ANDROID_ID); 589 | String deviceId = md5(android_id).toUpperCase(Locale.US); 590 | return deviceId; 591 | } 592 | 593 | } 594 | -------------------------------------------------------------------------------- /admob-plugin/godotadmob/src/main/java/shinnil/godot/plugin/android/godotadmob/GodotRewardedVideo.java: -------------------------------------------------------------------------------- 1 | package shinnil.godot.plugin.android.godotadmob; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.google.android.gms.ads.AdError; 9 | import com.google.android.gms.ads.AdRequest; 10 | import com.google.android.gms.ads.FullScreenContentCallback; 11 | import com.google.android.gms.ads.LoadAdError; 12 | import com.google.android.gms.ads.MobileAds; 13 | import com.google.android.gms.ads.rewarded.RewardedAd; 14 | import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback; 15 | 16 | import org.godotengine.godot.GodotLib; 17 | 18 | 19 | public class GodotRewardedVideo { 20 | private RewardedAd rewardedAd = null; 21 | private int instanceId; 22 | private Activity activity; 23 | 24 | public void init(Activity activity, final int instanceId) { 25 | this.activity = activity; 26 | this.instanceId = instanceId; 27 | MobileAds.initialize(activity); 28 | } 29 | 30 | public boolean isLoaded() { 31 | return rewardedAd != null; 32 | } 33 | 34 | public void load(final String id, AdRequest adRequest) { 35 | 36 | RewardedAd.load(activity, id, adRequest, new RewardedAdLoadCallback() { 37 | @Override 38 | public void onAdLoaded(@NonNull RewardedAd rewardedAd) { 39 | super.onAdLoaded(rewardedAd); 40 | setAd(rewardedAd); 41 | Log.w("godot", "AdMob: onAdLoaded"); 42 | GodotLib.calldeferred(instanceId, "_on_rewarded_video_ad_loaded", new Object[]{}); 43 | } 44 | 45 | @Override 46 | public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { 47 | super.onAdFailedToLoad(loadAdError); 48 | // safety 49 | setAd(null); 50 | Log.w("godot", "AdMob: onAdFailedToLoad. errorCode: " + loadAdError.getCode()); 51 | GodotLib.calldeferred(instanceId, "_on_rewarded_video_ad_failed_to_load", new Object[]{loadAdError.getCode()}); 52 | } 53 | }); 54 | } 55 | 56 | public void show() { 57 | if (rewardedAd != null) { 58 | rewardedAd.show(activity, rewardItem -> { 59 | Log.w("godot", "AdMob: " 60 | + String.format(" onRewarded! currency: %s amount: %d", rewardItem.getType(), rewardItem.getAmount())); 61 | GodotLib.calldeferred(instanceId, "_on_rewarded", 62 | new Object[]{rewardItem.getType(), rewardItem.getAmount()}); 63 | }); 64 | } 65 | } 66 | 67 | private void setAd(RewardedAd rewardedAd) { 68 | // Avoid memory leaks. 69 | if (this.rewardedAd != null) 70 | this.rewardedAd.setFullScreenContentCallback(null); 71 | if (rewardedAd != null) { 72 | rewardedAd.setFullScreenContentCallback(new FullScreenContentCallback() { 73 | @Override 74 | public void onAdClicked() { 75 | super.onAdClicked(); 76 | Log.w("godot", "AdMob: onAdClicked"); 77 | GodotLib.calldeferred(instanceId, "_on_rewarded_clicked", new Object[]{}); 78 | } 79 | 80 | @Override 81 | public void onAdDismissedFullScreenContent() { 82 | super.onAdDismissedFullScreenContent(); 83 | // TODO: Test if new video ads are loaded 84 | // setAd(null); 85 | // RewardedAd.load(activity, id, adRequest, rewardedAdLoadCallback); 86 | Log.w("godot", "AdMob: onAdDismissedFullScreenContent"); 87 | GodotLib.calldeferred(instanceId, "_on_rewarded_video_ad_closed", new Object[]{}); 88 | } 89 | 90 | @Override 91 | public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) { 92 | super.onAdFailedToShowFullScreenContent(adError); 93 | Log.w("godot", "AdMob: onAdFailedToShowFullScreenContent"); 94 | GodotLib.calldeferred(instanceId, "_on_rewarded_video_ad_failed_to_load", new Object[]{adError.getCode()}); 95 | } 96 | 97 | @Override 98 | public void onAdImpression() { 99 | super.onAdImpression(); 100 | Log.w("godot", "AdMob: onAdImpression"); 101 | GodotLib.calldeferred(instanceId, "_on_rewarded_impression", new Object[]{}); 102 | } 103 | 104 | @Override 105 | public void onAdShowedFullScreenContent() { 106 | super.onAdShowedFullScreenContent(); 107 | Log.w("godot", "AdMob: onAdShowedFullScreenContent"); 108 | GodotLib.calldeferred(instanceId, "_on_rewarded_video_ad_opened", new Object[]{}); 109 | } 110 | }); 111 | } 112 | this.rewardedAd = rewardedAd; 113 | } 114 | } -------------------------------------------------------------------------------- /admob-plugin/godotadmob/src/main/java/shinnil/godot/plugin/android/godotadmob/Interstitial.java: -------------------------------------------------------------------------------- 1 | package shinnil.godot.plugin.android.godotadmob; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.google.android.gms.ads.AdError; 9 | import com.google.android.gms.ads.AdRequest; 10 | import com.google.android.gms.ads.FullScreenContentCallback; 11 | import com.google.android.gms.ads.LoadAdError; 12 | import com.google.android.gms.ads.interstitial.InterstitialAd; 13 | import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback; 14 | 15 | interface InterstitialListener { 16 | void onInterstitialLoaded(); 17 | void onInterstitialFailedToLoad(int errorCode); 18 | void onInterstitialOpened(); 19 | void onInterstitialClosed(); 20 | // new 21 | void onInterstitialClicked(); 22 | void onInterstitialImpression(); 23 | } 24 | 25 | public class Interstitial { 26 | private final String id; 27 | private final AdRequest adRequest; 28 | private InterstitialAd interstitialAd = null; // Interstitial object 29 | private final Activity activity; 30 | private final InterstitialListener defaultInterstitialListener; 31 | 32 | public Interstitial(final String id, final AdRequest adRequest, final Activity activity, final InterstitialListener defaultInterstitialListener) { 33 | this.activity = activity; 34 | this.id = id; 35 | this.adRequest = adRequest; 36 | this.defaultInterstitialListener = defaultInterstitialListener; 37 | load(); 38 | } 39 | 40 | public void show() { 41 | if (interstitialAd != null) { 42 | interstitialAd.show(activity); 43 | } else { 44 | Log.w("w", "AdMob: showInterstitial - interstitial not loaded"); 45 | } 46 | } 47 | 48 | public boolean isLoaded() { 49 | return interstitialAd != null; 50 | } 51 | 52 | private void setAd(InterstitialAd interstitialAd) { 53 | if (interstitialAd == this.interstitialAd) 54 | return; 55 | // Avoid memory leaks 56 | if (this.interstitialAd != null) { 57 | this.interstitialAd.setFullScreenContentCallback(null); 58 | this.interstitialAd.setOnPaidEventListener(null); 59 | } 60 | if (interstitialAd != null) { 61 | interstitialAd.setFullScreenContentCallback(new FullScreenContentCallback() { 62 | @Override 63 | public void onAdClicked() { 64 | super.onAdClicked(); 65 | Log.w("godot", "AdMob: onAdClicked()"); 66 | defaultInterstitialListener.onInterstitialClicked(); 67 | } 68 | 69 | @Override 70 | public void onAdDismissedFullScreenContent() { 71 | super.onAdDismissedFullScreenContent(); 72 | // TODO: Test if new video ads are loaded 73 | setAd(null); 74 | Log.w("godot", "AdMob: onAdDismissedFullScreenContent"); 75 | defaultInterstitialListener.onInterstitialClosed(); 76 | load(); 77 | } 78 | 79 | @Override 80 | public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) { 81 | super.onAdFailedToShowFullScreenContent(adError); 82 | Log.w("godot", "AdMob: onAdFailedToShowFullScreenContent"); 83 | defaultInterstitialListener.onInterstitialFailedToLoad(adError.getCode()); 84 | } 85 | 86 | @Override 87 | public void onAdShowedFullScreenContent() { 88 | super.onAdShowedFullScreenContent(); 89 | Log.w("godot", "AdMob: onAdShowedFullScreenContent"); 90 | defaultInterstitialListener.onInterstitialOpened(); 91 | } 92 | 93 | @Override 94 | public void onAdImpression() { 95 | super.onAdImpression(); 96 | Log.w("godot", "AdMob: onAdImpression"); 97 | defaultInterstitialListener.onInterstitialImpression(); 98 | } 99 | }); 100 | } 101 | this.interstitialAd = interstitialAd; 102 | } 103 | 104 | private void load() { 105 | InterstitialAd.load(activity, id, adRequest, new InterstitialAdLoadCallback() { 106 | @Override 107 | public void onAdLoaded(@NonNull InterstitialAd interstitialAd) { 108 | super.onAdLoaded(interstitialAd); 109 | setAd(interstitialAd); 110 | Log.w("godot", "AdMob: onAdLoaded: interstitial"); 111 | defaultInterstitialListener.onInterstitialLoaded(); 112 | } 113 | 114 | @Override 115 | public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { 116 | super.onAdFailedToLoad(loadAdError); 117 | // safety 118 | setAd(null); 119 | Log.w("godot", "AdMob: onAdFailedToLoad(int errorCode) - error code: " + loadAdError.getCode()); 120 | defaultInterstitialListener.onInterstitialFailedToLoad(loadAdError.getCode()); 121 | } 122 | }); 123 | } 124 | } -------------------------------------------------------------------------------- /admob-plugin/godotadmob/src/main/java/shinnil/godot/plugin/android/godotadmob/RewardedInterstitial.java: -------------------------------------------------------------------------------- 1 | package shinnil.godot.plugin.android.godotadmob; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.google.android.gms.ads.AdError; 9 | import com.google.android.gms.ads.AdRequest; 10 | import com.google.android.gms.ads.FullScreenContentCallback; 11 | import com.google.android.gms.ads.LoadAdError; 12 | import com.google.android.gms.ads.MobileAds; 13 | import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd; 14 | import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAdLoadCallback; 15 | 16 | interface RewardedInterstitialListener { 17 | void onRewardedInterstitialLoaded(); 18 | void onRewardedInterstitialOpened(); 19 | void onRewardedInterstitialClosed(); 20 | void onRewardedInterstitialFailedToLoad(int errorCode); 21 | void onRewardedInterstitialFailedToShow(int errorCode); 22 | void onRewarded(String type, int amount); 23 | void onRewardedClicked(); 24 | void onRewardedAdImpression(); 25 | } 26 | 27 | public class RewardedInterstitial { 28 | private RewardedInterstitialAd rewardedAd = null; 29 | private final Activity activity; 30 | private final RewardedInterstitialListener defaultRewardedInterstitialListener; 31 | 32 | public RewardedInterstitial(Activity activity, final RewardedInterstitialListener defaultRewardedInterstitialListener) { 33 | this.activity = activity; 34 | this.defaultRewardedInterstitialListener = defaultRewardedInterstitialListener; 35 | MobileAds.initialize(activity); 36 | } 37 | 38 | public boolean isLoaded() { 39 | return rewardedAd != null; 40 | } 41 | 42 | public void load(final String id, AdRequest adRequest) { 43 | 44 | RewardedInterstitialAd.load(activity, id, adRequest, new RewardedInterstitialAdLoadCallback() { 45 | @Override 46 | public void onAdLoaded(@NonNull RewardedInterstitialAd rewardedAd) { 47 | super.onAdLoaded(rewardedAd); 48 | setAd(rewardedAd); 49 | Log.w("godot", "AdMob: onAdLoaded: rewarded interstitial"); 50 | defaultRewardedInterstitialListener.onRewardedInterstitialLoaded(); 51 | } 52 | 53 | @Override 54 | public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { 55 | super.onAdFailedToLoad(loadAdError); 56 | // safety 57 | setAd(null); 58 | Log.w("godot", "AdMob: onAdFailedToLoad. errorCode: " + loadAdError.getCode()); 59 | defaultRewardedInterstitialListener.onRewardedInterstitialFailedToLoad(loadAdError.getCode()); 60 | } 61 | }); 62 | } 63 | 64 | public void show() { 65 | if (rewardedAd != null) { 66 | rewardedAd.show(activity, rewardItem -> { 67 | Log.w("godot", "AdMob: " 68 | + String.format(" onRewarded! currency: %s amount: %d", rewardItem.getType(), rewardItem.getAmount())); 69 | defaultRewardedInterstitialListener.onRewarded(rewardItem.getType(), rewardItem.getAmount()); 70 | }); 71 | } 72 | } 73 | 74 | private void setAd(RewardedInterstitialAd rewardedAd) { 75 | // Avoid memory leaks. 76 | if (this.rewardedAd != null) 77 | this.rewardedAd.setFullScreenContentCallback(null); 78 | if (rewardedAd != null) { 79 | rewardedAd.setFullScreenContentCallback(new FullScreenContentCallback() { 80 | @Override 81 | public void onAdClicked() { 82 | super.onAdClicked(); 83 | Log.w("godot", "AdMob: onAdClicked"); 84 | defaultRewardedInterstitialListener.onRewardedClicked(); 85 | } 86 | 87 | @Override 88 | public void onAdDismissedFullScreenContent() { 89 | super.onAdDismissedFullScreenContent(); 90 | // TODO: Test if new video ads are loaded 91 | // setAd(null); 92 | // RewardedAd.load(activity, id, adRequest, rewardedAdLoadCallback); 93 | Log.w("godot", "AdMob: onAdDismissedFullScreenContent"); 94 | defaultRewardedInterstitialListener.onRewardedInterstitialClosed(); 95 | } 96 | 97 | @Override 98 | public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) { 99 | super.onAdFailedToShowFullScreenContent(adError); 100 | Log.w("godot", "AdMob: onAdFailedToShowFullScreenContent"); 101 | defaultRewardedInterstitialListener.onRewardedInterstitialFailedToShow(adError.getCode()); 102 | } 103 | 104 | @Override 105 | public void onAdImpression() { 106 | super.onAdImpression(); 107 | Log.w("godot", "AdMob: onAdImpression"); 108 | defaultRewardedInterstitialListener.onRewardedAdImpression(); 109 | } 110 | 111 | @Override 112 | public void onAdShowedFullScreenContent() { 113 | super.onAdShowedFullScreenContent(); 114 | Log.w("godot", "AdMob: onAdShowedFullScreenContent"); 115 | defaultRewardedInterstitialListener.onRewardedInterstitialOpened(); 116 | } 117 | }); 118 | } 119 | this.rewardedAd = rewardedAd; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /admob-plugin/godotadmob/src/main/java/shinnil/godot/plugin/android/godotadmob/RewardedVideo.java: -------------------------------------------------------------------------------- 1 | package shinnil.godot.plugin.android.godotadmob; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.google.android.gms.ads.AdError; 9 | import com.google.android.gms.ads.AdRequest; 10 | import com.google.android.gms.ads.FullScreenContentCallback; 11 | import com.google.android.gms.ads.LoadAdError; 12 | import com.google.android.gms.ads.MobileAds; 13 | import com.google.android.gms.ads.rewarded.RewardedAd; 14 | import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback; 15 | 16 | interface RewardedVideoListener { 17 | void onRewardedVideoLoaded(); 18 | void onRewardedVideoFailedToLoad(int errorCode); 19 | void onRewardedVideoOpened(); 20 | void onRewardedVideoClosed(); 21 | void onRewarded(String type, int amount); 22 | /* Removed in GMS Ads SDK version 19 or 20. 23 | void onRewardedVideoStarted(); 24 | void onRewardedVideoCompleted(); 25 | */ 26 | // new 27 | void onRewardedClicked(); 28 | void onRewardedAdImpression(); 29 | } 30 | 31 | public class RewardedVideo { 32 | private RewardedAd rewardedAd = null; 33 | private final Activity activity; 34 | private final RewardedVideoListener defaultRewardedVideoListener; 35 | 36 | public RewardedVideo(Activity activity, final RewardedVideoListener defaultRewardedVideoListener) { 37 | this.activity = activity; 38 | this.defaultRewardedVideoListener = defaultRewardedVideoListener; 39 | MobileAds.initialize(activity); 40 | } 41 | 42 | public boolean isLoaded() { 43 | return rewardedAd != null; 44 | } 45 | 46 | public void load(final String id, AdRequest adRequest) { 47 | 48 | RewardedAd.load(activity, id, adRequest, new RewardedAdLoadCallback() { 49 | @Override 50 | public void onAdLoaded(@NonNull RewardedAd rewardedAd) { 51 | super.onAdLoaded(rewardedAd); 52 | setAd(rewardedAd); 53 | Log.w("godot", "AdMob: onAdLoaded: rewarded video"); 54 | defaultRewardedVideoListener.onRewardedVideoLoaded(); 55 | } 56 | 57 | @Override 58 | public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { 59 | super.onAdFailedToLoad(loadAdError); 60 | // safety 61 | setAd(null); 62 | Log.w("godot", "AdMob: onAdFailedToLoad. errorCode: " + loadAdError.getCode()); 63 | defaultRewardedVideoListener.onRewardedVideoFailedToLoad(loadAdError.getCode()); 64 | } 65 | }); 66 | } 67 | 68 | public void show() { 69 | if (rewardedAd != null) { 70 | rewardedAd.show(activity, rewardItem -> { 71 | Log.w("godot", "AdMob: " 72 | + String.format(" onRewarded! currency: %s amount: %d", rewardItem.getType(), rewardItem.getAmount())); 73 | defaultRewardedVideoListener.onRewarded(rewardItem.getType(), rewardItem.getAmount()); 74 | }); 75 | } 76 | } 77 | 78 | private void setAd(RewardedAd rewardedAd) { 79 | // Avoid memory leaks. 80 | if (this.rewardedAd != null) 81 | this.rewardedAd.setFullScreenContentCallback(null); 82 | if (rewardedAd != null) { 83 | rewardedAd.setFullScreenContentCallback(new FullScreenContentCallback() { 84 | @Override 85 | public void onAdClicked() { 86 | super.onAdClicked(); 87 | Log.w("godot", "AdMob: onAdClicked"); 88 | defaultRewardedVideoListener.onRewardedClicked(); 89 | } 90 | 91 | @Override 92 | public void onAdDismissedFullScreenContent() { 93 | super.onAdDismissedFullScreenContent(); 94 | // TODO: Test if new video ads are loaded 95 | // setAd(null); 96 | // RewardedAd.load(activity, id, adRequest, rewardedAdLoadCallback); 97 | Log.w("godot", "AdMob: onAdDismissedFullScreenContent"); 98 | defaultRewardedVideoListener.onRewardedVideoClosed(); 99 | } 100 | 101 | @Override 102 | public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) { 103 | super.onAdFailedToShowFullScreenContent(adError); 104 | Log.w("godot", "AdMob: onAdFailedToShowFullScreenContent"); 105 | defaultRewardedVideoListener.onRewardedVideoFailedToLoad(adError.getCode()); 106 | } 107 | 108 | @Override 109 | public void onAdImpression() { 110 | super.onAdImpression(); 111 | Log.w("godot", "AdMob: onAdImpression"); 112 | defaultRewardedVideoListener.onRewardedAdImpression(); 113 | } 114 | 115 | @Override 116 | public void onAdShowedFullScreenContent() { 117 | super.onAdShowedFullScreenContent(); 118 | Log.w("godot", "AdMob: onAdShowedFullScreenContent"); 119 | defaultRewardedVideoListener.onRewardedVideoOpened(); 120 | } 121 | }); 122 | } 123 | this.rewardedAd = rewardedAd; 124 | } 125 | } -------------------------------------------------------------------------------- /admob-plugin/godotadmob/src/test/java/shinnil/godot/plugin/android/godotadmob/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package shinnil.godot.plugin.android.godotadmob; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /admob-plugin/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=-Xmx1536m 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 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | 21 | -------------------------------------------------------------------------------- /admob-plugin/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/admob-plugin/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /admob-plugin/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /admob-plugin/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original 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 POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /admob-plugin/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 | -------------------------------------------------------------------------------- /admob-plugin/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name='GodotAdMob' 2 | include ':app', ':godot-lib.release' 3 | include ':godotadmob' 4 | -------------------------------------------------------------------------------- /admob-plugin/version.gradle: -------------------------------------------------------------------------------- 1 | ext.pluginVersionCode = 8 2 | ext.pluginVersionName = "6.2.0" 3 | ext.playServicesAdsVersion = "22.2.0" 4 | ext.appcompatVersion = "1.5.1" 5 | -------------------------------------------------------------------------------- /config/GodotAdMob.gdap: -------------------------------------------------------------------------------- 1 | [config] 2 | 3 | name="GodotAdMob" 4 | binary_type="local" 5 | binary="GodotAdMob.6.2.0.release.aar" 6 | 7 | [dependencies] 8 | 9 | remote=["com.google.android.gms:play-services-ads:22.2.0", "androidx.appcompat:appcompat:1.5.1"] 10 | -------------------------------------------------------------------------------- /demo/admob-lib/admob.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | class_name AdMob, "res://admob-lib/icon.png" 4 | 5 | # signals 6 | signal banner_loaded 7 | signal banner_failed_to_load(error_code) 8 | signal interstitial_failed_to_load(error_code) 9 | signal interstitial_loaded 10 | signal interstitial_opened 11 | signal interstitial_closed 12 | signal interstitial_clicked 13 | signal interstitial_impression 14 | signal rewarded_video_opened 15 | signal rewarded_video_loaded 16 | signal rewarded_video_closed 17 | signal rewarded_video_failed_to_load(error_code) 18 | signal rewarded_interstitial_opened 19 | signal rewarded_interstitial_loaded 20 | signal rewarded_interstitial_closed 21 | signal rewarded_interstitial_failed_to_load(error_code) 22 | signal rewarded_interstitial_failed_to_show(error_code) 23 | signal rewarded(currency, amount) 24 | signal rewarded_clicked 25 | signal rewarded_impression 26 | 27 | signal consent_info_update_success 28 | signal consent_info_update_failure(error_code, error_message) 29 | signal consent_app_can_request_ad(consent_status) 30 | 31 | 32 | # properties 33 | export var is_real:bool setget is_real_set 34 | export var banner_on_top:bool = true 35 | # SMART_BANNER is deprecated 36 | export(String, "ADAPTIVE_BANNER", "SMART_BANNER", "BANNER", "LARGE_BANNER", "MEDIUM_RECTANGLE", "FULL_BANNER", "LEADERBOARD") var banner_size = "ADAPTIVE_BANNER" 37 | export var banner_id:String 38 | export var interstitial_id:String 39 | export var rewarded_id:String 40 | export var rewarded_interstitial_id:String 41 | export var child_directed:bool = false setget child_directed_set 42 | export var is_personalized:bool = true setget is_personalized_set 43 | export(String, "G", "PG", "T", "MA") var max_ad_content_rate = "G" setget max_ad_content_rate_set 44 | 45 | # Testing consent flag 46 | export var ads_using_consent:bool setget ads_using_consent 47 | export var testing_consent:bool setget testing_consent_set 48 | 49 | 50 | # "private" properties 51 | var _admob_singleton = null 52 | var _is_interstitial_loaded:bool = false 53 | var _is_rewarded_video_loaded:bool = false 54 | var _is_rewarded_interstitial_loaded:bool = false 55 | 56 | 57 | func _enter_tree(): 58 | if not init(): 59 | print("AdMob Java Singleton not found. This plugin will only work on Android") 60 | 61 | # setters 62 | func is_real_set(new_val) -> void: 63 | is_real = new_val 64 | # warning-ignore:return_value_discarded 65 | init() 66 | 67 | func testing_consent_set(new_val) -> void: 68 | testing_consent = new_val 69 | 70 | func ads_using_consent(new_val) -> void: 71 | ads_using_consent = new_val 72 | 73 | func child_directed_set(new_val) -> void: 74 | child_directed = new_val 75 | # warning-ignore:return_value_discarded 76 | init() 77 | 78 | func is_personalized_set(new_val) -> void: 79 | is_personalized = new_val 80 | # warning-ignore:return_value_discarded 81 | init() 82 | 83 | func max_ad_content_rate_set(new_val) -> void: 84 | if new_val != "G" and new_val != "PG" \ 85 | and new_val != "T" and new_val != "MA": 86 | 87 | max_ad_content_rate = "G" 88 | print("Invalid max_ad_content_rate, using 'G'") 89 | else: 90 | max_ad_content_rate = new_val 91 | init() 92 | 93 | 94 | # initialization 95 | func init() -> bool: 96 | if(Engine.has_singleton("GodotAdMob")): 97 | _admob_singleton = Engine.get_singleton("GodotAdMob") 98 | 99 | # check if one signal is already connected 100 | if not _admob_singleton.is_connected("on_admob_ad_loaded", self, "_on_admob_ad_loaded"): 101 | connect_signals() 102 | 103 | _admob_singleton.initWithContentRating( 104 | is_real, 105 | child_directed, 106 | is_personalized, 107 | max_ad_content_rate 108 | ) 109 | return true 110 | return false 111 | 112 | # connect the AdMob Java signals 113 | func connect_signals() -> void: 114 | _admob_singleton.connect("on_admob_ad_loaded", self, "_on_admob_ad_loaded") 115 | _admob_singleton.connect("on_admob_banner_failed_to_load", self, "_on_admob_banner_failed_to_load") 116 | _admob_singleton.connect("on_interstitial_failed_to_load", self, "_on_interstitial_failed_to_load") 117 | _admob_singleton.connect("on_interstitial_opened", self, "_on_interstitial_opened") 118 | _admob_singleton.connect("on_interstitial_loaded", self, "_on_interstitial_loaded") 119 | _admob_singleton.connect("on_interstitial_close", self, "_on_interstitial_close") 120 | _admob_singleton.connect("on_interstitial_clicked", self, "_on_interstitial_clicked") 121 | _admob_singleton.connect("on_interstitial_impression", self, "_on_interstitial_impression") 122 | _admob_singleton.connect("on_rewarded_video_ad_loaded", self, "_on_rewarded_video_ad_loaded") 123 | _admob_singleton.connect("on_rewarded_video_ad_opened", self, "_on_rewarded_video_ad_opened") 124 | _admob_singleton.connect("on_rewarded_video_ad_closed", self, "_on_rewarded_video_ad_closed") 125 | _admob_singleton.connect("on_rewarded_video_ad_failed_to_load", self, "_on_rewarded_video_ad_failed_to_load") 126 | _admob_singleton.connect("on_rewarded_interstitial_ad_loaded", self, "_on_rewarded_interstitial_ad_loaded") 127 | _admob_singleton.connect("on_rewarded_interstitial_ad_opened", self, "_on_rewarded_interstitial_ad_opened") 128 | _admob_singleton.connect("on_rewarded_interstitial_ad_closed", self, "_on_rewarded_interstitial_ad_closed") 129 | _admob_singleton.connect("on_rewarded_interstitial_ad_failed_to_load", self, "_on_rewarded_interstitial_ad_failed_to_load") 130 | _admob_singleton.connect("on_rewarded_interstitial_ad_failed_to_show", self, "_on_rewarded_interstitial_ad_failed_to_show") 131 | _admob_singleton.connect("on_rewarded", self, "_on_rewarded") 132 | _admob_singleton.connect("on_rewarded_clicked", self, "_on_rewarded_clicked") 133 | _admob_singleton.connect("on_rewarded_impression", self, "_on_rewarded_impression") 134 | 135 | _admob_singleton.connect("on_consent_info_update_success", self, "_on_consent_info_update_success") 136 | _admob_singleton.connect("on_consent_info_update_failure", self, "_on_consent_info_update_failure") 137 | _admob_singleton.connect("on_app_can_request_ads", self, "_on_app_can_request_ads") 138 | 139 | # load 140 | 141 | func load_banner() -> void: 142 | if _admob_singleton != null: 143 | _admob_singleton.loadBanner(banner_id, banner_on_top, banner_size) 144 | 145 | func load_interstitial() -> void: 146 | if _admob_singleton != null: 147 | _admob_singleton.loadInterstitial(interstitial_id) 148 | 149 | func is_interstitial_loaded() -> bool: 150 | if _admob_singleton != null: 151 | return _is_interstitial_loaded 152 | return false 153 | 154 | func load_rewarded_video() -> void: 155 | if _admob_singleton != null: 156 | _admob_singleton.loadRewardedVideo(rewarded_id) 157 | 158 | func is_rewarded_video_loaded() -> bool: 159 | if _admob_singleton != null: 160 | return _is_rewarded_video_loaded 161 | return false 162 | 163 | func load_rewarded_interstitial() -> void: 164 | if _admob_singleton != null: 165 | _admob_singleton.loadRewardedInterstitial(rewarded_interstitial_id) 166 | 167 | func is_rewarded_interstitial_loaded() -> bool: 168 | if _admob_singleton != null: 169 | return _is_rewarded_interstitial_loaded 170 | return false 171 | 172 | # show / hide 173 | 174 | func show_banner() -> void: 175 | if _admob_singleton != null: 176 | _admob_singleton.showBanner() 177 | 178 | func hide_banner() -> void: 179 | if _admob_singleton != null: 180 | _admob_singleton.hideBanner() 181 | 182 | func move_banner(on_top: bool) -> void: 183 | if _admob_singleton != null: 184 | banner_on_top = on_top 185 | _admob_singleton.move(banner_on_top) 186 | 187 | func show_interstitial() -> void: 188 | if _admob_singleton != null: 189 | _admob_singleton.showInterstitial() 190 | _is_interstitial_loaded = false 191 | 192 | func show_rewarded_video() -> void: 193 | if _admob_singleton != null: 194 | _admob_singleton.showRewardedVideo() 195 | _is_rewarded_video_loaded = false 196 | 197 | func show_rewarded_interstitial() -> void: 198 | if _admob_singleton != null: 199 | _admob_singleton.showRewardedInterstitial() 200 | _is_rewarded_interstitial_loaded = false 201 | 202 | # resize 203 | 204 | func banner_resize() -> void: 205 | if _admob_singleton != null: 206 | _admob_singleton.resize() 207 | 208 | # dimension 209 | func get_banner_dimension() -> Vector2: 210 | if _admob_singleton != null: 211 | return Vector2(_admob_singleton.getBannerWidth(), _admob_singleton.getBannerHeight()) 212 | return Vector2() 213 | 214 | func request_consent_info_update() -> void: 215 | if _admob_singleton != null: 216 | _admob_singleton.requestConsentInfoUpdate(testing_consent) 217 | 218 | func reset_consent() -> void: 219 | if _admob_singleton != null: 220 | _admob_singleton.resetConsentInformation() 221 | 222 | # callbacks 223 | 224 | func _on_admob_ad_loaded() -> void: 225 | emit_signal("banner_loaded") 226 | 227 | func _on_admob_banner_failed_to_load(error_code:int) -> void: 228 | emit_signal("banner_failed_to_load", error_code) 229 | 230 | func _on_interstitial_failed_to_load(error_code:int) -> void: 231 | _is_interstitial_loaded = false 232 | emit_signal("interstitial_failed_to_load", error_code) 233 | 234 | func _on_interstitial_opened() -> void: 235 | emit_signal("interstitial_opened") 236 | 237 | func _on_interstitial_loaded() -> void: 238 | _is_interstitial_loaded = true 239 | emit_signal("interstitial_loaded") 240 | 241 | func _on_interstitial_close() -> void: 242 | emit_signal("interstitial_closed") 243 | 244 | func _on_interstitial_clicked() -> void: 245 | emit_signal("interstitial_clicked") 246 | 247 | func _on_interstitial_impression() -> void: 248 | emit_signal("interstitial_impression") 249 | 250 | func _on_rewarded_video_ad_loaded() -> void: 251 | _is_rewarded_video_loaded = true 252 | emit_signal("rewarded_video_loaded") 253 | 254 | func _on_rewarded_video_ad_opened() -> void: 255 | emit_signal("rewarded_video_opened") 256 | 257 | func _on_rewarded_video_ad_closed() -> void: 258 | emit_signal("rewarded_video_closed") 259 | 260 | func _on_rewarded_video_ad_failed_to_load(error_code:int) -> void: 261 | _is_rewarded_video_loaded = false 262 | emit_signal("rewarded_video_failed_to_load", error_code) 263 | 264 | func _on_rewarded_interstitial_ad_opened() -> void: 265 | emit_signal("rewarded_interstitial_opened") 266 | 267 | func _on_rewarded_interstitial_ad_loaded() -> void: 268 | _is_rewarded_interstitial_loaded = true 269 | emit_signal("rewarded_interstitial_loaded") 270 | 271 | func _on_rewarded_interstitial_ad_closed() -> void: 272 | emit_signal("rewarded_interstitial_closed") 273 | 274 | func _on_rewarded_interstitial_ad_failed_to_load(error_code:int) -> void: 275 | _is_rewarded_interstitial_loaded = false 276 | emit_signal("rewarded_interstitial_failed_to_load", error_code) 277 | 278 | func _on_rewarded_interstitial_ad_failed_to_show(error_code:int) -> void: 279 | _is_rewarded_interstitial_loaded = false 280 | emit_signal("rewarded_interstitial_failed_to_show", error_code) 281 | 282 | func _on_rewarded(currency:String, amount:int) -> void: 283 | emit_signal("rewarded", currency, amount) 284 | 285 | func _on_rewarded_clicked() -> void: 286 | emit_signal("rewarded_clicked") 287 | 288 | func _on_rewarded_impression() -> void: 289 | emit_signal("rewarded_impression") 290 | 291 | func _on_consent_info_update_success() -> void: 292 | emit_signal("consent_info_update_success") 293 | 294 | func _on_consent_info_update_failure(error_code:int, error_message:String) -> void: 295 | emit_signal("consent_info_update_failure", error_code, error_message) 296 | 297 | func _on_app_can_request_ads(consent_status:int) -> void: 298 | emit_signal("consent_app_can_request_ad", consent_status) 299 | -------------------------------------------------------------------------------- /demo/admob-lib/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/demo/admob-lib/icon.png -------------------------------------------------------------------------------- /demo/export_presets.cfg: -------------------------------------------------------------------------------- 1 | [preset.0] 2 | 3 | name="Android" 4 | platform="Android" 5 | runnable=true 6 | custom_features="" 7 | export_filter="all_resources" 8 | include_filter="" 9 | exclude_filter="" 10 | export_path="" 11 | script_export_mode=1 12 | script_encryption_key="" 13 | 14 | [preset.0.options] 15 | 16 | custom_template/debug="" 17 | custom_template/release="" 18 | custom_build/use_custom_build=true 19 | custom_build/export_format=0 20 | custom_build/min_sdk="" 21 | custom_build/target_sdk="" 22 | plugins/GodotAdMob=true 23 | architectures/armeabi-v7a=true 24 | architectures/arm64-v8a=true 25 | architectures/x86=true 26 | architectures/x86_64=true 27 | keystore/debug="" 28 | keystore/debug_user="" 29 | keystore/debug_password="" 30 | keystore/release="" 31 | keystore/release_user="" 32 | keystore/release_password="" 33 | one_click_deploy/clear_previous_install=true 34 | version/code=1 35 | version/name="1.0" 36 | package/unique_name="org.godotengine.$genname" 37 | package/name="" 38 | package/signed=true 39 | package/classify_as_game=true 40 | package/retain_data_on_uninstall=false 41 | package/exclude_from_recents=false 42 | launcher_icons/main_192x192="" 43 | launcher_icons/adaptive_foreground_432x432="" 44 | launcher_icons/adaptive_background_432x432="" 45 | graphics/opengl_debug=false 46 | xr_features/xr_mode=0 47 | xr_features/hand_tracking=0 48 | xr_features/hand_tracking_frequency=0 49 | xr_features/passthrough=0 50 | screen/immersive_mode=true 51 | screen/support_small=true 52 | screen/support_normal=true 53 | screen/support_large=true 54 | screen/support_xlarge=true 55 | user_data_backup/allow=false 56 | command_line/extra_args="" 57 | apk_expansion/enable=false 58 | apk_expansion/SALT="" 59 | apk_expansion/public_key="" 60 | permissions/custom_permissions=PoolStringArray( "com.google.android.gms.permission.AD_ID" ) 61 | permissions/access_checkin_properties=false 62 | permissions/access_coarse_location=false 63 | permissions/access_fine_location=false 64 | permissions/access_location_extra_commands=false 65 | permissions/access_mock_location=false 66 | permissions/access_network_state=true 67 | permissions/access_surface_flinger=false 68 | permissions/access_wifi_state=false 69 | permissions/account_manager=false 70 | permissions/add_voicemail=false 71 | permissions/authenticate_accounts=false 72 | permissions/battery_stats=false 73 | permissions/bind_accessibility_service=false 74 | permissions/bind_appwidget=false 75 | permissions/bind_device_admin=false 76 | permissions/bind_input_method=false 77 | permissions/bind_nfc_service=false 78 | permissions/bind_notification_listener_service=false 79 | permissions/bind_print_service=false 80 | permissions/bind_remoteviews=false 81 | permissions/bind_text_service=false 82 | permissions/bind_vpn_service=false 83 | permissions/bind_wallpaper=false 84 | permissions/bluetooth=false 85 | permissions/bluetooth_admin=false 86 | permissions/bluetooth_privileged=false 87 | permissions/brick=false 88 | permissions/broadcast_package_removed=false 89 | permissions/broadcast_sms=false 90 | permissions/broadcast_sticky=false 91 | permissions/broadcast_wap_push=false 92 | permissions/call_phone=false 93 | permissions/call_privileged=false 94 | permissions/camera=false 95 | permissions/capture_audio_output=false 96 | permissions/capture_secure_video_output=false 97 | permissions/capture_video_output=false 98 | permissions/change_component_enabled_state=false 99 | permissions/change_configuration=false 100 | permissions/change_network_state=false 101 | permissions/change_wifi_multicast_state=false 102 | permissions/change_wifi_state=false 103 | permissions/clear_app_cache=false 104 | permissions/clear_app_user_data=false 105 | permissions/control_location_updates=false 106 | permissions/delete_cache_files=false 107 | permissions/delete_packages=false 108 | permissions/device_power=false 109 | permissions/diagnostic=false 110 | permissions/disable_keyguard=false 111 | permissions/dump=false 112 | permissions/expand_status_bar=false 113 | permissions/factory_test=false 114 | permissions/flashlight=false 115 | permissions/force_back=false 116 | permissions/get_accounts=false 117 | permissions/get_package_size=false 118 | permissions/get_tasks=false 119 | permissions/get_top_activity_info=false 120 | permissions/global_search=false 121 | permissions/hardware_test=false 122 | permissions/inject_events=false 123 | permissions/install_location_provider=false 124 | permissions/install_packages=false 125 | permissions/install_shortcut=false 126 | permissions/internal_system_window=false 127 | permissions/internet=true 128 | permissions/kill_background_processes=false 129 | permissions/location_hardware=false 130 | permissions/manage_accounts=false 131 | permissions/manage_app_tokens=false 132 | permissions/manage_documents=false 133 | permissions/manage_external_storage=false 134 | permissions/master_clear=false 135 | permissions/media_content_control=false 136 | permissions/modify_audio_settings=false 137 | permissions/modify_phone_state=false 138 | permissions/mount_format_filesystems=false 139 | permissions/mount_unmount_filesystems=false 140 | permissions/nfc=false 141 | permissions/persistent_activity=false 142 | permissions/process_outgoing_calls=false 143 | permissions/read_calendar=false 144 | permissions/read_call_log=false 145 | permissions/read_contacts=false 146 | permissions/read_external_storage=false 147 | permissions/read_frame_buffer=false 148 | permissions/read_history_bookmarks=false 149 | permissions/read_input_state=false 150 | permissions/read_logs=false 151 | permissions/read_phone_state=false 152 | permissions/read_profile=false 153 | permissions/read_sms=false 154 | permissions/read_social_stream=false 155 | permissions/read_sync_settings=false 156 | permissions/read_sync_stats=false 157 | permissions/read_user_dictionary=false 158 | permissions/reboot=false 159 | permissions/receive_boot_completed=false 160 | permissions/receive_mms=false 161 | permissions/receive_sms=false 162 | permissions/receive_wap_push=false 163 | permissions/record_audio=false 164 | permissions/reorder_tasks=false 165 | permissions/restart_packages=false 166 | permissions/send_respond_via_message=false 167 | permissions/send_sms=false 168 | permissions/set_activity_watcher=false 169 | permissions/set_alarm=false 170 | permissions/set_always_finish=false 171 | permissions/set_animation_scale=false 172 | permissions/set_debug_app=false 173 | permissions/set_orientation=false 174 | permissions/set_pointer_speed=false 175 | permissions/set_preferred_applications=false 176 | permissions/set_process_limit=false 177 | permissions/set_time=false 178 | permissions/set_time_zone=false 179 | permissions/set_wallpaper=false 180 | permissions/set_wallpaper_hints=false 181 | permissions/signal_persistent_processes=false 182 | permissions/status_bar=false 183 | permissions/subscribed_feeds_read=false 184 | permissions/subscribed_feeds_write=false 185 | permissions/system_alert_window=false 186 | permissions/transmit_ir=false 187 | permissions/uninstall_shortcut=false 188 | permissions/update_device_stats=false 189 | permissions/use_credentials=false 190 | permissions/use_sip=false 191 | permissions/vibrate=false 192 | permissions/wake_lock=false 193 | permissions/write_apn_settings=false 194 | permissions/write_calendar=false 195 | permissions/write_call_log=false 196 | permissions/write_contacts=false 197 | permissions/write_external_storage=false 198 | permissions/write_gservices=false 199 | permissions/write_history_bookmarks=false 200 | permissions/write_profile=false 201 | permissions/write_secure_settings=false 202 | permissions/write_settings=false 203 | permissions/write_sms=false 204 | permissions/write_social_stream=false 205 | permissions/write_sync_settings=false 206 | permissions/write_user_dictionary=false 207 | -------------------------------------------------------------------------------- /demo/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/demo/icon.png -------------------------------------------------------------------------------- /demo/main.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | onready var admob = $AdMob 4 | onready var debug_out = $CanvasLayer/DebugOut 5 | 6 | func _ready(): 7 | 8 | if not admob.ads_using_consent: 9 | print('Ads without consent verification') 10 | loadAds() 11 | else: 12 | print('Ads with consent') 13 | # warning-ignore:return_value_discarded 14 | get_tree().connect("screen_resized", self, "_on_resize") 15 | 16 | func loadAds() -> void: 17 | admob.load_banner() 18 | admob.load_interstitial() 19 | admob.load_rewarded_video() 20 | admob.load_rewarded_interstitial() 21 | 22 | # buttons callbacks 23 | func _on_BtnReload_pressed() -> void: 24 | loadAds() 25 | 26 | func _on_BtnBanner_toggled(button_pressed): 27 | if button_pressed: admob.show_banner() 28 | else: admob.hide_banner() 29 | 30 | func _on_BtnBannerMove_toggled(button_pressed: bool) -> void: 31 | admob.move_banner(button_pressed) 32 | $"CanvasLayer/BtnBannerResize".disabled = true 33 | $"CanvasLayer/BtnBanner".disabled = true 34 | $"CanvasLayer/BtnBannerMove".disabled = true 35 | 36 | func _on_BtnBannerResize_pressed() -> void: 37 | admob.banner_resize() 38 | 39 | func _on_BtnInterstitial_pressed(): 40 | debug_out.text = debug_out.text + "Interstitial loaded before shown = " + str(admob.is_interstitial_loaded()) +"\n" 41 | admob.show_interstitial() 42 | debug_out.text = debug_out.text + "Interstitial loaded after shown = " + str(admob.is_interstitial_loaded()) +"\n" 43 | 44 | func _on_BtnRewardedVideo_pressed(): 45 | debug_out.text = debug_out.text + "Rewarded loaded before shown = " + str(admob.is_rewarded_video_loaded()) +"\n" 46 | admob.show_rewarded_video() 47 | debug_out.text = debug_out.text + "Rewarded loaded after shown = " + str(admob.is_rewarded_video_loaded()) +"\n" 48 | 49 | func _on_BtnRewardedInterstitial_pressed() -> void: 50 | debug_out.text = debug_out.text + "Rewarded interstitial loaded before shown = " + str(admob.is_rewarded_interstitial_loaded()) +"\n" 51 | admob.show_rewarded_interstitial() 52 | debug_out.text = debug_out.text + "Rewarded interstitial loaded after shown = " + str(admob.is_rewarded_interstitial_loaded()) +"\n" 53 | 54 | # AdMob callbacks 55 | func _on_resize(): 56 | debug_out.text = debug_out.text + "Banner resized\n" 57 | admob.banner_resize() 58 | 59 | func _on_AdMob_banner_failed_to_load(error_code): 60 | debug_out.text = debug_out.text + "Banner failed to load: Error code " + str(error_code) + "\n" 61 | 62 | func _on_AdMob_banner_loaded(): 63 | $"CanvasLayer/BtnBannerResize".disabled = false 64 | $"CanvasLayer/BtnBanner".disabled = false 65 | $"CanvasLayer/BtnBannerMove".disabled = false 66 | debug_out.text = debug_out.text + "Banner loaded\n" 67 | debug_out.text = debug_out.text + "Banner size = " + str(admob.get_banner_dimension()) + "\n" 68 | 69 | func _on_AdMob_interstitial_opened(): 70 | debug_out.text = debug_out.text + "Interstitial opened\n" 71 | 72 | func _on_AdMob_interstitial_loaded(): 73 | $"CanvasLayer/BtnInterstitial".disabled = false 74 | debug_out.text = debug_out.text + "Interstitial loaded\n" 75 | 76 | func _on_AdMob_interstitial_clicked(): 77 | debug_out.text = debug_out.text + "Interstitial clicked\n" 78 | 79 | func _on_AdMob_interstitial_closed(): 80 | debug_out.text = debug_out.text + "Interstitial closed\n" 81 | $"CanvasLayer/BtnInterstitial".disabled = false 82 | 83 | func _on_AdMob_interstitial_failed_to_load(error_code): 84 | debug_out.text = debug_out.text + "Interstitial failed to load: Error code " + str(error_code) + "\n" 85 | 86 | func _on_AdMob_interstitial_impression(): 87 | debug_out.text = debug_out.text + "Interstitial impression\n" 88 | 89 | func _on_AdMob_network_error(): 90 | debug_out.text = debug_out.text + "Network error\n" 91 | 92 | func _on_AdMob_rewarded(currency, amount): 93 | debug_out.text = debug_out.text + "Rewarded watched, currency: " + str(currency) + " amount:"+ str(amount)+ "\n" 94 | 95 | func _on_AdMob_rewarded_clicked(): 96 | debug_out.text = debug_out.text + "Rewarded clicked\n" 97 | 98 | func _on_AdMob_rewarded_impression(): 99 | debug_out.text = debug_out.text + "Rewarded impression\n" 100 | 101 | func _on_AdMob_rewarded_video_closed(): 102 | debug_out.text = debug_out.text + "Rewarded video closed\n" 103 | $"CanvasLayer/BtnRewardedVideo".disabled = true 104 | admob.load_rewarded_video() 105 | 106 | func _on_AdMob_rewarded_video_failed_to_load(error_code): 107 | debug_out.text = debug_out.text + "Rewarded video failed to load: Error code " + str(error_code) + "\n" 108 | 109 | func _on_AdMob_rewarded_video_loaded(): 110 | $"CanvasLayer/BtnRewardedVideo".disabled = false 111 | debug_out.text = debug_out.text + "Rewarded video loaded\n" 112 | 113 | func _on_AdMob_rewarded_video_opened(): 114 | debug_out.text = debug_out.text + "Rewarded video opened\n" 115 | 116 | func _on_AdMob_rewarded_interstitial_loaded() -> void: 117 | $CanvasLayer/BtnRewardedInterstitial.disabled = false 118 | debug_out.text = debug_out.text + "Rewarded interstitial loaded\n" 119 | 120 | func _on_AdMob_rewarded_interstitial_opened() -> void: 121 | debug_out.text = debug_out.text + "Rewarded interstitial opened\n" 122 | 123 | func _on_AdMob_rewarded_interstitial_closed() -> void: 124 | debug_out.text = debug_out.text + "Rewarded interstitial closed\n" 125 | $CanvasLayer/BtnRewardedInterstitial.disabled = true 126 | admob.load_rewarded_interstitial() 127 | 128 | func _on_AdMob_rewarded_interstitial_failed_to_load(error_code) -> void: 129 | debug_out.text = debug_out.text + "Rewarded interstitial failed to load: Error code " + str(error_code) + "\n" 130 | 131 | func _on_AdMob_rewarded_interstitial_failed_to_show(error_code) -> void: 132 | debug_out.text = debug_out.text + "Rewarded interstitial failed to show: Error code " + str(error_code) + "\n" 133 | 134 | func _on_BtnRequestConsentInfoUpdate_pressed(): 135 | debug_out.text = debug_out.text + "Request Consent\n" 136 | admob.request_consent_info_update() 137 | 138 | 139 | 140 | func _on_BtnResetConsent_pressed(): 141 | debug_out.text = debug_out.text + "RESET Consent\n" 142 | admob.reset_consent() 143 | 144 | 145 | func _on_AdMob_consent_app_can_request_ad(consent_status): 146 | debug_out.text = debug_out.text + 'App can start requesting ads.' 147 | print('App can start requesting ads.') 148 | loadAds() 149 | 150 | func _on_AdMob_consent_info_update_failure(error_code, error_message): 151 | debug_out.text = debug_out.text + 'Consent failure: ' + "(" + str(error_code) + "," + error_message + ")" 152 | 153 | 154 | func _on_AdMob_consent_info_update_success(): 155 | debug_out.text = debug_out.text + '_on_AdMob_consent_info_update_success' 156 | -------------------------------------------------------------------------------- /demo/main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://main.gd" type="Script" id=1] 4 | [ext_resource path="res://admob-lib/admob.gd" type="Script" id=2] 5 | 6 | [node name="Node2D" type="Node2D"] 7 | script = ExtResource( 1 ) 8 | __meta__ = { 9 | "__editor_plugin_screen__": "3D" 10 | } 11 | 12 | [node name="CanvasLayer" type="CanvasLayer" parent="."] 13 | 14 | [node name="DebugOut" type="RichTextLabel" parent="CanvasLayer"] 15 | anchor_left = 0.5 16 | anchor_top = 0.5 17 | anchor_right = 0.5 18 | anchor_bottom = 0.5 19 | margin_left = -75.0 20 | margin_top = 120.0 21 | margin_right = 305.0 22 | margin_bottom = 208.0 23 | text = " 24 | " 25 | scroll_following = true 26 | 27 | [node name="BtnReload" type="Button" parent="CanvasLayer"] 28 | anchor_left = 0.5 29 | anchor_top = 0.5 30 | anchor_right = 0.5 31 | anchor_bottom = 0.5 32 | margin_left = -291.0 33 | margin_top = -217.0 34 | margin_right = -213.0 35 | margin_bottom = -142.0 36 | text = "Reload" 37 | __meta__ = { 38 | "_edit_use_anchors_": false 39 | } 40 | 41 | [node name="BtnInterstitial" type="Button" parent="CanvasLayer"] 42 | anchor_left = 0.5 43 | anchor_top = 0.5 44 | anchor_right = 0.5 45 | anchor_bottom = 0.5 46 | margin_left = -75.0 47 | margin_top = -166.077 48 | margin_right = 75.0 49 | margin_bottom = -91.0766 50 | disabled = true 51 | text = "Show Interstitial" 52 | 53 | [node name="BtnBannerResize" type="Button" parent="CanvasLayer"] 54 | anchor_left = 0.5 55 | anchor_top = 0.5 56 | anchor_right = 0.5 57 | anchor_bottom = 0.5 58 | margin_left = -251.0 59 | margin_top = -65.6294 60 | margin_right = -101.0 61 | margin_bottom = 9.37061 62 | disabled = true 63 | text = "Resize Banner" 64 | 65 | [node name="BtnRequestConsentInfoUpdate" type="Button" parent="CanvasLayer"] 66 | anchor_left = 0.5 67 | anchor_top = 0.5 68 | anchor_right = 0.5 69 | anchor_bottom = 0.5 70 | margin_left = -251.0 71 | margin_top = 31.0 72 | margin_right = -101.0 73 | margin_bottom = 106.0 74 | text = "Request Consent" 75 | 76 | [node name="BtnResetConsent" type="Button" parent="CanvasLayer"] 77 | anchor_left = 0.5 78 | anchor_top = 0.5 79 | anchor_right = 0.5 80 | anchor_bottom = 0.5 81 | margin_left = -251.0 82 | margin_top = 120.0 83 | margin_right = -101.0 84 | margin_bottom = 195.0 85 | text = "Reset Consent" 86 | 87 | [node name="BtnBanner" type="Button" parent="CanvasLayer"] 88 | anchor_left = 0.5 89 | anchor_top = 0.5 90 | anchor_right = 0.5 91 | anchor_bottom = 0.5 92 | margin_left = -75.0 93 | margin_top = -65.6294 94 | margin_right = 75.0 95 | margin_bottom = 9.37061 96 | disabled = true 97 | toggle_mode = true 98 | text = "Show/Hide Banner" 99 | 100 | [node name="BtnBannerMove" type="Button" parent="CanvasLayer"] 101 | anchor_left = 0.5 102 | anchor_top = 0.5 103 | anchor_right = 0.5 104 | anchor_bottom = 0.5 105 | margin_left = 103.0 106 | margin_top = -65.6294 107 | margin_right = 253.0 108 | margin_bottom = 9.37061 109 | disabled = true 110 | toggle_mode = true 111 | text = "Move Banner" 112 | 113 | [node name="BtnRewardedVideo" type="Button" parent="CanvasLayer"] 114 | anchor_left = 0.5 115 | anchor_top = 0.5 116 | anchor_right = 0.5 117 | anchor_bottom = 0.5 118 | margin_left = -75.0 119 | margin_top = 31.0654 120 | margin_right = 76.0 121 | margin_bottom = 106.065 122 | disabled = true 123 | text = "Show Rewarded Video" 124 | 125 | [node name="BtnRewardedInterstitial" type="Button" parent="CanvasLayer"] 126 | anchor_left = 0.5 127 | anchor_top = 0.5 128 | anchor_right = 0.5 129 | anchor_bottom = 0.5 130 | margin_left = 103.0 131 | margin_top = 31.0 132 | margin_right = 254.0 133 | margin_bottom = 106.0 134 | disabled = true 135 | text = "Show Rewarded Interstitial" 136 | 137 | [node name="AdMob" type="Node" parent="."] 138 | script = ExtResource( 2 ) 139 | __meta__ = { 140 | "_editor_description_": "" 141 | } 142 | banner_id = "ca-app-pub-3940256099942544/6300978111" 143 | interstitial_id = "ca-app-pub-3940256099942544/1033173712" 144 | rewarded_id = "ca-app-pub-3940256099942544/5224354917" 145 | rewarded_interstitial_id = "ca-app-pub-3940256099942544/5354046379" 146 | is_personalized = false 147 | 148 | [connection signal="pressed" from="CanvasLayer/BtnReload" to="." method="_on_BtnReload_pressed"] 149 | [connection signal="pressed" from="CanvasLayer/BtnInterstitial" to="." method="_on_BtnInterstitial_pressed"] 150 | [connection signal="pressed" from="CanvasLayer/BtnBannerResize" to="." method="_on_BtnBannerResize_pressed"] 151 | [connection signal="pressed" from="CanvasLayer/BtnRequestConsentInfoUpdate" to="." method="_on_BtnRequestConsentInfoUpdate_pressed"] 152 | [connection signal="pressed" from="CanvasLayer/BtnResetConsent" to="." method="_on_BtnResetConsent_pressed"] 153 | [connection signal="toggled" from="CanvasLayer/BtnBanner" to="." method="_on_BtnBanner_toggled"] 154 | [connection signal="toggled" from="CanvasLayer/BtnBannerMove" to="." method="_on_BtnBannerMove_toggled"] 155 | [connection signal="pressed" from="CanvasLayer/BtnRewardedVideo" to="." method="_on_BtnRewardedVideo_pressed"] 156 | [connection signal="pressed" from="CanvasLayer/BtnRewardedInterstitial" to="." method="_on_BtnRewardedInterstitial_pressed"] 157 | [connection signal="banner_failed_to_load" from="AdMob" to="." method="_on_AdMob_banner_failed_to_load"] 158 | [connection signal="banner_loaded" from="AdMob" to="." method="_on_AdMob_banner_loaded"] 159 | [connection signal="consent_app_can_request_ad" from="AdMob" to="." method="_on_AdMob_consent_app_can_request_ad"] 160 | [connection signal="consent_info_update_failure" from="AdMob" to="." method="_on_AdMob_consent_info_update_failure"] 161 | [connection signal="consent_info_update_success" from="AdMob" to="." method="_on_AdMob_consent_info_update_success"] 162 | [connection signal="interstitial_clicked" from="AdMob" to="." method="_on_AdMob_interstitial_clicked"] 163 | [connection signal="interstitial_closed" from="AdMob" to="." method="_on_AdMob_interstitial_closed"] 164 | [connection signal="interstitial_failed_to_load" from="AdMob" to="." method="_on_AdMob_interstitial_failed_to_load"] 165 | [connection signal="interstitial_impression" from="AdMob" to="." method="_on_AdMob_interstitial_impression"] 166 | [connection signal="interstitial_loaded" from="AdMob" to="." method="_on_AdMob_interstitial_loaded"] 167 | [connection signal="interstitial_opened" from="AdMob" to="." method="_on_AdMob_interstitial_opened"] 168 | [connection signal="rewarded" from="AdMob" to="." method="_on_AdMob_rewarded"] 169 | [connection signal="rewarded_clicked" from="AdMob" to="." method="_on_AdMob_rewarded_clicked"] 170 | [connection signal="rewarded_impression" from="AdMob" to="." method="_on_AdMob_rewarded_impression"] 171 | [connection signal="rewarded_interstitial_closed" from="AdMob" to="." method="_on_AdMob_rewarded_interstitial_closed"] 172 | [connection signal="rewarded_interstitial_failed_to_load" from="AdMob" to="." method="_on_AdMob_rewarded_interstitial_failed_to_load"] 173 | [connection signal="rewarded_interstitial_failed_to_show" from="AdMob" to="." method="_on_AdMob_rewarded_interstitial_failed_to_show"] 174 | [connection signal="rewarded_interstitial_loaded" from="AdMob" to="." method="_on_AdMob_rewarded_interstitial_loaded"] 175 | [connection signal="rewarded_interstitial_opened" from="AdMob" to="." method="_on_AdMob_rewarded_interstitial_opened"] 176 | [connection signal="rewarded_video_closed" from="AdMob" to="." method="_on_AdMob_rewarded_video_closed"] 177 | [connection signal="rewarded_video_failed_to_load" from="AdMob" to="." method="_on_AdMob_rewarded_video_failed_to_load"] 178 | [connection signal="rewarded_video_loaded" from="AdMob" to="." method="_on_AdMob_rewarded_video_loaded"] 179 | [connection signal="rewarded_video_opened" from="AdMob" to="." method="_on_AdMob_rewarded_video_opened"] 180 | -------------------------------------------------------------------------------- /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=4 10 | 11 | _global_script_classes=[ { 12 | "base": "Node", 13 | "class": "AdMob", 14 | "language": "GDScript", 15 | "path": "res://admob-lib/admob.gd" 16 | } ] 17 | _global_script_class_icons={ 18 | "AdMob": "res://admob-lib/icon.png" 19 | } 20 | 21 | [application] 22 | 23 | config/name="AdMob Plugin Test" 24 | run/main_scene="res://main.tscn" 25 | config/icon="res://icon.png" 26 | 27 | [display] 28 | 29 | window/size/width=640 30 | window/size/height=480 31 | window/handheld/orientation="sensor" 32 | window/stretch/mode="2d" 33 | window/stretch/aspect="keep_height" 34 | 35 | [editor_plugins] 36 | 37 | enabled=PoolStringArray( ) 38 | 39 | [rendering] 40 | 41 | quality/driver/driver_name="GLES2" 42 | vram_compression/import_etc=true 43 | -------------------------------------------------------------------------------- /images/properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/images/properties.png -------------------------------------------------------------------------------- /images/search_node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/images/search_node.png -------------------------------------------------------------------------------- /images/signals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shin-NiL/Godot-Android-Admob-Plugin/459293ea8be1c64a88b4307a82c5514401327408/images/signals.png -------------------------------------------------------------------------------- /issue_template.md: -------------------------------------------------------------------------------- 1 | **Godot version:** 2 | 3 | **AdMob Plugin version:** 4 | 5 | **Issue description:** 6 | 7 | 8 | -------------------------------------------------------------------------------- /showcase.md: -------------------------------------------------------------------------------- 1 | # Showcase 2 | I've created this file because I love to see what people create using this plugin ;) 3 | 4 | If you like to add your game in this list open an issue or send me a pull request with the link and title of your published game. 5 | 6 | ## List of Published Games using this Plugin 7 | 8 | - [12345 Rotors - Puzzle Game](https://play.google.com/store/apps/details?id=com.ross.numbergame12345) 9 | - [Ball Square](https://play.google.com/store/apps/details?id=com.mani.ballandsquare&hl=en_US) 10 | - [Ball Toss](https://play.google.com/store/apps/details?id=com.EGFABT.Ball_Toss) 11 | - [Block Runner](https://play.google.com/store/apps/details?id=org.godotengine.blockrunner) 12 | - [Bomber Pilot](https://play.google.com/store/apps/details?id=org.ohyaiamheregames.bomberpilot) 13 | - [Bomber Pilot 2](https://play.google.com/store/apps/details?id=org.ohyaiamheregames.bomberpilottwo) 14 | - [Break Bacteria](https://play.google.com/store/apps/details?id=com.turkicgames.breakbacteria) 15 | - [Cars On Tracks](https://play.google.com/store/apps/details?id=org.Introsflexion.CarsOnTracks) 16 | - [DeathGuy](https://play.google.com/store/apps/details?id=org.amdotblacksheep.deathguy) 17 | - [Dirtfall - Vacuum Cleaning Simulator](https://play.google.com/store/apps/details?id=com.goldspark.cleaner.dirtfall) 18 | - [Dogecoin: The Video Game](https://play.google.com/store/apps/details?id=org.dtgames.dodecoingame) 19 | - [Domino Trail](https://play.google.com/store/apps/details?id=org.losyk.dominotrail) 20 | - [Flappy Unicorn](https://play.google.com/store/apps/details?id=com.dtgames.flappyunicorn) 21 | - [Future Funk Soccer Racing](https://play.google.com/store/apps/details?id=org.godotengine.cargame) 22 | - [Galaxy Fights - Space Shooter](https://play.google.com/store/apps/details?id=com.landapp.games.shooter) 23 | - [Help Luigi!](https://play.google.com/store/apps/details?id=com.bananaonfire.helpluigi) 24 | - [HoverFlow - Endless Driver](https://play.google.com/store/apps/details?id=godot.pizzajuggler.hoverflow) 25 | - [iDrink – Drinking Game 🍻](https://play.google.com/store/apps/details?id=com.garfsapps.umgole) 26 | - [Inline](https://play.google.com/store/apps/details?id=com.loustak.inline) 27 | - [Ionitron - ion magnet puzzle game](https://play.google.com/store/apps/details?id=godot.pizzajuggler.ionitron) 28 | - [Landbox](https://play.google.com/store/apps/details?id=org.hideckies.landbox) 29 | - [Life Is Not The End](https://play.google.com/store/apps/details?id=org.godotengine.lifeisnottheend) 30 | - [Logikal](https://play.google.com/store/apps/details?id=org.khgames.logikal) 31 | - [Mad Crab Grab](https://play.google.com/store/apps/details?id=org.godotengine.madcrabgrab) 32 | - [No Rush - Puzzle/Platformer Game](https://play.google.com/store/apps/details?id=org.turkmenh.norush) 33 | - [Office Kiss Game](https://play.google.com/store/apps/details?id=org.godotengine.secretkisses) 34 | - [Piranha Escape](https://play.google.com/store/apps/details?id=com.bitmagine.piranhaescape) 35 | - [Plane in a Cavern](https://play.google.com/store/apps/details?id=org.kanataexe.tappytheplane) 36 | - [Push Puzzle](https://play.google.com/store/apps/details?id=push.puzzle.sokoban) 37 | - [Reverse Babylon](https://play.google.com/store/apps/details?id=com.PlastilinLepim.babylon) 38 | - [Rush: Endless Arcade](https://play.google.com/store/apps/details?id=org.haruntrkmn.rush) 39 | - [SLID - Letter Sliding Puzzle](https://play.google.com/store/apps/details?id=com.kamofa.slid) 40 | - [Space Flex](https://play.google.com/store/apps/details?id=com.gachitech.space_flex) 41 | - [SpeedSnake](https://play.google.com/store/apps/details?id=com.mosscrow.speedsnake) 42 | - [Spin Mania](https://play.google.com/store/apps/details?id=com.kalzcoat.spinmania) 43 | - [TriHard](https://play.google.com/store/apps/details?id=org.dtgames.trihard) 44 | - [Turzzle](https://play.google.com/store/apps/details?id=com.bananaonfire.turzzle) 45 | - [Walls of Stone - Puzzle Game](https://play.google.com/store/apps/details?id=dev.mintstudios.wos) 46 | - [Warzone 2 - Top down shooter](https://play.google.com/store/apps/details?id=com.raptor.inc) 47 | - [WoW Death Roll Simulator](https://play.google.com/store/apps/details?id=ch.jackfruit.deathrollsimulator) 48 | - [Zeptris](https://play.google.com/store/apps/details?id=net.wynoo.zeptris) 49 | - [Zig-Zag](https://play.google.com/store/apps/details?id=org.kanataexe.zigzag) 50 | - [Zone Crush - A Match 3 Game 2020](https://play.google.com/store/apps/details?id=com.GamesForLife.ZoneCrush) 51 | -------------------------------------------------------------------------------- /tools/package_build.sh: -------------------------------------------------------------------------------- 1 | #!bin/bash 2 | 3 | RELEASES_DIR="releases" 4 | VERSION_DIR="$RELEASES_DIR/$1" 5 | PLUGIN_DIR="$RELEASES_DIR/$1/admob-plugin" 6 | 7 | if [[ ! -e $RELEASES_DIR ]]; then 8 | mkdir -p $RELEASES_DIR 9 | fi 10 | 11 | mkdir -p $PLUGIN_DIR 12 | cp -r ../admob-lib $VERSION_DIR 13 | cp ../config/GodotAdmob.gdap ../admob-plugin/godotadmob/build/outputs/aar/GodotAdMob.${1}.release.aar $PLUGIN_DIR 14 | 15 | cd $VERSION_DIR 16 | zip -r "../GodotAdmobPlugin-${1}.zip" . 17 | --------------------------------------------------------------------------------