Posts tagged python
Technology at mSale - Tips & Tricks part 2 - Kivy Runtime Permissions on Android

Target reader: Intermediate Kivy and Python users

After hanging out in the Kivy discord server for a while (I recommend everyone interested in Kivy join up here!) I noticed there was a distinct problem showing up multiple times, that did not have a clear cut solution for people that were experienced with the framework - but not necessarily with the inner workings of the Android operating system.
In older API versions, you simply had to request whatever permissions you needed up front in the manifest (or buildozer.spec in our case) - and that would be good enough. Nowadays, however - we are required to request them during runtime - and the user can actively select at their own leasure if they approve of them or not. It is also bad form to have our app crash or close if the permission is not granted, so as developers we should handle this in a better way.

The required tools for this can be achieved through the use of Pyjnius - a kivy tool to interface through the JNI - granting us access to everything we would normally have access through natively. Pyjnius is incredibly powerful, and I will come back to it in a later post detailing how to use it with third party libraries - but for now, we will only focus on mostly accessing already existing android api’s.

The completed example app can be found here

First off, we need to declare the permissions we want to use in buildozer.spec as normal - for our example we will be using ACCESS_FINE_LOCATION. The reason for this being that android classifies certain permissions as Dangerous Permissions - and only those are the ones that need to be granted at runtime by the user. Other permissions, such as VIBRATE, are granted upon install as many old timers are used to.

Second, we need to grab a certain external support library - granting us access to ContextCompat - which we will use to check the status of permissions. Here, it is important to grab a package that corresponds to your targeted API - the default currently being 27. You can grab the .aar file from Maven, a sort of repository for artifacts in the Java world (consider it a sort of pypi).
We then declare the file in our buildozer.spec, under the android.add_aars property.

Finally - we need the code to tie this all together. Ultimately this is a pretty simple snippet - but there is a lot going on here that one might not normally get exposed through while only doing Kivy apps. Normally, one would not dive too deep into the various native API’s and such - and only the basic non-dangerous permissions are used.

The completed snippet is as follows:

from jnius import autoclass
from kivy.logger import Logger

PythonActivity = autoclass("org.kivy.android.PythonActivity").mActivity
Context = autoclass('android.content.Context')
ContextCompat = autoclass('android.support.v4.content.ContextCompat')

def check_permission(permission, activity=PythonActivity):
    permission_status = ContextCompat.checkSelfPermission(activity,
                                                          permission)

    Logger.info(permission_status)
    permission_granted = 0 == permission_status
    Logger.info("Permission Status: {}".format(permission_granted))
    return permission_granted

def ask_permission(permission, activity=PythonActivity):
    PythonActivity.requestPermissions([permission])

Simply put - this grants us two methods we can use throughout our Kivy apps to confirm the existence of permissions - and also grant them.

The simple order here would be to check if we have the permission, in this case “android.permission.ACCESS_FINE_LOCATION”, confirm wether or not we have it, if the check returns False - we request it, and recheck aftewards if the user granted us the permission we needed. Repeat as needed throughout your app until you get what you require.

I hope this simple blog post will help some people out there in the Kivy world with something that might be a small headache for people getting into mobile development - and an inspiration to continue testing out various other native API’s in collaboration with Kivy.

I have been Kjetil A. Liknes
Thank you for reading.

IMG_4762.jpg
Technology at mSale - Tips & Tricks part 1 - Native extensions for iOS using Kivy

Target reader: Intermediate Kivy and Python user, people curious about the Kivy framework.

Here at mSale, we use a multitude of different technologies, depending on the needs of our projects. Most of our tech stack is heavily based on Python, however, and therefore it was only natural that we would gravitate towards the Kivy framework. 

Kivy is, as per Kivy.org is a "Open source Python library for rapid development of applications
that make use of innovative user interfaces, such as multi-touch apps."
We will not go too deep in the merits of Kivy in this article, but it has served us extremely well and is in use for both our maintenance tooling aimed at mobile phones, and the gift card machines themselves.

The problem

Kivy, by nature of being a not-first-class framework for the platforms it is used on (such as using Swift/Objective-c on iOS and Android/Kotlin on Android), does not have a significant or large extension ecosystem. Given that it also has a relatively low userbase, compared to more popular frameworks and implementations, like React Native, Ionic and the native implementations, there aren't enough people there for the third parties creating these extra services to make their own implementation aimed specifically towards Kivy.

This means that we do not have easy access to things like Fabric crash logs, OneSignal push notifications and so forth. I have often seen people comment that this is often the dealbreaker when it comes to using the framework, considering the multitude of content and extensions for the other frameworks.
But not any more!

The solution

From this point on, I am going to demonstrate how you can use just about any (disclaimer: I have not tried every single third party library out there) extension out there, just like you would in a normal iOS project.

Part 1:

Create the base of your kivy app!
For reference, I'll include a full main.py from one of the apps we are currently developing: 
gist
The actual look and structure of your app does not particularly matter, but make notice of the naming conventions for the initialisation of the libraries in the finish_ios_init method. The actual code is merely a pyobjus  translation of the official fabric and onesignal installation instructions - and those instructions should be followed for all non-kivy things.
After your skeleton is up, you should do your initial buildozer build: buildozer ios debug.

Part 2:

After your initial build is done, the iOS project itself resides in .buildozer/ios/platform/kivy-ios/<project-name>-ios/ and it is within that folder we will do our cocoapods setup. For this project, the Podfile should look something like this. After the podfile is set up, you can run pod install like normal in this folder and work with the <projectname>.xcworkspace like any other project, and finish the initialisation from there.

Once these relatively simple steps are done, the app should work with the extentions when run from the xcode workspace, like a normal iOS project utilizing cocoapods. 
An important takeaway is how methods with parameters are called with pyobjus.
Take the Fabric initialisation, for example: 

self.Crashlytics = autoclass("Crashlytics")
self.Fabric = autoclass("Fabric").with_([self.Crashlytics])

For pyobjus, it would appear that anything that has a unnamed parameter ends the method call with an underscore. Similarly, for named parameters, we see it follows a similar pattern:
methodCall_namedParameter_(unnamed first parameter, named parameter)
This can be seen with the onesignal initialisation:

self.onesignal_object = autoclass("OneSignal")
mock_launch_options = objc_dict({})
self.onesignal_object.initWithLaunchOptions_appId_(mock_launch_options, "onesignal-appid-goes-here")

This would be the equivalent of the official way, taken from the install instructions:

   // Replace 'YOUR_APP_ID' with your OneSignal App ID.
   OneSignal.initWithLaunchOptions(launchOptions,
       appId: "YOUR_APP_ID",
       handleNotificationAction: nil,
       settings: onesignalInitSettings)

In conclusion

I hope this little blog entry has been a useful tip on unlocking more power from the Kivy framework! It has been but a minor peek into the potential possibilities you can unlock, combining Kivy with the power of native third party libraries. 

This is but a small part of the technology we use at mSale, and we are always interested in talking to other people and sharing our knowledge, so leave a comment! Have you used Kivy before? Does it sound like something you would consider for your next project?

I have been Kjetil A. Liknes, thank you for reading.

Live example of the OneSignal notifications coming in

Live example of the OneSignal notifications coming in