In previous guide I described minimal changes required to make Admob work with kivy built for API 27. Now I will try to expand the functionality one step further by adding support for new type of ads: Rewarded Videos. This type is bit more complex than Interstitials as we need to be able to handle rewards for the user who watched the ad to the end, and, ideally, support many different types of rewards rather than a single one. Additionally, as part of this mini project I decided to go for the latest libraries and dependencies possible in a hope they will have fewer bugs.
To get started, clone python-for-android-kivmob27 repo. I will try to cherry pick changes from main p4a repo from time to time to keep it up to date.
Next, copy kivmob27.py file into your App directory (same location as your main.py).
buildozer.spec:
android.api = 27 android.minapi = 19 requirements = kivy, hostpython2, android, jnius android.permissions = INTERNET, ACCESS_NETWORK_STATE android.p4a_dir = # dir/to/python-for-android-kivmob27/ android.gradle_dependencies = 'com.google.android.gms:play-services-ads:16.0.0','com.android.support:appcompat-v7:26.1.0'
And, finally, this is how you could show the ad and reward the user:
from kivmob27 import KivMob from kivy.app import App from kivy.uix.button import Button from kivy.uix.boxlayout import BoxLayout from kivy.properties import ObjectProperty class KivMobTest(App): ads = ObjectProperty(allownone=True) def build(self): self.ads = KivMob("APP_ID") self.ads.add_test_device("TEST_DEVICE_ID") self.ads.new_interstitial("INTERSTITIAL_ID") self.ads.request_interstitial() self.ads.new_rewarded("REWARDED_ID") self.ads.request_rewarded() bl = BoxLayout(orientation='vertical') b1 = Button(text='Show Interstitial', on_release= lambda a:ads.show_interstitial()) b2 = Button(text='Show Rewarded', on_release= lambda a:ads.show_rewarded(reward_type='your reward type')) bl.add_widget(b1) bl.add_widget(b2) return bl def on_resume(self): res = self.ads.get_reward_type() if res != "No reward": #Give reward based on reward_type self.reward(res) def reward(self, reward_type) #reward the player self.ads.playerRewarded() #reset the reward so it is not triggered again KivMobTest().run()
Please check out my app Quadropoly to see it in action. ⭐⭐⭐⭐⭐ rating would be appreciated 🙂
Ideally, I would love to submit my changes as a pull request to original MichaelStott/KivMob repo after polishing it up.
Any questions, suggestions or issues, please let me know in the comments below. Pull requests to the repos are welcome too.
Let me know if it works for you!
Hey mind_writer, I’m getting started with these instructions. Is is true that in the buildozer.spec there is no requirement for kivmob?
Update: I tried many different ways to integrate your example but the ads don’t seem to come up. I usually build API 19, then re-build API 27 after deleting the dist folder. When my app built, I changed the android.p4a_dir location to your file and android.gradle_dependencies to the one above. All this with the kivmob27.py file in the directory the whole time. Is this correct, and which steps do you take to implement this method above?
Hi Petar,
After changing p4a_dir you need to “buildozer android clean” before rebuilding, otherwise buildozer would just keep using already copied p4a files from .buildozer folder.
There is no requirement for kivmob as everything you need is in kivmob27.py file. If you add kivmob requirement, it would overwrite or conflict with my kivmob27 changes
mind_writer, thank you for the explanation! I get an error whenever I try to ‘buildozer android clean’. I opened up a ticket on GitHub because I couldn’t find a solution: https://github.com/kivy/buildozer/issues/732. There is my terminal printed error if you think you can assist with that issue. But what is a work around if I can’t get ‘buildozer android clean’ to work? I probably have to delete specific folders then.
Hey Petar,
FileNotFoundError: [Errno 2] No such file or directory: '/home/pl/Desktop/X/.buildozer/android/platform/python-for-android-new-toolchain'
This error is easy to fix – just create folder ‘python-for-android-new-toolchain’ at this path. No need to put any files inside. After this
buildozer android clean
should work without issues.I want to thank you for your great work!
However if someone is experiencing trouble in using it I managed to compile an app through those simple steps:
1. Be sure to run buildozer android clean in your project folder
2. Compile for api 19 using python-for-android-kivmob27 repo, but don’t add gradle deps. It will fail but we don’t need to have a complete build at this point.
3. Delete dist folder in buildozer, change api to 27 and add gradle deps.
4. Build
Thank you for your feedback and the steps!
I have just pushed a new commit to both python-for-android-kivmob27 and kivmob27.py, it fixes a couple of bugs from original kivmob implementation. Old kivmob would crash after a certain number of ad requests, which for me was less than 60 minutes of gameplay in some cases.
hi mind_writer, i have been trying to build my app with your instruction for a week. i couldn’t even success to built my app with api 27 without your changes on ubuntu 16.04 (64bit). then i decided to use this PR https://github.com/kivy/python-for-android/pull/1381. now i can build my app with api 28 but when i apply your changes in buildozer i got error bellow. could you make it compatibe with that PR or tell me what shoul i do to build my app with kivmob?
[DEBUG]:
[DEBUG]: BUILD FAILED
[DEBUG]:
[DEBUG]: Total time: 3.919 secs
Exception in thread background thread for pid 28907:
Traceback (most recent call last):
File “/usr/lib/python2.7/threading.py”, line 801, in __bootstrap_inner
self.run()
File “/usr/lib/python2.7/threading.py”, line 754, in run
self.__target(*self.__args, **self.__kwargs)
File “/usr/local/lib/python2.7/dist-packages/sh.py”, line 1540, in wrap
fn(*args, **kwargs)
File “/usr/local/lib/python2.7/dist-packages/sh.py”, line 2459, in background_thread
handle_exit_code(exit_code)
File “/usr/local/lib/python2.7/dist-packages/sh.py”, line 2157, in fn
return self.command.handle_command_exit_code(exit_code)
File “/usr/local/lib/python2.7/dist-packages/sh.py”, line 815, in handle_command_exit_code
raise exc
ErrorReturnCode_1:
RAN: /root/test/.buildozer/android/platform/build/dists/sets/gradlew assembleDebug
STDOUT:
Incremental java compilation is an incubating feature. :preBuild UP-TO-DATE :preDebugBuild UP-TO-DATE
:checkDebugManifest
:preReleaseBuild UP-TO-DATE
:prepareAndroidArchLifecycleRuntime100Library
:prepareComAndroidSupportAnimatedVectorDrawable2610Library
:prepareComAndroidSupportAppcompatV72610Library
:prepareComAndroidSupportCustomtabs2610Library
:prepareComAndroidSupportSupportCompat2610Library
:prepareComAndroidSupportSupportCoreUi2610Library
:prepareComAndroidSupportSupportCoreUtils2610Library
:prepareComAndroidSupportSupportFragment2610Library
:prepareComAndroidSupportSupportMediaCompat2610Library
:prepareComAndroidSupportSupportV42610Library
:prepareComAndroidSupportSupportVectorDrawable2610Library
:prepareComGoogleAndroidGmsPlayServicesAds1600Library
:prepareComGoogleAndroidGmsPlayServicesAdsBase1600Library
:prepareComGoogleAndroidGmsPlayServicesAdsIdentifier1600Library
:prepareComGoogleAndroidGmsPlayServicesAdsLite1600Library
:prepareComGoogleAndroidGmsPlayServicesBasement1601Library
:prepareComGoogleAndroidGmsPlayServicesGass1600Library
:prepareDebugDependencies
:compileDebugAidl UP-TO-DATE
:compileDebugRenderscript UP-TO-DATE :generateDebugBuildConfig UP-TO-DATE
:generateDebugResValues UP-TO-DATE
:generateDebugResources UP-TO-DATE
:mergeDebugResources :processDebugManifest UP-TO-DATE :processDebugResources :generateDebugSources :incrementalDebugJavaCompilationSafeguard UP-TO-DATE
:javaPreCompileDebug
:compileDebugJavaWithJavac
:compileDebugJavaWithJavac – is not incremental (e.g. outputs have changed, no previous execution, etc.).
/root/test/.buildozer/android/platform/build/dists/sets/src/main/java/org/kivy/android/PythonActivity.java:63: error: package com.crashlytics.android does not exist
import com.crashlytics.android.Crashlytics;
^
/root/test/.buildozer/android/platform/build/dists/sets/src/main/java/org/kivy/android/PythonActivity.java:64: error: package io.fabric.sdk.android does not exist
import io.fabric.sdk.android.Fabric;
^
/root/test/.buildozer/android/platform/build/dists/sets/src/main/java/org/kivy/android/PythonActivity.java:321: error: cannot find symbol
Fabric.with(this, new Crashlytics());
^
symbol: class Crashlytics
location: class PythonActivity
/root/test/.buildozer/android/platform/build/dists/sets/src/main/java/org/kivy/android/PythonActivity.java:321: error: cannot find symbol
Fabric.with(this, new Crashlytics());
^
symbol: variable Fabric
location: class PythonActivity
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
4 errors
:compileDebugJavaWithJavac FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ‘:compileDebugJavaWithJavac’.
> Compilation failed; see the compiler error output for details.
* Try:
Run with –stacktrace option to get the stack trace. Run with –info or –debug option to get more log output.
BUILD FAILED
Total time: 3.919 secs
STDERR:
[DEBUG]: [INFO]: STDOUT (last 20 lines of 70):
symbol: variable Fabric
location: class PythonActivity
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
4 errors
:compileDebugJavaWithJavac FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ‘:compileDebugJavaWithJavac’.
> Compilation failed; see the compiler error output for details.
* Try:
Run with –stacktrace option to get the stack trace. Run with –info or –debug option to get more log output.
BUILD FAILED
Total time: 3.919 secs
[INFO]: STDERR:
[INFO]: COMMAND:
cd /root/test/.buildozer/android/platform/build/dists/sets && /root/test/.buildozer/android/platform/build/dists/sets/gradlew assembleDebug
[WARNING]: ERROR: /root/test/.buildozer/android/platform/build/dists/sets/gradlew failed!
# Command failed: /usr/bin/python -m pythonforandroid.toolchain apk –debug –bootstrap=sdl2 –dist_name sets –name ‘MIUI Hidden Settings’ –version 1.4 –package com.ceyhan.sets –android_api 28 –minsdk 21 –private /root/test/.buildozer/android/app –permission INTERNET –permission ACCESS_NETWORK_STATE –presplash /root/test/./splash.png –icon /root/test/./icon.png –orientation portrait –window –presplash-color ‘#334C80’ –copy-libs –depend ‘com.google.android.gms:play-services-ads:16.0.0’ –depend ‘com.android.support:appcompat-v7:26.1.0’ –arch armeabi-v7a –color=always –storage-dir=”/root/test/.buildozer/android/platform/build”
#
# Buildozer failed to execute the last command
# The error might be hidden in the log above this error
# Please read the full log, and search for it before
# raising an issue with buildozer itself.
# In case of a bug report, please add a full log with log_level = 2
root@ubuntu-s-1vcpu-2gb-fra1-01:~/test#
@Ethnic, I just tried building my app with mind_writer’s instructions and ran into the same error you were having. The error led me to believe that something (crashlytics) wasn’t getting built into the apk.
Adding this: ‘com.crashlytics.sdk.android:crashlytics:2.9.8’ to the buildozer.spec gradle dependencies solved the issue for me, hopefully it’ll work for you as well! You might need to clear your dists folder before doing it, but I’m not sure if that’s necessary.
Thank you so much. i removed the “import com.crashlytics.android.Crashlytics;” line from PythonActivity.java to get rid of this error but your solution is more logical than mine’s. 🙂
Hey @Ethnic and @tkuam, sorry I meant to remove Crashlitics part from the code but forgot to do it. You could either add the gradle dependency or remove it from PythonActivity (the import statement and in onCreate method, line 321). This SDK is quite handy though, it gives insights into crashes happening outside of python code. I use Sentry for python related crashes, but it does not work if the crash happens outside of main kivy loop (ie. ads, native onPause, onResume methods, OutOfMemories etc). If you decide to keep it, make sure to
1) Create account on https://fabric.io/
2) Add gradle dependency:
compile('com.crashlytics.sdk.android:crashlytics:2.9.6@aar') {
transitive = true;
}
3) after
apply plugin: 'com.android.application'
in your build.tmpl.gradle add this:apply plugin: 'io.fabric'
repositories {
maven { url 'https://maven.fabric.io/public' }
}
4) In AndroidManifest.tmpl.xml file add this after closing tag for your PythonActivity:
meta-data
android:name="io.fabric.ApiKey"
android:value="[your own crashlitics key here]"
Thanks for the info mind_writer, I’ll look into the Crashlytics stuff and figure out what I want to do. If you don’t mind me asking, what all was needed to get Sentry built into your app? Same thing as Crashlytics essentially? And do you use the free version, and if so is it sufficient enough for your needs? Haven’t had many crashes yet, but I’d like to get something to help with my app (and any future apps) to help me resolve any future issues quickly. Thanks again for your help!
Hi tkuam, sorry for delayed response. Sentry requires you to basically wrap your entire main.py inside try/except block to capture all errors and redirect it to their service before crashing your app. I am using raven which has become recently deprecated, so probably I need to invest some time into replacing it with newer SDK.
https://docs.sentry.io/clients/python/
This is a good minimum example of how I am using it (this post is not mine though):
https://www.reddit.com/r/kivy/comments/42fvxy/problem_with_kivy_and_sentryraven/
Hey mind_writer,
Sorry for taking so long to try out your rewarded ads tutorial. I tried it a couple weeks ago briefly and ran into a bit of trouble so set it aside for a while as I’ve been doing other things with my app and learning the ins and outs of this whole app development/testing/marketing process 🙂 But anyways, I finally got back to it and ran into the same error that @Ethnic had. Looked to me like crashlytics wasn’t getting loaded into the app so a bit of googling led me to try adding ‘com.crashlytics.sdk.android:crashlytics:2.9.8’ to my buildozer.spec’s gradle dependencies and after that the apk built successfully, I tested a reward ad and it worked! Thanks again for all your work on this! Been playing Quadropoly quite a bit recently too, thanks for that as well 😉
Are banner and interstitial ads working with this method? have you tested it?
Haven’t tested it extensively, but yes, on my own device banner and interstitial ads are working as well.
Thanks..
mind writer, eventually i managed to make ads working on my app again. Thank you so much for your effort.
You are welcome! All the best with your app 🙂
hey @mind_writer, i have a last question, is there a way to change banner ads position from top to bottom? i want banner ad to be shown at the bottom of screen. is that possible?
now it’s possible with KivMob 2.0, thanks anyway.
Hi Ethnic, sorry for the late reply. Yes, KivMob 2.0 does look very promising, and the best part there is no java code to modify, and as a result, we can use master branch of p4a. First version of kivmob had huge issue with local ref table overflow (hardcoded value of 512 on Android, cannot be extended) due to every single ad request going through AdHandler which stored reference to PythonActivity. I fixed it with WeakRefs. I am not sure yet if kivmob 2.0 is affected by this issue. Testing it is quite hard, as you would need to run your app with test ads for a very long time until you make over 500 ad requests.
hi mind_writer, lets say kivmob 2 still has the same issue, then how to fix it?