Python for android project include a python module named “android”. This module is designed to give you an access to the Java Android API. As for today, the module is very limited, and waiting for contribution to wrap more Java Android API.
Using PyJNIus to access the Android API restricts the usage to a simple call of the autoclass constructor function and a second call to instantiate this class.
You can access through this method all Java Android API, e.g. to get the DisplayMetrics of an Android device could fetched using the following piece of code:
DisplayMetrics = autoclass('android.util.DisplayMetrics')
metrics = DisplayMetrics()
metrics.setToDefaults()
self.densityDpi = metrics.densityDpi
You can access all fields and methods as described in the Java Android DisplayMetrics API as shown here with the method setToDefaults() and the field densityDpi. Before you use o view a field, you should always call setToDefaults to initiate to the default values of the device.
Currently only JavaMethod, JavaStaticMethod, JavaField, JavaStaticField and JavaMultipleMethod are built into PyJNIus, therefore such constructs like registerListener or something like this have to be coded still in Java. For this the Android module described below is available to access some of the hardware in Android devices.
More background how to use the Java Android API without PyJNIus is also given below in the chapter `how-it-s-working-without-pyjnius`_
A good starting point to build an APK are prebuilt VirtualBox images, where the Android NDK, the Android SDK and the Kivy Python-For-Android sources are prebuilt in an VirtualBox image. Please search the Download Section for such an image.
The following example is an extract from the Compass app as provided in the Kivy examples/android/compass folder:
from jnius import autoclass
...
class CompassApp(App):
def __init__(self, **kwargs):
"""
Constructor of the Compass App
1) The Java Android API DisplayMetrics is called to get
information about the densityDpi factor of the Android device
2) The Kivy Python-For-Android Android API is called to
get access to the hardware sensors of the Android device
"""
super(CompassApp, self).__init__(**kwargs)
DisplayMetrics = autoclass('android.util.DisplayMetrics')
metrics = DisplayMetrics()
metrics.setToDefaults()
LoggerDisplayMetrics(metrics)
self.densityDpi = metrics.densityDpi
Hardware = autoclass('org.renpy.android.Hardware')
self.hw = Hardware()
Logger.info('COMPASS: Hardware Objects: %s'%(str(dir(self.hw))))
Logger.info('COMPASS: Hardware Sensors\n%s\n'%(self.hw.getHardwareSensors()))
def viewCompass(self, *largs):
"""
viewCompass calls the readSensor method of the
magneticFieldSensor instance of the generic3AxisSensor, it reads the
3-tuple value of the magnetic field
the declination angle is computed as the angle of the magnetic field
vector in the x,y-plane and the unity-vector of the y-axis.
afterwards the rotateNeedle function rotates the needle as given
by the declination angle parameter
"""
(x, y, z) = self.hw.magneticFieldSensor.readSensor()
declination = Vector(x,y).angle((0,1))
Logger.info('COMPASS: viewCompass x=%s y=%s z=%s declination=%s'%(x,y,z,declination))
self.needle.rotateNeedle(declination)
def stopApp(self,*largs):
"""
this function is called when pushed the stopButton, disables
the magneticFieldSensor and stops the app
"""
self.hw.magneticFieldSensor.changeStatus(False)
Logger.info('COMPASS: stop largs '+str(largs))
self.stop()
def build(self):
"""
Building all together:
1) Creating the parent widget and clearing it to white background color
2) Defining a suitable position and size of the CompassWidget, the
needleSize and the stopButtonHeight depending on the densityDpi value
given by DisplayMetrics
3) Creating an instance of the CompassWidget and adding it to the
parent widget and calling the appropriate build function
4) Creating an instance of the NeedleWidget and adding it also to the
parent widget and calling the appropriate build function
5) Creating an instance of a Button widget and adding it as stopButton
also to the parent widget and bind it with the stopApp function
6) Calling the instance method changeStatus of the magneticFieldSensor
instance with parameter True to enable the magnetic field sensor
and additionally calling the function schedule_interval of the Clock
class for a repeated call of the function viewCompass every second.
"""
parent = FloatLayout(size=(500,500))
Window.clearcolor = (1, 1, 1, 1)
if self.densityDpi == 240:
CompassPos = Vector(50., 200.)
CompassSize = Vector(400., 400.)
needleSize = Vector(100., 60.)
stopButtonHeight = 60
elif self.densityDpi == 320:
CompassPos = Vector(75., 300.)
CompassSize = Vector(600., 600.)
needleSize = Vector(150., 90.)
stopButtonHeight = 90
else:
Logger.info('COMPASS: widget size should be adopted - minimum used for densityDpi=%s'%(str(self.densityDpi)))
CompassPos = Vector(50., 200.)
CompassSize = Vector(400., 400.)
needleSize = Vector(100., 60.)
stopButtonHeight = 60
self.Compass = CompassWidget()
parent.add_widget(self.Compass)
self.Compass.build(pos=CompassPos,size=CompassSize)
self.needle = NeedleWidget()
parent.add_widget(self.needle)
self.needle.build(center=CompassPos+CompassSize/2.,needleSize=needleSize)
self.stopButton = Button(text='Stop', pos_hint={'right':1}, size_hint=(None,None), height=stopButtonHeight)
parent.add_widget(self.stopButton)
self.stopButton.bind(on_press=self.stopApp)
self.hw.magneticFieldSensor.changeStatus(True)
Clock.schedule_interval(self.viewCompass, 1.)
return parent
If you compile this app, you will get an APK which outputs the following screen:
Screenshot of the Kivy Compass App (Source of the Compass Windrose: Wikipedia)
This module is built for accessing hardware devices of an Android device
Causes the phone to vibrate for s seconds. This requires that your application have the VIBRATE permission.
Returns a string of all hardware sensors of an Android device where each line lists the informations about one sensor in the following format:
Name=name,Vendor=vendor,Version=version,MaximumRange=maximumRange,MinDelay=minDelay,Power=power,Type=type
For more information about this informations look into the original Java API for the Sensors Class
This variable links to a generic3AxisSensor instance and their functions to access the accelerometer sensor
This variable links to a generic3AxisSensor instance and their functions to access the orientation sensor
The following two instance methods of the generic3AxisSensor class should be used to enable/disable the sensor and to read the sensor
Changes the status of the sensor, the status of the sensor is enabled, if enable is true or disabled, if enable is false.
Returns an (x, y, z) tuple of floats that gives the sensor reading, the units depend on the sensor as shown on the Java API page for SensorEvent. The sesnor must be enabled before this function is called. If the tuple contains three zero values, the accelerometer is not enabled, not available, defective, has not returned a reading, or the device is in free-fall.
Returns the screen density in dots per inch.
Shows the soft keyboard.
Hides the soft keyboard.
Enables wifi scanning.
Note
ACCESS_WIFI_STATE and CHANGE_WIFI_STATE permissions are required.
Returns a String for each visible WiFi access point
(SSID, BSSID, SignalLevel)
This module is built to deliver data to someone else.
Deliver data to someone else. This method is a wrapper around ACTION_SEND
| Parameters : |
|
|---|
Sending a simple hello world text:
android.action_send('text/plain', text='Hello world',
subject='Test from python')
Sharing an image file:
# let's say you've make an image in /sdcard/image.png
android.action_send('image/png', filename='/sdcard/image.png')
Sharing an image with a default text too:
android.action_send('image/png', filename='/sdcard/image.png',
text='Hi,\n\tThis is my awesome image, what do you think about it ?')
Some further modules are currently available but not yet documented. Please have a look into the code and you are very welcome to contribute to this documentation.
Service part of the application is controlled through the class AndroidService.
Run service/main.py from application directory as a service.
| Parameters : |
|
|---|
Start the service.
Parameters:
- arg: str, default to ‘’
Argument to pass to a service, through environment variable PYTHON_SERVICE_ARGUMENT.
Stop the service.
Application activity part example, main.py:
from android import AndroidService
...
class ServiceExample(App):
...
def start_service(self):
self.service = AndroidService('Sevice example', 'service is running')
self.service.start('Hello From Service')
def stop_service(self):
self.service.stop()
Application service part example, service/main.py:
import os
import time
# get the argument passed
arg = os.getenv('PYTHON_SERVICE_ARGUMENT')
while True:
# this will print 'Hello From Service' continually, even when application is switched
print arg
time.sleep(1)
The whole Android API is accessible in Java. Their is no native or extensible way to access it from Python. The schema for accessing to their API is:
[1] Cython -> [2] C JNI -> [3] Java
All the source code is available at:
import android
# activate the vibrator
android.vibrate(1)
# read screen dpi
print android.get_dpi()
Some part of the Android API is not accessible from PyJNIus. For example, if you want to receive some broadcast, you need to implement a BroadcastReceiver. PyJNIus allows you to implement dynamically classes from Java interfaces, but unfortunately, the BroadcastReceiver is an abstract class.
So we started to create bridges for this case.
Implementation of the android BroadcastReceiver. You can specify the callback that will receive the broadcast event, and actions or categories filters.
Warning
The callback will be called in another thread than the main thread. Be careful to not access to OpenGL or something like that.
| Parameters: |
|
|---|
For actions and categories, the string must be in lower case, without the prefix:
# In java: Intent.ACTION_HEADSET_PLUG
# In python: 'headset_plug'
Register the receiver with all the actions and categories, and start handling events.
Unregister the receiver with all the actions and categories, and stop handling events.
Example:
class TestApp(App):
def build(self):
self.br = BroadcastReceiver(
self.on_broadcast, actions=['headset_plug'])
self.br.start()
# ...
def on_broadcast(self, context, intent):
extras = intent.getExtras()
headset_state = bool(extras.get('state'))
if headset_state:
print 'The headset is plugged'
else:
print 'The headset is unplugged'
# don't forget to stop and restart the receiver when the app is going
# to pause / resume mode
def on_pause(self):
self.br.stop()
return True
def on_resume(self):
self.br.start()
Note
The following is from an older version and the documentation for this part is currently not updated. Nevertheless it is included here for history and further development aspects.
This should be called on a regular basis to check to see if Android expects the game to pause. If it return true, the game should call android.wait_for_resume(), after persisting its state as necessary.
This function should be called after android.check_pause() returns true. It does not return until Android has resumed from the pause. While in this function, Android may kill a game without further notice.
This maps between an android keycode and a python keysym. The android keycodes are available as constants in the android module.
The android_mixer module contains a subset of the functionality in found in the pygame.mixer module. It’s intended to be imported as an alternative to pygame.mixer, using code like:
try:
import pygame.mixer as mixer
except ImportError:
import android_mixer as mixer
Note that if you’re using kivy.core.audio module, you don’t have to do anything, all is automatic.
The android_mixer module is a wrapper around the Android MediaPlayer class. This allows it to take advantage of any hardware acceleration present, and also eliminates the need to ship codecs as part of an application.
It has several differences from the pygame mixer:
Note
The android_mixer module hasn’t been tested much, and so bugs may be present.