Included local plyer

This commit is contained in:
Mark Qvist 2022-09-16 18:07:57 +02:00
parent 25f0d52260
commit f23855fb68
166 changed files with 15862 additions and 0 deletions

124
sbapp/plyer/__init__.py Normal file
View File

@ -0,0 +1,124 @@
'''
Plyer
=====
'''
__all__ = (
'accelerometer', 'audio', 'barometer', 'battery', 'bluetooth',
'brightness', 'call', 'camera', 'compass', 'cpu', 'email', 'filechooser',
'flash', 'gps', 'gravity', 'gyroscope', 'humidity', 'irblaster',
'keystore', 'light', 'notification', 'orientation', 'processors',
'proximity', 'screenshot', 'sms', 'spatialorientation', 'storagepath',
'stt', 'temperature', 'tts', 'uniqueid', 'vibrator', 'wifi', 'devicename'
)
__version__ = '2.1.0.dev0'
from plyer import facades
from plyer.utils import Proxy
#: Accelerometer proxy to :class:`plyer.facades.Accelerometer`
accelerometer = Proxy('accelerometer', facades.Accelerometer)
#: Keyring proxy to :class::`plyer.facades.Keystore`
keystore = Proxy('keystore', facades.Keystore)
#: Audio proxy to :class:`plyer.facades.Audio`
audio = Proxy('audio', facades.Audio)
#: Barometer proxy to :class:`plyer.facades.Barometer`
barometer = Proxy('barometer', facades.Barometer)
#: Battery proxy to :class:`plyer.facades.Battery`
battery = Proxy('battery', facades.Battery)
#: Call proxy to :class `plyer.facades.Call`
call = Proxy('call', facades.Call)
#: Compass proxy to :class:`plyer.facades.Compass`
compass = Proxy('compass', facades.Compass)
#: Camera proxy to :class:`plyer.facades.Camera`
camera = Proxy('camera', facades.Camera)
#: Email proxy to :class:`plyer.facades.Email`
email = Proxy('email', facades.Email)
#: FileChooser proxy to :class:`plyer.facades.FileChooser`
filechooser = Proxy('filechooser', facades.FileChooser)
#: GPS proxy to :class:`plyer.facades.GPS`
gps = Proxy('gps', facades.GPS)
#: Gravity proxy to :class:`plyer.facades.Gravity`
gravity = Proxy('gravity', facades.Gravity)
#: Gyroscope proxy to :class:`plyer.facades.Gyroscope`
gyroscope = Proxy('gyroscope', facades.Gyroscope)
#: IrBlaster proxy to :class:`plyer.facades.IrBlaster`
irblaster = Proxy('irblaster', facades.IrBlaster)
#: Light proxy to :class:`plyer.facades.Light`
light = Proxy('light', facades.Light)
#: Orientation proxy to :class:`plyer.facades.Orientation`
orientation = Proxy('orientation', facades.Orientation)
#: Notification proxy to :class:`plyer.facades.Notification`
notification = Proxy('notification', facades.Notification)
#: Proximity proxy to :class:`plyer.facades.Proximity`
proximity = Proxy('proximity', facades.Proximity)
#: Sms proxy to :class:`plyer.facades.Sms`
sms = Proxy('sms', facades.Sms)
#: Speech proxy to :class:`plyer.facades.STT`
stt = Proxy('stt', facades.STT)
#: TTS proxy to :class:`plyer.facades.TTS`
tts = Proxy('tts', facades.TTS)
#: UniqueID proxy to :class:`plyer.facades.UniqueID`
uniqueid = Proxy('uniqueid', facades.UniqueID)
#: Vibrator proxy to :class:`plyer.facades.Vibrator`
vibrator = Proxy('vibrator', facades.Vibrator)
#: Flash proxy to :class:`plyer.facades.Flash`
flash = Proxy('flash', facades.Flash)
#: Wifi proxy to :class:`plyer.facades.Wifi`
wifi = Proxy('wifi', facades.Wifi)
#: Temperature proxy to :class:`plyer.facades.Temperature`
temperature = Proxy('temperature', facades.Temperature)
#: Humidity proxy to :class:`plyer.facades.Humidity`
humidity = Proxy('humidity', facades.Humidity)
#: SpatialOrientation proxy to :class:`plyer.facades.SpatialOrientation`
spatialorientation = Proxy('spatialorientation', facades.SpatialOrientation)
#: Brightness proxy to :class:`plyer.facades.Brightness`
brightness = Proxy('brightness', facades.Brightness)
#: StoragePath proxy to :class:`plyer.facades.StoragePath`
storagepath = Proxy('storagepath', facades.StoragePath)
#: Bluetooth proxy to :class:`plyer.facades.Bluetooth`
bluetooth = Proxy('bluetooth', facades.Bluetooth)
#: Processors proxy to :class:`plyer.facades.Processors`
processors = Proxy('processors', facades.Processors)
#: Processors proxy to :class:`plyer.facades.CPU`
cpu = Proxy('cpu', facades.CPU)
#: Screenshot proxy to :class:`plyer.facades.Screenshot`
screenshot = Proxy('screenshot', facades.Screenshot)
#: devicename proxy to :class:`plyer.facades.DeviceName`
devicename = Proxy('devicename', facades.DeviceName)

View File

@ -0,0 +1,51 @@
'''
Facades
=======
Interface of all the features available.
'''
__all__ = ('Accelerometer', 'Audio', 'Barometer', 'Battery', 'Call', 'Camera',
'Compass', 'Email', 'FileChooser', 'GPS', 'Gravity', 'Gyroscope',
'IrBlaster', 'Light', 'Orientation', 'Notification', 'Proximity',
'Sms', 'TTS', 'UniqueID', 'Vibrator', 'Wifi', 'Flash', 'CPU',
'Temperature', 'Humidity', 'SpatialOrientation', 'Brightness',
'Processors', 'StoragePath', 'Keystore', 'Bluetooth', 'Screenshot',
'STT', 'DeviceName')
from plyer.facades.accelerometer import Accelerometer
from plyer.facades.audio import Audio
from plyer.facades.barometer import Barometer
from plyer.facades.battery import Battery
from plyer.facades.call import Call
from plyer.facades.camera import Camera
from plyer.facades.compass import Compass
from plyer.facades.email import Email
from plyer.facades.filechooser import FileChooser
from plyer.facades.flash import Flash
from plyer.facades.gps import GPS
from plyer.facades.gravity import Gravity
from plyer.facades.gyroscope import Gyroscope
from plyer.facades.irblaster import IrBlaster
from plyer.facades.light import Light
from plyer.facades.proximity import Proximity
from plyer.facades.orientation import Orientation
from plyer.facades.notification import Notification
from plyer.facades.sms import Sms
from plyer.facades.stt import STT
from plyer.facades.tts import TTS
from plyer.facades.uniqueid import UniqueID
from plyer.facades.vibrator import Vibrator
from plyer.facades.wifi import Wifi
from plyer.facades.temperature import Temperature
from plyer.facades.humidity import Humidity
from plyer.facades.spatialorientation import SpatialOrientation
from plyer.facades.brightness import Brightness
from plyer.facades.keystore import Keystore
from plyer.facades.storagepath import StoragePath
from plyer.facades.bluetooth import Bluetooth
from plyer.facades.processors import Processors
from plyer.facades.cpu import CPU
from plyer.facades.screenshot import Screenshot
from plyer.facades.devicename import DeviceName

View File

@ -0,0 +1,75 @@
'''
Accelerometer
============
The accelerometer is a motion sensor that detects the change (delta) in
movement relative to the current device orientation, in three dimensions
along the x, y, and z axis.
The :class:`Accelerometer` provides access to public methods to
use accelerometer of your device.
Simple Examples
---------------
To enable accelerometer::
>>> from plyer import accelerometer
>>> accelerometer.enable()
To disable accelerometer::
>>> accelerometer.disable()
To get the acceleration::
>>> accelerometer.acceleration
(-10.048464775085449, 6.825869083404541, 7.7260890007019043)
Supported Plaforms
------------------
Android, iOS, OS X, Linux
'''
class Accelerometer:
'''
Accelerometer facade.
'''
@property
def acceleration(self):
'''
Property that returns values of the current acceleration
sensors, as a (x, y, z) tuple. Returns (None, None, None)
if no data is currently available.
'''
return self.get_acceleration()
def enable(self):
'''
Activate the accelerometer sensor. Throws an error if the
hardware is not available or not implemented on.
'''
self._enable()
def disable(self):
'''
Disable the accelerometer sensor.
'''
self._disable()
def get_acceleration(self):
return self._get_acceleration()
# private
def _enable(self):
raise NotImplementedError()
def _disable(self):
raise NotImplementedError()
def _get_acceleration(self):
raise NotImplementedError()

View File

@ -0,0 +1,103 @@
'''
Audio
=====
The :class:`Audio` is used for recording audio.
Default path for recording is set in platform implementation.
.. note::
On Android the `RECORD_AUDIO`, `WAKE_LOCK` permissions are needed.
Simple Examples
---------------
To get the file path::
>>> audio.file_path
'/sdcard/testrecorder.3gp'
To set the file path::
>>> import os
>>> current_list = os.listdir('.')
['/sdcard/testrecorder.3gp', '/sdcard/testrecorder1.3gp',
'/sdcard/testrecorder2.3gp', '/sdcard/testrecorder3.3gp']
>>> file_path = current_list[2]
>>> audio.file_path = file_path
To start recording::
>>> from plyer import audio
>>> audio.start()
To stop recording::
>>> audio.stop()
To play recording::
>>> audio.play()
Supported Platforms
-------------------
Android, Windows, macOS
'''
class Audio:
'''
Audio facade.
'''
state = 'ready'
_file_path = ''
def __init__(self, file_path=None):
super().__init__()
self._file_path = file_path or self._file_path
def start(self):
'''
Start record.
'''
self._start()
self.state = 'recording'
def stop(self):
'''
Stop record.
'''
self._stop()
self.state = 'ready'
def play(self):
'''
Play current recording.
'''
self._play()
self.state = 'playing'
@property
def file_path(self):
return self._file_path
@file_path.setter
def file_path(self, location):
'''
Location of the recording.
'''
assert isinstance(location, str), 'Location must be string or unicode'
self._file_path = location
# private
def _start(self):
raise NotImplementedError()
def _stop(self):
raise NotImplementedError()
def _play(self):
raise NotImplementedError()

View File

@ -0,0 +1,36 @@
class Barometer:
'''Barometer facade.
Barometer sensor is used to measure the ambient air pressure in hPa.
With method `enable` you can turn on pressure sensor and 'disable'
method stops the sensor.
Use property `pressure` to get current air pressure in hPa.
.. versionadded:: 1.2.5
Supported Platforms:: Android, iOS
'''
@property
def pressure(self):
'''Current air pressure in hPa.'''
return self._get_pressure()
def _get_pressure(self, **kwargs):
raise NotImplementedError()
def _enable(self, **kwargs):
raise NotImplementedError()
def enable(self):
'''Enable barometer sensor.'''
self._enable()
def _disable(self, **kwargs):
raise NotImplementedError()
def disable(self):
'''Disable barometer sensor.'''
self._disable()

View File

@ -0,0 +1,54 @@
'''
Battery
=======
The :class:`Battery` provides information about the battery of your device.
.. note::
On Android the `BATTERY_STATS` permission is needed.
Simple Example
---------------
To get battery status::
>>> from plyer import battery
>>> battery.status
{'percentage': 82.0, 'isCharging': False}
Supported Platforms
-------------------
Android, iOS, Windows, OS X, Linux
'''
class Battery:
'''
Battery info facade.
'''
@property
def status(self):
'''
Property that contains a dict with the following fields:
* **isCharging** *(bool)*: Battery is charging
* **percentage** *(float)*: Battery charge remaining
.. warning::
If any of the fields is not readable, it is set as
None.
'''
return self.get_state()
def get_state(self):
'''
Public method for filling battery.status via platform-specific
API in plyer.platforms.
'''
return self._get_state()
# private
def _get_state(self):
raise NotImplementedError()

View File

@ -0,0 +1,43 @@
'''Bluetooth facade.
Returns the following:
* Bluetooth info
Simple Example
--------------
To get the bluetooth status info::
todo: will be extended to get additional bluetooth info
todo: will be extended to allow bluetooth connections etc.
>>> from plyer import bluetooth
>>> bluetooth
'on' or 'off'
Supported Platforms
-------------------
Android, OS X
'''
class Bluetooth:
'''
Bluetooth facade.
'''
@property
def info(self):
'''
Property that returns the info (currently status) of the bluetooth.
'''
return self.get_info()
def get_info(self):
return self._get_info()
# private
def _get_info(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,58 @@
'''
Brightness
==========
This API helps you to control the brightness of your primary display screen.
The :class:`Brightness` provides access to public methods to control the
brightness of screen.
NOTE:: For Android, make sure to add permission, WRITE_SETTINGS
Simple Examples
---------------
To know the current brightness level of device::
>>> from plyer import brightness
>>> brightness.current_level()
To set the brightness level to half of maximum::
>>> from plyer import brightness
>>> brightness.set_level(50)
Supported Platforms
-------------------
Android, iOS, Linux
'''
class Brightness:
'''
Brightness facade.
'''
def current_level(self):
'''
Know the current level of device's brightness.
'''
return self._current_level()
def set_level(self, level):
'''
Adjust the brightness of the screen.
Minimum brightness level:: 1
Maximum brightness level:: 100
:param level: New level of brightness between 1 and 100
:type level: int
'''
return self._set_level(level)
# private
def _set_level(self, level):
raise NotImplementedError()
def _current_level(self):
raise NotImplementedError()

View File

@ -0,0 +1,59 @@
'''
Call
====
The :class:`Call` provides access to calling feature of your device.
.. note::
- On Android your app needs the `CALL_PHONE` or `CALL_PRIVILEGED`
permission in order to make calls.
- Dialing call feature in not supported yet in iOS devices.
Simple Examples
---------------
To make call::
>>> from plyer import call
>>> tel = 9999222299
>>> call.makecall(tel=tel)
To dial call::
>>> call.dialcall()
Supported Platforms
-------------------
Android, iOS
'''
class Call:
'''
Call facade.
'''
def makecall(self, tel):
'''
Make calls using your device.
:param tel: The reciever
:type tel: number
'''
self._makecall(tel=tel)
def dialcall(self):
'''
Opens dialing interface.
'''
self._dialcall()
# private
def _makecall(self, **kwargs):
raise NotImplementedError()
def _dialcall(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,88 @@
'''
Camera
======
The :class:`Camera` is to capture pictures and make videos.
.. note::
- On Android the `CAMERA` , `WRITE_EXTERNAL_STORAGE`,
`READ_EXTERNAL_STORAGE` permissions are needed.
Simple Examples
---------------
Setup callback function.
>>> from os.path import exists, join
>>> from plyer import camera
>>> def camera_callback(filepath):
>>> if(exists(filepath)):
>>> print "saved"
>>> else:
>>> print "unable to save."
>>> filepath = 'path/to/your/file'
>>> # e.g: filepath = join(App.get_running_app().user_data_dir, file_name)
To take picture::
>>> file_name = "test.jpg"
>>> camera.take_picture(filename=file_name,
>>> on_complete=camera_callback)
Ta take a video::
>>> file_name = "test.mp4"
>>> camera.take_video(filename=file_name,
>>> on_complete=camera_callback)
Supported Platforms
-------------------
Android, iOS
'''
class Camera:
'''
Camera facade.
'''
def take_picture(self, filename, on_complete):
'''Ask the OS to capture a picture, and store it at filename.
When the capture is done, on_complete will be called with the filename
as an argument. If the callback returns True, the filename will be
unlinked.
:param filename: Name of the image file
:param on_complete: Callback that will be called when the operation is
done
:type filename: str
:type on_complete: callable
'''
self._take_picture(filename=filename, on_complete=on_complete)
def take_video(self, filename, on_complete):
'''Ask the OS to capture a video, and store it at filename.
When the capture is done, on_complete will be called with the filename
as an argument. If the callback returns True, the filename will be
unlinked.
:param filename: Name of the video file
:param on_complete: Callback that will be called when the operation is
done
:type filename: str
:type on_complete: callable
'''
self._take_video(filename=filename, on_complete=on_complete)
# private
def _take_picture(self, **kwargs):
raise NotImplementedError()
def _take_video(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,113 @@
'''
Compass
=======
The :class:`Compass` provides access to public methods to use compass of your
device.
Simple Examples
---------------
To enable compass::
>>> from plyer import compass
>>> compass.enable()
To disable compass::
>>> compass.disable()
To get the field::
>>> compass.field()
(-23.721826553344727, -5.7114701271057129, -36.749668121337891)
To get the uncalibrated field along with iron bias estimation::
>>> compass.field_uncalib()
(a,b,c,x,y,z)
# a,b,c denote the Geomagnetic field strength
# (without hard iron calibration) along the three axes.
# x,y,z denote the Iron bias estimation along the three axes.
Supported Platforms
-------------------
Android, iOS
'''
class Compass:
'''Compass facade.
.. versionadded:: 1.2.0
'''
@property
def orientation(self):
'''
WARNING:: This property is deprecated after API level 8.
Use `compass.field` instead.
Property that returns values of the current compass
(magnetic field) sensors, as a (x, y, z) tuple.
Returns (None, None, None) if no data is currently available.
'''
return self.get_orientation()
@property
def field(self):
'''
.. versionadded:: 1.3.1
Property that returns values of the current compass
(magnetic field) sensors, as a (x, y, z) tuple.
Returns (None, None, None) if no data is currently available.
'''
return self.get_orientation()
@property
def field_uncalib(self):
'''
.. versionadded:: 1.3.1
Property that returns the current value of Uncalibrated Magnetic Field
(without hard iron calibration) along with the iron bias estimation
along the three axes.
'''
return self.get_field_uncalib()
def enable(self):
'''
Activate the compass sensor.
'''
self._enable()
def disable(self):
'''
Disable the compass sensor.
'''
self._disable()
def get_orientation(self):
return self._get_orientation()
def get_field_uncalib(self):
'''
.. versionadded:: 1.3.1
'''
return self._get_field_uncalib()
# private
def _enable(self):
raise NotImplementedError()
def _disable(self):
raise NotImplementedError()
def _get_orientation(self):
raise NotImplementedError()
def _get_field_uncalib(self):
raise NotImplementedError()

View File

@ -0,0 +1,92 @@
'''
CPU
===
Simple Example
---------------
To get CPU count::
>>> from plyer import cpu
>>> # 1 socket, 1 core per socket, 2 threads per core
>>> cpu.sockets # 1 CPU socket (or slot)
1
>>> cpu.physical # 1 CPU socket * 1 core per socket
1
>>> cpu.logical # 1 CPU socket * 1 core per socket * 2 threads per core
2
Supported Platforms
-------------------
MacOS
Linux
Windows
'''
class CPU:
'''
Facade providing info about sockets, physical and logical
number of processors.
'''
@property
def sockets(self):
'''
Property that contains the count of CPU sockets.
'''
return self._sockets()
@property
def physical(self):
'''
Property that contains the total number of physical cores
(max core count) in the system.
.. note:: `sockets * cores per socket`
'''
return self._physical()
@property
def logical(self):
'''
Property that contains the total number of logical cores
(max thread count) in the system.
.. note:: `sockets * cores per socket * threads per core`
'''
return self._logical()
@property
def cache(self):
'''
Property that contains the count of L1, L2, L3 caches in the system
as a dictionary `{'L1': int, 'L2': int, 'L3': int}`.
'''
return self._cache()
@property
def numa(self):
'''
Property that contains the count of NUMA nodes in the system.
.. note:: https://en.wikipedia.org/wiki/Non-uniform_memory_access
'''
return self._numa()
# private
def _sockets(self):
raise NotImplementedError()
def _physical(self):
raise NotImplementedError()
def _logical(self):
raise NotImplementedError()
def _cache(self):
raise NotImplementedError()
def _numa(self):
raise NotImplementedError()

View File

@ -0,0 +1,44 @@
'''DeviceName facade.
Returns the following depending on the platform:
* **Android**: Android Device name
* **Linux**: Hostname of the machine
* **OS X**: Hostname of the machine
* **Windows**: Hostname of the machine
Simple Example
--------------
To get the Device Name::
>>> from plyer import devicename
>>> devicename.device_name
'Oneplus 3'
.. versionadded:: 2.1.0
- first release
Supported Platforms
-------------------
Android, Windows, OS X, Linux
'''
class DeviceName:
'''
DeviceName facade.
'''
@property
def device_name(self):
'''
Property that returns the device name of the platform.
'''
return self._get_device_name()
# private
def _get_device_name(self):
raise NotImplementedError()

View File

@ -0,0 +1,58 @@
'''
Email
=====
The :class:`Email` provides access to public methods to use email of your
device.
.. note::
On Android `INTERNET` permission is needed.
Simple Examples
---------------
To send an e-mail::
>>> from plyer import email
>>> recipient = 'abc@gmail.com'
>>> subject = 'Hi'
>>> text = 'This is an example.'
>>> create_chooser = False
>>> email.send(recipient=recipient, subject=subject, text=text,
create_chooser=create_chooser)
>>> # opens email interface where user can change the content.
Supported Platforms
-------------------
Android, iOS, Windows, OS X, Linux
'''
class Email:
'''
Email facade.
'''
def send(self, recipient=None, subject=None, text=None,
create_chooser=None):
'''
Open an email client message send window, prepopulated with the
given arguments.
:param recipient: Recipient of the message (str)
:param subject: Subject of the message (str)
:param text: Main body of the message (str)
:param create_chooser: Whether to display a program chooser to
handle the message (bool)
.. note:: create_chooser is only supported on Android
'''
self._send(recipient=recipient, subject=subject, text=text,
create_chooser=create_chooser)
# private
def _send(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,74 @@
'''
Native filechooser dialog facade.
=================================
open_file, save_file and choose_dir accept a number of arguments
listed below. They return either a list of paths (normally
absolute), or None if no file was selected or the operation was
canceled and no result is available.
Arguments:
* **path** *(string or None)*: a path that will be selected
by default, or None
* **multiple** *(bool)*: True if you want the dialog to
allow multiple file selection. (Note: Windows doesn't
support multiple directory selection)
* **filters** *(iterable)*: either a list of wildcard patterns
or of sequences that contain the name of the filter and any
number of wildcards that will be grouped under that name
(e.g. [["Music", "*mp3", "*ogg", "*aac"], "*jpg", "*py"])
* **preview** *(bool)*: True if you want the file chooser to
show a preview of the selected file, if supported by the
back-end.
* **title** *(string or None)*: The title of the file chooser
window, or None for the default title.
* **icon** *(string or None)*: Path to the icon of the file
chooser window (where supported), or None for the back-end's
default.
* **show_hidden** *(bool)*: Force showing hidden files (currently
supported only on Windows)
* **on_selection** *(func)*: Callback for fetching the selection.
Important: these methods will return only after user interaction.
Use threads or you will stop the mainloop if your app has one.
.. versionchanged:: 1.4.0
Added Android implementation for open_file()
Added ``on_selection`` kwarg for callback function
Supported Plaforms
------------------
Android, iOS, macOS, Linux, Windows
'''
class FileChooser:
'''
File Chooser facade.
'''
def open_file(self, *args, **kwargs):
"""
Open the file chooser in "open" mode.
"""
return self._file_selection_dialog(mode="open", *args, **kwargs)
def save_file(self, *args, **kwargs):
"""
Open the file chooser in "save" mode. Confirmation will be asked
when a file with the same name already exists.
"""
return self._file_selection_dialog(mode="save", *args, **kwargs)
def choose_dir(self, *args, **kwargs):
"""
Open the directory chooser. Note that on Windows this is very
limited. Consider writing your own chooser if you target that
platform and are planning on using unsupported features.
"""
return self._file_selection_dialog(mode="dir", *args, **kwargs)
# private
def _file_selection_dialog(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,77 @@
# coding=utf-8
'''
Flash
=====
The :class:`Flash` provides access to public methods to use flash of your
device.
.. note::
In android you need CAMERA, FLASHLIGHT permissions
to access flash.
.. versionadded:: 1.2.5
This can be used to activate the flash of your camera on
Android and iOS.
Simple Examples
---------------
To turn on flash::
>>> from plyer import flash
>>> flash.on()
To turn off flash::
>>> flash.off()
To release flash::
>>> flash.release()
Supported Platforms
-------------------
Android, iOS
'''
class Flash:
"""
Flash facade.
"""
def on(self):
"""
Activate the flash
"""
self._on()
def off(self):
"""
Deactiavte the flash
"""
self._off()
def release(self):
"""
Release any access to the Flash / Camera.
Call this when you're done using the Flash.
This will release the Camera, and stop any process.
Next call to `_on` will reactivate it.
"""
self._release()
# private
def _on(self):
raise NotImplementedError()
def _off(self):
raise NotImplementedError()
def _release(self):
pass

View File

@ -0,0 +1,96 @@
'''
GPS
====
.. versionadded:: 1.1
.. note::
On Android `INTERNET`, `ACCESS_FINE_LOCATION`, `ACCESS_COARSE_LOCATION`
permissions are needed.
.. note::
On iOS `NSLocationWhenInUseUsageDescription` key is required for app to
display geolocation usage permission prompt. Key can be added in Xcode
target `info` section or in ``Resources/<YourApp>-info.plist``.
App background mode (`on_pause`) also must be supported.
You need to set a `on_location` callback with the :meth:`configure` method.
This callback will receive a couple of keywords / values, that might be
different depending of their availability on the targeted platform.
Lat and lon are always available.
- lat: latitude of the last location, in degrees
- lon: longitude of the last location, in degrees
- speed: speed of the user, in meters/second over ground
- bearing: bearing in degrees
- altitude: altitude in meters above the sea level
Here is an example of the usage of gps::
from plyer import gps
def print_locations(**kwargs):
print 'lat: {lat}, lon: {lon}'.format(**kwargs)
gps.configure(on_location=print_locations)
gps.start()
# later
gps.stop()
Supported Platforms
-------------------
Android, iOS
'''
class GPS:
'''
GPS facade.
'''
def configure(self, on_location, on_status=None):
'''
Configure the GPS object. This method should be called before
:meth:`start`.
:param on_location: Function to call when receiving a new location
:param on_status: Function to call when a status message is received
:type on_location: callable, multiples keys/value will be passed.
:type on_status: callable, args are "message-type", "status"
.. warning::
The `on_location` and `on_status` callables might be called from
another thread than the thread used for creating the GPS object.
'''
self.on_location = on_location
self.on_status = on_status
self._configure()
def start(self, minTime=1000, minDistance=1):
'''
Start the GPS location updates.
Expects 2 parameters:
minTime: milliseconds. (float)
minDistance: meters. (float)
'''
self._start(minTime=minTime, minDistance=minDistance)
def stop(self):
'''
Stop the GPS location updates.
'''
self._stop()
# private
def _configure(self):
raise NotImplementedError()
def _start(self, **kwargs):
raise NotImplementedError()
def _stop(self):
raise NotImplementedError()

View File

@ -0,0 +1,38 @@
class Gravity:
'''Gravity facade.
.. versionadded:: 1.2.5
Supported Platforms:: Android
'''
@property
def gravity(self):
'''Property that returns values of the current gravity force
as a (x, y, z) tuple. Returns (None, None, None)
if no data is currently available.
'''
return self._get_gravity()
def enable(self):
'''Activate the gravity sensor. Throws an error if the
hardware is not available or not implemented on.
'''
self._enable()
def disable(self):
'''Disable the gravity sensor.
'''
self._disable()
# private
def _enable(self):
raise NotImplementedError()
def _disable(self):
raise NotImplementedError()
def _get_gravity(self):
raise NotImplementedError()

View File

@ -0,0 +1,126 @@
'''
Gyroscope
============
The gyroscope measures the rate of rotation around a device's x, y,
and z axis.
The :class:`Gyroscope` provides access to public methods to
use gyroscope of your device.
Simple Examples
---------------
To enable gyroscope::
>>> from plyer import gyroscope
>>> gyroscope.enable()
To disable gyroscope::
>>> gyroscope.disable()
To get the rate of rotation along the three axes::
>>> gyroscope.rotation
(-0.0034587313421070576, -0.0073830625042319298, 0.0046892408281564713)
To get the uncalibrated rate of rotation along the three axes along with the
drift compensation::
>>> gyroscope.rotation_uncalib
()
where the first three values show the rate of rotation w/o drift
compensation and the last three show the estimated drift along the three
axes.
Supported Platforms
-------------------
Android, iOS
'''
class Gyroscope:
'''
Gyroscope facade.
.. versionadded:: 1.3.1
'''
@property
def rotation(self):
'''
Property that returns the rate of rotation around the device's local
X, Y and Z axis.
Along x-axis: angular speed around the X axis
Along y-axis: angular speed around the Y axis
Along z-axis: angular speed around the Z axis
Returns (None, None, None) if no data is currently available.
'''
return self.get_orientation()
@property
def rotation_uncalib(self):
'''
Property that returns the current rate of rotation around the X, Y and
Z axis. An estimation of the drift on each axis is reported as well.
Along x-axis: angular speed (w/o drift compensation) around the X axis
Along y-axis: angular speed (w/o drift compensation) around the Y axis
Along z-axis: angular speed (w/o drift compensation) around the Z axis
Along x-axis: estimated drift around X axis
Along y-axis: estimated drift around Y axis
Along z-axis: estimated drift around Z axis
Returns (None, None, None, None, None, None) if no data is currently
available.
'''
return self.get_rotation_uncalib()
@property
def orientation(self):
'''
WARNING:: This property is deprecated after API Level 8.
Use `gyroscope.rotation` instead.
Property that returns values of the current Gyroscope sensors, as
a (x, y, z) tuple. Returns (None, None, None) if no data is currently
available.
'''
return self.get_orientation()
def enable(self):
'''
Activate the Gyroscope sensor.
'''
self._enable()
def disable(self):
'''
Disable the Gyroscope sensor.
'''
self._disable()
def get_orientation(self):
return self._get_orientation()
def get_rotation_uncalib(self):
return self._get_rotation_uncalib()
# private
def _enable(self):
raise NotImplementedError()
def _disable(self):
raise NotImplementedError()
def _get_orientation(self):
raise NotImplementedError()
def _get_rotation_uncalib(self):
raise NotImplementedError()

View File

@ -0,0 +1,34 @@
class Humidity:
'''Humidity facade.
Humidity sensor returns value of humidity.
With method `enable` you can turn on Humidity sensor and
'disable' method stops the sensor.
Use property `tell` to get humidity value.
Supported Platforms
-------------------
Android
'''
@property
def tell(self):
'''Current humidity'''
return self._get_humidity()
def enable(self):
'''Enable Humidity sensor.'''
self._enable()
def disable(self):
'''Disable Humidity sensor.'''
self._disable()
# private
def _get_humidity(self, **kwargs):
raise NotImplementedError()
def _enable(self, **kwargs):
raise NotImplementedError()
def _disable(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,104 @@
'''
IrBlaster
============
The :class:`IrBlaster` provides access to public methods by which your device
can act as a remote and could be used to control your TV, AC, Music Player,
Projectors, Set top box or anything that can be controlled by a remote.
.. note::
- On Android your app needs the TRANSMIT_IR permission which allows an
application to use the device's IR transmitter, If available.
Simple Examples
---------------
To get transmit an IR sequence::
>>> from plyer import irblaster
>>> irblaster.transmit(frequency, pattern, mode)
To get frequencies::
>>> irblaster.frequencies
To check if IrBlaster exists::
>>> irblaster.exists()
True/False
Supported Platforms
-------------------
Android
'''
class IrBlaster:
'''
Infrared blaster facade.
'''
@staticmethod
def periods_to_microseconds(frequency, pattern):
'''
Convert a pattern from period counts to microseconds.
'''
period = 1000000. / frequency
return [period * x for x in pattern]
@staticmethod
def microseconds_to_periods(frequency, pattern):
'''
Convert a pattern from microseconds to period counts.
'''
period = 1000000. / frequency
return [x / period for x in pattern]
@property
def frequencies(self):
'''
Property which contains a list of frequency ranges
supported by the device in the form:
[(from1, to1),
(from2, to2),
...
(fromN, toN)]
'''
return self.get_frequencies()
def get_frequencies(self):
return self._get_frequencies()
def transmit(self, frequency, pattern, mode='period'):
'''
Transmit an IR sequence.
:parameters:
`frequency`: int
Carrier frequency for the IR transmission.
`pattern`: list[int]
Burst pair pattern to transmit.
`mode`: str, defaults to 'period'
Specifies the format of the pattern values.
Can be 'period' or 'microseconds'.
'''
return self._transmit(frequency, pattern, mode)
def exists(self):
'''
Check if the device has an infrared emitter.
'''
return self._exists()
# private
def _get_frequencies(self):
raise NotImplementedError()
def _transmit(self, frequency, pattern, mode):
raise NotImplementedError()
def _exists(self):
raise NotImplementedError()

View File

@ -0,0 +1,32 @@
'''
Keystore
=======
The :class:`Keystore` provides a mechanism for securing/storing
cryptographic keys (such as user credentials) in a container.
Typically needed to support authentication APIs such as OAuth2
.. note::
Typically needed to support authentication APIs such as OAuth2
Supported Platforms
-------------------
Android, iOS, Windows, OS X, Linux
---------------
'''
class Keystore:
'''
Keystore facade
'''
def set_key(self, servicename, key, value, **kwargs):
self._set_key(servicename, key, value, **kwargs)
def _set_key(self, servicename, key, value, **kwargs):
raise NotImplementedError()
def get_key(self, servicename, key, **kwargs):
return self._get_key(servicename, key)
def _get_key(self, servicename, key, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,39 @@
class Light:
'''Light facade.
Light sensor measures the ambient light level(illumination) in lx.
Common uses include controlling screen brightness.
With method `enable` you can turn on the sensor and
`disable` method stops the sensor.
Use property `illumination` to get current illumination in lx.
.. versionadded:: 1.2.5
Supported Platforms:: Android
'''
@property
def illumination(self):
'''Current illumination in lx.'''
return self._get_illumination()
def enable(self):
'''Enable light sensor.'''
self._enable()
def disable(self):
'''Disable light sensor.'''
self._disable()
# private
def _get_illumination(self, **kwargs):
raise NotImplementedError()
def _enable(self, **kwargs):
raise NotImplementedError()
def _disable(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,93 @@
'''
Notification
============
The :class:`Notification` provides access to public methods to create
notifications.
Simple Examples
---------------
To send notification::
>>> from plyer import notification
>>> title = 'plyer'
>>> message = 'This is an example.'
>>> notification.notify(title=title, message=message)
Android toast notification::
>>> from plyer import notification
>>> notification.notify(message='hello', toast=True)
Android simple notification::
>>> from plyer import notification
>>> notification.notify(message='hello', toast=True)
Notification with custom icon::
>>> from plyer import notification
>>> notification.notify(title='title', message='hello', app_icon=<path>)
.. versionadded:: 1.0.0
.. versionadded:: 1.4.0
Add implementation of primitive Android popup-like notification (toast)
.. versionchanged:: 1.4.0
Android implementation now supports custom icons for notifications.
'''
class Notification:
'''
Notification facade.
'''
def notify(self, title='', message='', app_name='', app_icon='', notification_icon=None,
timeout=10, ticker='', toast=False, hints={}):
'''
Send a notification.
:param title: Title of the notification
:param message: Message of the notification
:param app_name: Name of the app launching this notification
:param app_icon: Icon to be displayed along with the message
:param timeout: time to display the message for, defaults to 10
:param ticker: text to display on status bar as the notification
arrives
:param toast: simple Android message instead of full notification
:param hints: Optional hints that can be used to pass along extra
instructions on Linux.
(See https://specifications.freedesktop.org/notification-spec/latest/ar01s08.html) # noqa: E501
:type title: str
:type message: str
:type app_name: str
:type app_icon: str
:type timeout: int
:type ticker: str
:type toast: bool
:type hints: dict
.. note::
When called on Windows, ``app_icon`` has to be a path to
a file in .ICO format.
.. versionadded:: 1.0.0
.. versionchanged:: 1.4.0
Add 'toast' keyword argument
'''
self._notify(
title=title, message=message,
app_icon=app_icon, app_name=app_name, notification_icon=notification_icon,
timeout=timeout, ticker=ticker, toast=toast, hints=hints
)
# private
def _notify(self, **kwargs):
raise NotImplementedError("No usable implementation found!")

View File

@ -0,0 +1,81 @@
'''
Orientation
==========
The :class:`Orientation` provides access to public methods to set orientation
of your device.
.. note::
These settings are generally guidelines, the operating
system may choose to ignore them, or they may be overridden by
other system components.
.. versionadded:: 1.2.4
Simple Examples
---------------
To set landscape::
>>> from plyer import orientation
>>> orientation.set_landscape()
To set portrait::
>>> orientation.set_portrait()
To set sensor::
>>> orientation.set_sensor()
Supported Platforms
-------------------
Android
'''
class Orientation:
'''
Orientation facade.
'''
def set_landscape(self, reverse=False):
'''
Rotate the app to a landscape orientation.
:param reverse: If True, uses the opposite of the natural
orientation.
'''
self._set_landscape(reverse=reverse)
def set_portrait(self, reverse=False):
'''
Rotate the app to a portrait orientation.
:param reverse: If True, uses the opposite of the natural
orientation.
'''
self._set_portrait(reverse=reverse)
def set_sensor(self, mode='any'):
'''
Rotate freely following sensor information from the device.
:param mode: The rotation mode, should be one of 'any' (rotate
to any orientation), 'landscape' (choose nearest
landscape mode) or 'portrait' (choose nearest
portrait mode). Defaults to 'any'.
'''
self._set_sensor(mode=mode)
# private
def _set_landscape(self, **kwargs):
raise NotImplementedError()
def _set_portrait(self, **kwargs):
raise NotImplementedError()
def _set_sensor(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,42 @@
'''
Number of Processors
=======
The :class:`Processors` provides a information on the number of
processors in a system
.. note::
Deprecated in favor of `cpu`
Simple Example
---------------
To get processors status::
>>> from plyer import processors
>>> processors.status
{'Number_of_Processors': '4'}
Supported Platforms
-------------------
Linux
'''
class Processors:
'''
Number of Processors info facade.
'''
@property
def status(self):
'''
Property that contains a dict with the following fields:
* **Number_of_Processors** *(int)*: Number of Processors in
the system
.. warning::
If any of the fields is not readable, it is set as
None.
'''
return self.get_state()
def get_state(self):
return self._get_state()
def _get_state(self):
raise NotImplementedError()

View File

@ -0,0 +1,44 @@
class Proximity:
'''Proximity facade.
The proximity sensor is commonly used to determine distance whether
phone is close to your head. Commonly is used when you have a call
and you stick your phone with your head. Then screen of phone turns off.
Use method `enable` to turn on proximity sensor and method `disable` for
turn off.
To check if some object (or your head) is near sensor check values from
property `proximity`. It returns `True` when object is close.
.. versionadded:: 1.2.5
Supported Platforms::Android
'''
@property
def proximity(self):
'''Return True or False depending if there is an object or not.
:return: True if there is an object. Otherwise False.
'''
return self._get_proximity()
def _enable(self, **kwargs):
raise NotImplementedError()
def enable(self):
'''Enable the proximity sensor.
'''
self._enable()
def _disable(self, **kwargs):
raise NotImplementedError()
def disable(self):
'''Disable the proximity sensor.
'''
self._disable()
def _get_proximity(self):
raise NotImplementedError()

View File

@ -0,0 +1,57 @@
'''
Screenshot
==========
The :class:`Screenshot` is used for capturing a digital image of what
is currently visible on the monitor.
The default path for taking screenshot is set in each platform implementation.
Simple Examples
---------------
To get the file path::
>>> screenshot.file_path
'/sdcard/test.jpg'
To set the file path::
>>> screenshot.file_path = '/Users/OSXUser/Pictures/screenshot.png'
To take screenshot::
>>> from plyer import screenshot
>>> screenshot.capture()
'''
class Screenshot:
'''
Screenshot facade.
'''
_file_path = ''
def __init__(self, file_path=None):
self._file_path = file_path
def capture(self):
self._capture()
@property
def file_path(self):
return self._file_path
@file_path.setter
def file_path(self, location):
'''
Location of the screenshot.
'''
self._file_path = location
# private
def _capture(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,51 @@
'''
Sms
====
The :class:`Sms` provides access to sending Sms from your device.
.. note::
On Android your app needs the SEND_SMS permission in order to
send sms messages.
.. versionadded:: 1.2.0
Simple Examples
---------------
To send sms::
>>> from plyer import sms
>>> recipient = 9999222299
>>> message = 'This is an example.'
>>> sms.send(recipient=recipient, message=message)
Supported Platforms
-------------------
Android, iOS
'''
class Sms:
'''
Sms facade.
'''
def send(self, recipient, message):
'''
Send SMS or open SMS interface.
:param recipient: The receiver
:param message: the message
:type recipient: number
:type message: str
'''
self._send(recipient=recipient, message=message)
# private
def _send(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,54 @@
# coding=utf-8
class SpatialOrientation:
'''Spatial Orientation facade.
Computes the device's orientation based on the rotation matrix.
.. versionadded:: 1.3.1
'''
@property
def orientation(self):
'''Property that returns values of the current device orientation
as a (azimuth, pitch, roll) tuple.
Azimuth, angle of rotation about the -z axis. This value represents the
angle between the device's y axis and the magnetic north pole.
The range of values is -π to π.
Pitch, angle of rotation about the x axis. This value represents the
angle between a plane parallel to the device's screen and a plane
parallel to the ground.
The range of values is -π to π.
Roll, angle of rotation about the y axis. This value represents the
angle between a plane perpendicular to the device's screen and a plane
perpendicular to the ground.
The range of values is -π/2 to π/2.
Returns (None, None, None) if no data is currently available.
Supported Platforms:: Android
'''
return self._get_orientation() or (None, None, None)
def _get_orientation(self):
raise NotImplementedError()
def enable_listener(self):
'''Enable the orientation sensor.
'''
self._enable_listener()
def _enable_listener(self, **kwargs):
raise NotImplementedError()
def disable_listener(self):
'''Disable the orientation sensor.
'''
self._disable_listener()
def _disable_listener(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,135 @@
'''
Storage Path
============
The StorgePath API can be used to gain access to standard storage locations
across platforms such as home directory, root directory, external storage
directory, documents, downloads, etc.
The :class:`StoragePath` provides access to public methods to access standard
storage locations.
Simple Examples
---------------
To get the path of user's home directory::
>>> from plyer import storagepath
>>> storagepath.get_home_dir()
To get the path of standard downloads directory::
>>> from plyer import storagepath
>>> storagepath.get_downloads_dir()
To get the path of directory holding application files::
>>> from plyer import storagepath
>>> storagepath.get_application_dir()
'''
class StoragePath:
'''
StoragePath facade.
'''
def get_home_dir(self):
'''
Get the path of home directory of current user.
'''
return self._get_home_dir()
def get_external_storage_dir(self):
'''
Get the path of primary shared or external storage directory.
'''
return self._get_external_storage_dir()
def get_sdcard_dir(self):
'''
Get the path of external SD card.
.. versionadded:: 1.4.0
'''
return self._get_sdcard_dir()
def get_root_dir(self):
'''
Get the path of root of the "system" partition holding the core OS.
'''
return self._get_root_dir()
def get_documents_dir(self):
'''
Get the path of standard directory in which to place documents that
have been created by the user.
'''
return self._get_documents_dir()
def get_downloads_dir(self):
'''
Get the path of standard directory in which to place files that have
been downloaded by the user.
'''
return self._get_downloads_dir()
def get_videos_dir(self):
'''
Get the path of standard directory in which to place videos that are
available to the user.
'''
return self._get_videos_dir()
def get_music_dir(self):
'''
Get the path of standard directory in which to place any audio files
that should be in the regular list of music for the user.
'''
return self._get_music_dir()
def get_pictures_dir(self):
'''
Standard directory in which to place pictures that are available to
the user.
'''
return self._get_pictures_dir()
def get_application_dir(self):
'''
Get the path of the directory holding application files.
'''
return self._get_application_dir()
# private
def _get_home_dir(self):
raise NotImplementedError()
def _get_external_storage_dir(self):
raise NotImplementedError()
def _get_sdcard_dir(self):
raise NotImplementedError()
def _get_root_dir(self):
raise NotImplementedError()
def _get_documents_dir(self):
raise NotImplementedError()
def _get_downloads_dir(self):
raise NotImplementedError()
def _get_videos_dir(self):
raise NotImplementedError()
def _get_music_dir(self):
raise NotImplementedError()
def _get_pictures_dir(self):
raise NotImplementedError()
def _get_application_dir(self):
raise NotImplementedError()

188
sbapp/plyer/facades/stt.py Normal file
View File

@ -0,0 +1,188 @@
'''
Speech to Text
==============
.. versionadded:: 1.4.0
Speech Recognition facade.
In order to check that your device supports voice recognition use method
`exist`.
Variable `language` indicates which language will be used to match words from
voice.
Use `start` to start voice recognition immediately and `stop` to stop.
.. note::
Needed permissions for Android: `RECORD_AUDIO` (and `INTERNET` if you want
online voice recognition API to be used)
.. note::
On Android platform, after execute `start` method you can hear BEEP!
Mute sound in order to disable it.
.. note::
For Android implementation to work there has to be an application with
`android.speech.RecognitionService` implementation present in the system.
Mostly it's `com.google.android.googlequicksearchbox` or "Google"
application (the search bar with the launcher widget).
Offline Speech Recognition on Android
-------------------------------------
Requires any application that provides an
`android.speech.RecognitionService` implementation to the other apps. One of
such applications is on a lot of devices preinstalled Google (quick search
box).
The API prefers offline recognition, but should be able to switch to online
alternative in case you don't have a language package installed (`INTERNET`
permission necessary).
You can enable offline speech recognition this way (Android 8.1):
* open the `Settings` app
* choose `Language & Input` / `Language & Keyboard` (Samsung might include it
in the `General` category)
* choose `On-Screen keyboard` or `Voice search`
* choose `Google Keyboard`
* choose `Offline Speech recognition`
* download language package if you don't have one already
Simple Examples
---------------
To start listening::
>>> from plyer import stt
>>> stt.start()
To retrieve partial results while listening::
>>> assert stt.listening
>>> print(stt.partial_results)
To stop listening::
>>> stt.stop()
To retrieve results after the listening stopped::
>>> print(stt.results)
'''
class STT:
'''
Speech to text facade.
'''
_language = 'en-US'
'''
Default language in which platform will try to recognize voice.
In order to change language pick one from list by using
`supported_languages` method.
'''
_supported_languages = [
'en-US',
'pl-PL'
]
results = []
'''
List of sentences found while listening. It may consist of many similar
and possible sentences that was recognition program.
'''
errors = []
'''
List of errors found while listening.
'''
partial_results = []
'''
List of results found while the listener is still being active.
'''
prefer_offline = True
'''
Preference whether to use offline language package necessary for
each platform dependant implementation or online API.
'''
listening = False
'''
Current state of listening.
'''
@property
def supported_languages(self):
'''
Return list of supported languages used in recognition.
'''
return self._supported_languages
@property
def language(self):
'''
Return current language.
'''
return self._language
@language.setter
def language(self, lang):
'''
Set current language.
Value can not be set if it's not supported. See `supported_languages`
to get what language you can set.
.. note::
We obviously can't check each language, therefore if you find
that a specific language is available to you and the only limitation
is our check for the internally defined `supported_languages`, feel
free to open a pull request for adding your language to the list.
'''
if lang in self.supported_languages:
self._language = lang
# public methods
def start(self):
'''
Start listening.
'''
self.results = []
self.partial_results = []
self._start()
self.listening = True
def stop(self):
'''
Stop listening.
'''
self._stop()
self.listening = False
def exist(self):
'''
Returns a boolean for speech recognition availability.
'''
return self._exist()
# private methods
def _start(self):
raise NotImplementedError
def _stop(self):
raise NotImplementedError
def _exist(self):
raise NotImplementedError

View File

@ -0,0 +1,41 @@
# coding=utf-8
class Temperature:
'''Temperature facade.
Temperature sensor is used to measure the ambient room temperature in
degrees Celsius (°C)
With method `enable` you can turn on temperature sensor and 'disable'
method stops the sensor.
Use property `temperature` to get ambient air temperature in degree C.
.. versionadded:: 1.2.5
Supported Platforms:: Android
'''
@property
def temperature(self):
'''Current air temperature in degree C.'''
return self._get_temperature()
def enable(self):
'''Enable temperature sensor.'''
self._enable()
def disable(self):
'''Disable temperature sensor.'''
self._disable()
# private
def _get_temperature(self, **kwargs):
raise NotImplementedError()
def _enable(self, **kwargs):
raise NotImplementedError()
def _disable(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,39 @@
'''
TTS
====
The :class:`TTS` provides provides access to public methods to
use text to speech of your device.
Simple Examples
---------------
To speak::
>>> from plyer import tts
>>> tts.speak(message=message)
Supported Platforms
-------------------
Android, iOS, Windows, OS X, Linux
'''
class TTS:
'''
TextToSpeech facade.
'''
def speak(self, message=''):
'''Use text to speech capabilities to speak the message.
:param message: What to speak
:type message: str
'''
self._speak(message=message)
# private
def _speak(self, **kwargs):
raise NotImplementedError()

View File

@ -0,0 +1,54 @@
'''UniqueID facade.
Returns the following depending on the platform:
* **Android**: Android ID
* **OS X**: Serial number of the device
* **Linux**: Serial number using lshw
* **Windows**: MachineGUID from regkey
* **iOS**: UUID
Simple Example
--------------
To get the unique ID::
>>> from plyer import uniqueid
>>> uniqueid.id
'1b1a7a4958e2a845'
.. versionadded:: 1.2.0
.. versionchanged:: 1.2.4
On Android returns Android ID instead of IMEI.
Supported Platforms
-------------------
Android, iOS, Windows, OS X, Linux
'''
class UniqueID:
'''
UniqueID facade.
'''
@property
def id(self):
'''
Property that returns the unique id of the platform.
'''
return self.get_uid()
def get_uid(self):
'''
Public method for receiving unique ID via platform-specific
API in plyer.platforms.
'''
return self._get_uid()
# private
def _get_uid(self):
raise NotImplementedError()

View File

@ -0,0 +1,97 @@
'''
Vibrator
=======
The :class:`Vibrator` provides access to public methods to use vibrator of your
device.
.. note::
On Android your app needs the VIBRATE permission to
access the vibrator.
Simple Examples
---------------
To vibrate your device::
>>> from plyer import vibrator
>>> time=2
>>> vibrator.vibrate(time=time)
To set a pattern::
>>> vibrator.pattern(pattern=pattern, repeat=repeat)
To know whether vibrator exists or not::
>>> vibrator.exists()
To cancel vibration::
>>> vibrator.cancel()
Supported Platforms
-------------------
Android, iOS
'''
class Vibrator:
'''
Vibration facade.
'''
def vibrate(self, time=1):
'''
Ask the vibrator to vibrate for the given period.
:param time: Time to vibrate for, in seconds. Default is 1.
'''
self._vibrate(time=time)
def pattern(self, pattern=(0, 1), repeat=-1):
'''
Ask the vibrator to vibrate with the given pattern, with an
optional repeat.
:param pattern: Pattern to vibrate with. Should be a list of
times in seconds. The first number is how long to wait
before vibrating, and subsequent numbers are times to
vibrate and not vibrate alternately.
Defaults to ``[0, 1]``.
:param repeat: Index at which to repeat the pattern. When the
vibration pattern reaches this index, it will start again
from the beginning. Defaults to ``-1``, which means no
repeat.
'''
self._pattern(pattern=pattern, repeat=repeat)
def exists(self):
'''
Check if the device has a vibrator. Returns True or
False.
'''
return self._exists()
def cancel(self):
'''
Cancels any current vibration, and stops the vibrator.
'''
self._cancel()
# private
def _vibrate(self, **kwargs):
raise NotImplementedError()
def _pattern(self, **kwargs):
raise NotImplementedError()
def _exists(self, **kwargs):
raise NotImplementedError()
def _cancel(self, **kwargs):
raise NotImplementedError()

187
sbapp/plyer/facades/wifi.py Normal file
View File

@ -0,0 +1,187 @@
'''
Wifi Facade.
=============
The :class:`Wifi` is to provide access to the wifi of your mobile/ desktop
devices.
It currently supports `connecting`, `disconnecting`, `scanning`, `getting
available wifi network list` and `getting network information`.
Simple examples
---------------
To enable/ turn on wifi scanning::
>>> from plyer import wifi
>>> wifi.start_scanning()
Once the wifi is enabled/ turned on, then this command starts to scan
all the nearby available wifi networks.
To get network info::
>>> from plyer import wifi
>>> wifi.start_scanning()
>>> return wifi.get_network_info(name)
Returns network details of the network who's name/ssid is provided in the
`name` parameter.
To connect to a network::
>>> from plyer import wifi
>>> wifi.start_scanning()
>>> wifi.connect(network, parameters)
This connects to the network who's name/ssid is provided under `network`
parameter and along with other necessary methods for connection
which depends upon platform to platform.
please visit following files for more details about requirements of
`paramaters` argument in `connect` method:
plyer/platforms/win/wifi.py
plyer/platforms/macosx/wifi.py
plyer/platforms/win/wifi.py
To disconnect from wifi::
>>> from plyer import wifi
>>> wifi.disconnect()
This disconnects your device from any wifi network.
To get available wifi networks::
>>> from plyer import wifi
>>> wifi.start_scanning()
>>> return wifi.get_available_wifi()
This returns all the available wifi networks near the device.
Supported Platforms
-------------------
Windows, OS X, Linux
Ex: 6
----------
from plyer import wifi
wifi.enable()
This enables wifi device.
Ex: 7
----------
from plyer import wifi
wifi.disable()
This disable wifi device
'''
class Wifi:
'''
Wifi Facade.
'''
def is_enabled(self):
'''
Return enabled status of WiFi hardware.
'''
return self._is_enabled()
def is_connected(self, interface=None):
'''
Return connection state of WiFi interface.
.. versionadded:: 1.4.0
'''
return self._is_connected(interface=interface)
@property
def interfaces(self):
'''
List all available WiFi interfaces.
.. versionadded:: 1.4.0
'''
raise NotImplementedError()
def start_scanning(self, interface=None):
'''
Turn on scanning.
'''
return self._start_scanning(interface=interface)
def get_network_info(self, name):
'''
Return a dictionary of specified network.
'''
return self._get_network_info(name=name)
def get_available_wifi(self):
'''
Returns a list of all the available wifi.
'''
return self._get_available_wifi()
def connect(self, network, parameters, interface=None):
'''
Method to connect to some network.
'''
self._connect(
network=network,
parameters=parameters,
interface=interface
)
def disconnect(self, interface=None):
'''
To disconnect from some network.
'''
self._disconnect(interface=interface)
def enable(self):
'''
Wifi interface power state is set to "ON".
'''
self._enable()
def disable(self):
'''
Wifi interface power state is set to "OFF".
'''
self._disable()
# private
def _is_enabled(self):
raise NotImplementedError()
def _is_connected(self, interface=None):
raise NotImplementedError()
def _start_scanning(self, interface=None):
raise NotImplementedError()
def _get_network_info(self, **kwargs):
raise NotImplementedError()
def _get_available_wifi(self):
raise NotImplementedError()
def _connect(self, **kwargs):
raise NotImplementedError()
def _disconnect(self, interface=None):
raise NotImplementedError()
def _enable(self):
raise NotImplementedError()
def _disable(self):
raise NotImplementedError()

View File

View File

@ -0,0 +1,18 @@
from os import environ
from jnius import autoclass
ANDROID_VERSION = autoclass('android.os.Build$VERSION')
SDK_INT = ANDROID_VERSION.SDK_INT
try:
from android import config
ns = config.JAVA_NAMESPACE
except (ImportError, AttributeError):
ns = 'org.renpy.android'
if 'PYTHON_SERVICE_ARGUMENT' in environ:
PythonService = autoclass(ns + '.PythonService')
activity = PythonService.mService
else:
PythonActivity = autoclass(ns + '.PythonActivity')
activity = PythonActivity.mActivity

View File

@ -0,0 +1,79 @@
'''
Android accelerometer
---------------------
'''
from plyer.facades import Accelerometer
from jnius import PythonJavaClass, java_method, autoclass, cast
from plyer.platforms.android import activity
Context = autoclass('android.content.Context')
Sensor = autoclass('android.hardware.Sensor')
SensorManager = autoclass('android.hardware.SensorManager')
class AccelerometerSensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
self.SensorManager = cast(
'android.hardware.SensorManager',
activity.getSystemService(Context.SENSOR_SERVICE)
)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_ACCELEROMETER
)
self.values = [None, None, None]
def enable(self):
self.SensorManager.registerListener(
self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.values = event.values[:3]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
# Maybe, do something in future?
pass
class AndroidAccelerometer(Accelerometer):
def __init__(self):
super().__init__()
self.bState = False
def _enable(self):
if (not self.bState):
self.listener = AccelerometerSensorListener()
self.listener.enable()
self.bState = True
def _disable(self):
if (self.bState):
self.bState = False
self.listener.disable()
del self.listener
def _get_acceleration(self):
if (self.bState):
return tuple(self.listener.values)
else:
return (None, None, None)
def __del__(self):
if(self.bState):
self._disable()
super().__del__()
def instance():
return AndroidAccelerometer()

View File

@ -0,0 +1,58 @@
from jnius import autoclass
from plyer.facades.audio import Audio
# Recorder Classes
MediaRecorder = autoclass('android.media.MediaRecorder')
AudioSource = autoclass('android.media.MediaRecorder$AudioSource')
OutputFormat = autoclass('android.media.MediaRecorder$OutputFormat')
AudioEncoder = autoclass('android.media.MediaRecorder$AudioEncoder')
# Player Classes
MediaPlayer = autoclass('android.media.MediaPlayer')
class AndroidAudio(Audio):
'''Audio for android.
For recording audio we use MediaRecorder Android class.
For playing audio we use MediaPlayer Android class.
'''
def __init__(self, file_path=None):
default_path = '/sdcard/testrecorder.3gp'
super().__init__(file_path or default_path)
self._recorder = None
self._player = None
def _start(self):
self._recorder = MediaRecorder()
self._recorder.setAudioSource(AudioSource.DEFAULT)
self._recorder.setOutputFormat(OutputFormat.DEFAULT)
self._recorder.setAudioEncoder(AudioEncoder.DEFAULT)
self._recorder.setOutputFile(self.file_path)
self._recorder.prepare()
self._recorder.start()
def _stop(self):
if self._recorder:
self._recorder.stop()
self._recorder.release()
self._recorder = None
if self._player:
self._player.stop()
self._player.release()
self._player = None
def _play(self):
self._player = MediaPlayer()
self._player.setDataSource(self.file_path)
self._player.prepare()
self._player.start()
def instance():
return AndroidAudio()

View File

@ -0,0 +1,65 @@
from jnius import autoclass
from jnius import cast
from jnius import java_method
from jnius import PythonJavaClass
from plyer.facades import Barometer
from plyer.platforms.android import activity
ActivityInfo = autoclass('android.content.pm.ActivityInfo')
Context = autoclass('android.content.Context')
Sensor = autoclass('android.hardware.Sensor')
SensorManager = autoclass('android.hardware.SensorManager')
class BarometerSensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
service = activity.getSystemService(Context.SENSOR_SERVICE)
self.SensorManager = cast('android.hardware.SensorManager', service)
self.sensor = self.SensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE)
self.value = None
def enable(self):
self.SensorManager.registerListener(
self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.value = event.values[0]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
pass
class AndroidBarometer(Barometer):
listener = None
def _get_pressure(self):
if self.listener and self.listener.value:
pressure = self.listener.value
return pressure
def _enable(self):
if not self.listener:
self.listener = BarometerSensorListener()
self.listener.enable()
def _disable(self):
if self.listener:
self.listener.disable()
delattr(self, 'listener')
def instance():
return AndroidBarometer()

View File

@ -0,0 +1,47 @@
'''
Module of Android API for plyer.battery.
'''
from jnius import autoclass, cast
from plyer.platforms.android import activity
from plyer.facades import Battery
Intent = autoclass('android.content.Intent')
BatteryManager = autoclass('android.os.BatteryManager')
IntentFilter = autoclass('android.content.IntentFilter')
class AndroidBattery(Battery):
'''
Implementation of Android battery API.
'''
def _get_state(self):
status = {"isCharging": None, "percentage": None}
ifilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
battery_status = cast(
'android.content.Intent',
activity.registerReceiver(None, ifilter)
)
query = battery_status.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
is_charging = query == BatteryManager.BATTERY_STATUS_CHARGING
is_full = query == BatteryManager.BATTERY_STATUS_FULL
level = battery_status.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
scale = battery_status.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
percentage = (level / float(scale)) * 100
status['isCharging'] = is_charging or is_full
status['percentage'] = percentage
return status
def instance():
'''
Instance for facade proxy.
'''
return AndroidBattery()

View File

@ -0,0 +1,32 @@
'''
Module of Android API for plyer.bluetooth.
'''
from jnius import autoclass
from plyer.platforms.android import activity
from plyer.facades import Bluetooth
Global = autoclass('android.provider.Settings$Global')
class AndroidBluetooth(Bluetooth):
'''
Implementation of Android Bluetooth API.
'''
def _get_info(self):
bluetooth_enabled = Global.getString(
activity.getContentResolver(),
Global.BLUETOOTH_ON
)
status = 'off'
if bluetooth_enabled:
status = 'on'
return status
def instance():
'''
Instance for facade proxy.
'''
return AndroidBluetooth()

View File

@ -0,0 +1,34 @@
'''
Android Brightness
------------------
'''
from jnius import autoclass
from plyer.facades import Brightness
from android import mActivity
System = autoclass('android.provider.Settings$System')
class AndroidBrightness(Brightness):
def _current_level(self):
System.putInt(
mActivity.getContentResolver(),
System.SCREEN_BRIGHTNESS_MODE,
System.SCREEN_BRIGHTNESS_MODE_MANUAL)
cr_level = System.getInt(
mActivity.getContentResolver(),
System.SCREEN_BRIGHTNESS)
return (cr_level / 255.) * 100
def _set_level(self, level):
System.putInt(
mActivity.getContentResolver(),
System.SCREEN_BRIGHTNESS,
(level / 100.) * 255)
def instance():
return AndroidBrightness()

View File

@ -0,0 +1,29 @@
'''
Android Call
-----------
'''
from jnius import autoclass
from plyer.facades import Call
from plyer.platforms.android import activity
Intent = autoclass('android.content.Intent')
uri = autoclass('android.net.Uri')
class AndroidCall(Call):
def _makecall(self, **kwargs):
intent = Intent(Intent.ACTION_CALL)
tel = kwargs.get('tel')
intent.setData(uri.parse("tel:{}".format(tel)))
activity.startActivity(intent)
def _dialcall(self, **kwargs):
intent_ = Intent(Intent.ACTION_DIAL)
activity.startActivity(intent_)
def instance():
return AndroidCall()

View File

@ -0,0 +1,59 @@
import android
import android.activity
from os import remove
from jnius import autoclass, cast
from plyer.facades import Camera
from plyer.platforms.android import activity
Intent = autoclass('android.content.Intent')
PythonActivity = autoclass('org.kivy.android.PythonActivity')
MediaStore = autoclass('android.provider.MediaStore')
Uri = autoclass('android.net.Uri')
class AndroidCamera(Camera):
def _take_picture(self, on_complete, filename=None):
assert(on_complete is not None)
self.on_complete = on_complete
self.filename = filename
android.activity.unbind(on_activity_result=self._on_activity_result)
android.activity.bind(on_activity_result=self._on_activity_result)
intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
uri = Uri.parse('file://' + filename)
parcelable = cast('android.os.Parcelable', uri)
intent.putExtra(MediaStore.EXTRA_OUTPUT, parcelable)
activity.startActivityForResult(intent, 0x123)
def _take_video(self, on_complete, filename=None):
assert(on_complete is not None)
self.on_complete = on_complete
self.filename = filename
android.activity.unbind(on_activity_result=self._on_activity_result)
android.activity.bind(on_activity_result=self._on_activity_result)
intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)
uri = Uri.parse('file://' + filename)
parcelable = cast('android.os.Parcelable', uri)
intent.putExtra(MediaStore.EXTRA_OUTPUT, parcelable)
# 0 = low quality, suitable for MMS messages,
# 1 = high quality
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1)
activity.startActivityForResult(intent, 0x123)
def _on_activity_result(self, requestCode, resultCode, intent):
if requestCode != 0x123:
return
android.activity.unbind(on_activity_result=self._on_activity_result)
if self.on_complete(self.filename):
self._remove(self.filename)
def _remove(self, fn):
try:
remove(fn)
except OSError:
pass
def instance():
return AndroidCamera()

View File

@ -0,0 +1,119 @@
'''
Android Compass
---------------------
'''
from plyer.facades import Compass
from jnius import PythonJavaClass, java_method, autoclass, cast
from plyer.platforms.android import activity
Context = autoclass('android.content.Context')
Sensor = autoclass('android.hardware.Sensor')
SensorManager = autoclass('android.hardware.SensorManager')
class MFUSensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
service = activity.getSystemService(Context.SENSOR_SERVICE)
self.SensorManager = cast('android.hardware.SensorManager', service)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED)
self.values = [None, None, None, None, None, None]
def enable(self):
self.SensorManager.registerListener(
self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.values = event.values[:6]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
pass
class MagneticFieldSensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
self.SensorManager = cast(
'android.hardware.SensorManager',
activity.getSystemService(Context.SENSOR_SERVICE)
)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_MAGNETIC_FIELD
)
self.values = [None, None, None]
def enable(self):
self.SensorManager.registerListener(
self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.values = event.values[:3]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
# Maybe, do something in future?
pass
class AndroidCompass(Compass):
def __init__(self):
super().__init__()
self.bState = False
def _enable(self):
if (not self.bState):
self.listenerm = MagneticFieldSensorListener()
self.listenermu = MFUSensorListener()
self.listenerm.enable()
self.listenermu.enable()
self.bState = True
def _disable(self):
if (self.bState):
self.bState = False
self.listenerm.disable()
self.listenermu.disable()
del self.listenerm
del self.listenermu
def _get_orientation(self):
if (self.bState):
return tuple(self.listenerm.values)
else:
return (None, None, None)
def _get_field_uncalib(self):
if (self.bState):
return tuple(self.listenermu.values)
else:
return (None, None, None, None, None, None)
def __del__(self):
if(self.bState):
self._disable()
super().__del__()
def instance():
return AndroidCompass()

View File

@ -0,0 +1,33 @@
'''
Module of Android API for plyer.devicename.
'''
from jnius import autoclass
from plyer.facades import DeviceName
Build = autoclass('android.os.Build')
class AndroidDeviceName(DeviceName):
'''
Implementation of Android devicename API.
'''
def _get_device_name(self):
"""
Method to get the device name aka model in an android environment.
Changed the implementation from 'android.provider.Settings.Global' to
'android.os.Build' because 'android.provider.Settings.Global' was
introduced in API 17 whereas 'android.os.Build' is present since API 1
Thereby making this method more backward compatible.
"""
return Build.MODEL
def instance():
'''
Instance for facade proxy.
'''
return AndroidDeviceName()

View File

@ -0,0 +1,58 @@
'''
Module of Android API for plyer.email.
'''
from jnius import autoclass, cast
from plyer.facades import Email
from plyer.platforms.android import activity
Intent = autoclass('android.content.Intent')
AndroidString = autoclass('java.lang.String')
class AndroidEmail(Email):
'''
Implementation of Android email API.
'''
def _send(self, **kwargs):
intent = Intent(Intent.ACTION_SEND)
intent.setType('text/plain')
recipient = kwargs.get('recipient')
subject = kwargs.get('subject')
text = kwargs.get('text')
create_chooser = kwargs.get('create_chooser')
if recipient:
intent.putExtra(Intent.EXTRA_EMAIL, [recipient])
if subject:
android_subject = cast(
'java.lang.CharSequence',
AndroidString(subject)
)
intent.putExtra(Intent.EXTRA_SUBJECT, android_subject)
if text:
android_text = cast(
'java.lang.CharSequence',
AndroidString(text)
)
intent.putExtra(Intent.EXTRA_TEXT, android_text)
if create_chooser:
chooser_title = cast(
'java.lang.CharSequence',
AndroidString('Send message with:')
)
activity.startActivity(
Intent.createChooser(intent, chooser_title)
)
else:
activity.startActivity(intent)
def instance():
'''
Instance for facade proxy.
'''
return AndroidEmail()

View File

@ -0,0 +1,447 @@
'''
Android file chooser
--------------------
Android runs ``Activity`` asynchronously via pausing our ``PythonActivity``
and starting a new one in the foreground. This means
``AndroidFileChooser._open_file()`` will always return the default value of
``AndroidFileChooser.selection`` i.e. ``None``.
After the ``Activity`` (for us it's the file chooser ``Intent``) is completed,
Android moves it to the background (or destroys or whatever is implemented)
and pushes ``PythonActivity`` to the foreground.
We have a custom listener for ``android.app.Activity.onActivityResult()``
via `android` package from `python-for-android` recipe,
``AndroidFileChooser._on_activity_result()`` which is called independently of
any our action (we may call anything from our application in Python and this
handler will be called nevertheless on each ``android.app.Activity`` result
in the system).
In the handler we check if the ``request_code`` matches the code passed to the
``Context.startActivityForResult()`` i.e. if the result from
``android.app.Activity`` is indeed meant for our ``PythonActivity`` and then we
proceed.
Since the ``android.app.Activity.onActivityResult()`` is the only way for us
to intercept the result and we have a handler bound via ``android`` package,
we need to get the path/file/... selection to the user the same way.
Threading + ``Thread.join()`` or ``time.sleep()`` or any other kind of waiting
for the result is not an option because:
1) ``android.app.Activity.onActivityResult()`` might remain unexecuted if
the launched file chooser activity does not return the result (``Activity``
dies/freezes/etc).
2) Thread will be still waiting for the result e.g. an update of a value or
to actually finish, however the result from the call of
``AndroidFileChooser._open_file()`` will be returned nevertheless and anything
using that result will use an incorrect one i.e. the default value of
``AndroidFilechooser.selection`` (``None``).
.. versionadded:: 1.4.0
'''
from os.path import join, basename
from random import randint
from android import activity, mActivity
from jnius import autoclass, cast, JavaException
from plyer.facades import FileChooser
from plyer import storagepath
Environment = autoclass("android.os.Environment")
String = autoclass('java.lang.String')
Intent = autoclass('android.content.Intent')
Activity = autoclass('android.app.Activity')
DocumentsContract = autoclass('android.provider.DocumentsContract')
ContentUris = autoclass('android.content.ContentUris')
Uri = autoclass('android.net.Uri')
Long = autoclass('java.lang.Long')
IMedia = autoclass('android.provider.MediaStore$Images$Media')
VMedia = autoclass('android.provider.MediaStore$Video$Media')
AMedia = autoclass('android.provider.MediaStore$Audio$Media')
class AndroidFileChooser(FileChooser):
'''
FileChooser implementation for Android using
the built-in file browser via Intent.
.. versionadded:: 1.4.0
'''
# filechooser activity <-> result pair identification
select_code = None
# default selection value
selection = None
# select multiple files
multiple = False
# mime types
mime_type = {
"doc": "application/msword",
"docx": "application/vnd.openxmlformats-officedocument." +
"wordprocessingml.document",
"ppt": "application/vnd.ms-powerpoint",
"pptx": "application/vnd.openxmlformats-officedocument." +
"presentationml.presentation",
"xls": "application/vnd.ms-excel",
"xlsx": "application/vnd.openxmlformats-officedocument." +
"spreadsheetml.sheet",
"text": "text/*",
"pdf": "application/pdf",
"zip": "application/zip",
"image": "image/*",
"video": "video/*",
"audio": "audio/*",
"application": "application/*"}
selected_mime_type = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.select_code = randint(123456, 654321)
self.selection = None
# bind a function for a response from filechooser activity
activity.bind(on_activity_result=self._on_activity_result)
@staticmethod
def _handle_selection(selection):
'''
Dummy placeholder for returning selection from
``android.app.Activity.onActivityResult()``.
.. versionadded:: 1.4.0
'''
return selection
def _open_file(self, **kwargs):
'''
Running Android Activity is non-blocking and the only call
that blocks is onActivityResult running in GUI thread
.. versionadded:: 1.4.0
'''
# set up selection handler
# startActivityForResult is async
# onActivityResult is sync
self._handle_selection = kwargs.pop(
'on_selection', self._handle_selection
)
self.selected_mime_type = \
kwargs.pop("filters")[0] if "filters" in kwargs else ""
# create Intent for opening
file_intent = Intent(Intent.ACTION_GET_CONTENT)
if not self.selected_mime_type or \
type(self.selected_mime_type) != str or \
self.selected_mime_type not in self.mime_type:
file_intent.setType("*/*")
else:
file_intent.setType(self.mime_type[self.selected_mime_type])
file_intent.addCategory(
Intent.CATEGORY_OPENABLE
)
# use putExtra to allow multiple file selection
if kwargs.get('multiple', self.multiple):
file_intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, True)
# start a new activity from PythonActivity
# which creates a filechooser via intent
mActivity.startActivityForResult(
Intent.createChooser(file_intent, cast(
'java.lang.CharSequence',
String("FileChooser")
)),
self.select_code
)
def _on_activity_result(self, request_code, result_code, data):
'''
Listener for ``android.app.Activity.onActivityResult()`` assigned
via ``android.activity.bind()``.
.. versionadded:: 1.4.0
'''
# not our response
if request_code != self.select_code:
return
if result_code != Activity.RESULT_OK:
# The action had been cancelled.
return
selection = []
# Process multiple URI if multiple files selected
try:
for count in range(data.getClipData().getItemCount()):
ele = self._resolve_uri(
data.getClipData().getItemAt(count).getUri()) or []
selection.append(ele)
except Exception:
selection = [self._resolve_uri(data.getData()), ]
# return value to object
self.selection = selection
# return value via callback
self._handle_selection(selection)
@staticmethod
def _handle_external_documents(uri):
'''
Selection from the system filechooser when using ``Phone``
or ``Internal storage`` or ``SD card`` option from menu.
.. versionadded:: 1.4.0
'''
file_id = DocumentsContract.getDocumentId(uri)
file_type, file_name = file_id.split(':')
# internal SD card mostly mounted as a files storage in phone
internal = storagepath.get_external_storage_dir()
# external (removable) SD card i.e. microSD
external = storagepath.get_sdcard_dir()
try:
external_base = basename(external)
except TypeError:
external_base = basename(internal)
# resolve sdcard path
sd_card = internal
# because external might have /storage/.../1 or other suffix
# and file_type might be only a part of the real folder in /storage
if file_type in external_base or external_base in file_type:
sd_card = external
elif file_type == "home":
sd_card = join(Environment.getExternalStorageDirectory(
).getAbsolutePath(), Environment.DIRECTORY_DOCUMENTS)
return join(sd_card, file_name)
@staticmethod
def _handle_media_documents(uri):
'''
Selection from the system filechooser when using ``Images``
or ``Videos`` or ``Audio`` option from menu.
.. versionadded:: 1.4.0
'''
file_id = DocumentsContract.getDocumentId(uri)
file_type, file_name = file_id.split(':')
selection = '_id=?'
if file_type == 'image':
uri = IMedia.EXTERNAL_CONTENT_URI
elif file_type == 'video':
uri = VMedia.EXTERNAL_CONTENT_URI
elif file_type == 'audio':
uri = AMedia.EXTERNAL_CONTENT_URI
return file_name, selection, uri
@staticmethod
def _handle_downloads_documents(uri):
'''
Selection from the system filechooser when using ``Downloads``
option from menu. Might not work all the time due to:
1) invalid URI:
jnius.jnius.JavaException:
JVM exception occurred: Unknown URI:
content://downloads/public_downloads/1034
2) missing URI / android permissions
jnius.jnius.JavaException:
JVM exception occurred:
Permission Denial: reading
com.android.providers.downloads.DownloadProvider uri
content://downloads/all_downloads/1034 from pid=2532, uid=10455
requires android.permission.ACCESS_ALL_DOWNLOADS,
or grantUriPermission()
Workaround:
Selecting path from ``Phone`` -> ``Download`` -> ``<file>``
(or ``Internal storage``) manually.
.. versionadded:: 1.4.0
'''
# known locations, differ between machines
downloads = [
'content://downloads/public_downloads',
'content://downloads/my_downloads',
# all_downloads requires separate permission
# android.permission.ACCESS_ALL_DOWNLOADS
'content://downloads/all_downloads'
]
file_id = DocumentsContract.getDocumentId(uri)
try_uris = [
ContentUris.withAppendedId(
Uri.parse(down), Long.valueOf(file_id)
)
for down in downloads
]
# try all known Download folder uris
# and handle JavaExceptions due to different locations
# for content:// downloads or missing permission
path = None
for down in try_uris:
try:
path = AndroidFileChooser._parse_content(
uri=down, projection=['_data'],
selection=None,
selection_args=None,
sort_order=None
)
except JavaException:
import traceback
traceback.print_exc()
# we got a path, ignore the rest
if path:
break
# alternative approach to Downloads by joining
# all data items from Activity result
if not path:
for down in try_uris:
try:
path = AndroidFileChooser._parse_content(
uri=down, projection=None,
selection=None,
selection_args=None,
sort_order=None,
index_all=True
)
except JavaException:
import traceback
traceback.print_exc()
# we got a path, ignore the rest
if path:
break
return path
def _resolve_uri(self, uri):
'''
Resolve URI input from ``android.app.Activity.onActivityResult()``.
.. versionadded:: 1.4.0
'''
uri_authority = uri.getAuthority()
uri_scheme = uri.getScheme().lower()
path = None
file_name = None
selection = None
downloads = None
# This does not allow file selected from google photos or gallery
# or even any other file explorer to work
# not a document URI, nothing to convert from
# if not DocumentsContract.isDocumentUri(mActivity, uri):
# return path
if uri_authority == 'com.android.externalstorage.documents':
return self._handle_external_documents(uri)
# in case a user selects a file from 'Downloads' section
# note: this won't be triggered if a user selects a path directly
# e.g.: Phone -> Download -> <some file>
elif uri_authority == 'com.android.providers.downloads.documents':
path = downloads = self._handle_downloads_documents(uri)
elif uri_authority == 'com.android.providers.media.documents':
file_name, selection, uri = self._handle_media_documents(uri)
# parse content:// scheme to path
if uri_scheme == 'content' and not downloads:
try:
path = self._parse_content(
uri=uri, projection=['_data'], selection=selection,
selection_args=file_name, sort_order=None
)
except JavaException: # handles array error for selection_args
path = self._parse_content(
uri=uri, projection=['_data'], selection=selection,
selection_args=[file_name], sort_order=None
)
# nothing to parse, file:// will return a proper path
elif uri_scheme == 'file':
path = uri.getPath()
return path
@staticmethod
def _parse_content(
uri, projection, selection, selection_args, sort_order,
index_all=False
):
'''
Parser for ``content://`` URI returned by some Android resources.
.. versionadded:: 1.4.0
'''
result = None
resolver = mActivity.getContentResolver()
read = Intent.FLAG_GRANT_READ_URI_PERMISSION
write = Intent.FLAG_GRANT_READ_URI_PERMISSION
persist = Intent.FLAG_GRANT_READ_URI_PERMISSION
# grant permission for our activity
mActivity.grantUriPermission(
mActivity.getPackageName(),
uri,
read | write | persist
)
if not index_all:
cursor = resolver.query(
uri, projection, selection,
selection_args, sort_order
)
idx = cursor.getColumnIndex(projection[0])
if idx != -1 and cursor.moveToFirst():
result = cursor.getString(idx)
else:
result = []
cursor = resolver.query(
uri, projection, selection,
selection_args, sort_order
)
while cursor.moveToNext():
for idx in range(cursor.getColumnCount()):
result.append(cursor.getString(idx))
result = '/'.join(result)
return result
def _file_selection_dialog(self, **kwargs):
mode = kwargs.pop('mode', None)
if mode == 'open':
self._open_file(**kwargs)
def instance():
return AndroidFileChooser()

View File

@ -0,0 +1,55 @@
# coding=utf-8
"""
Flash
-----
"""
from plyer.facades import Flash
from jnius import autoclass
from plyer.platforms.android import activity
Camera = autoclass("android.hardware.Camera")
CameraParameters = autoclass("android.hardware.Camera$Parameters")
SurfaceTexture = autoclass("android.graphics.SurfaceTexture")
PackageManager = autoclass('android.content.pm.PackageManager')
pm = activity.getPackageManager()
flash_available = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)
class AndroidFlash(Flash):
_camera = None
def _on(self):
if self._camera is None:
self._camera_open()
if not self._camera:
return
self._camera.setParameters(self._f_on)
def _off(self):
if not self._camera:
return
self._camera.setParameters(self._f_off)
def _release(self):
if not self._camera:
return
self._camera.stopPreview()
self._camera.release()
self._camera = None
def _camera_open(self):
if not flash_available:
return
self._camera = Camera.open()
self._f_on = Camera.getParameters()
self._f_off = Camera.getParameters()
self._f_on.setFlashMode(CameraParameters.FLASH_MODE_TORCH)
self._f_off.setFlashMode(CameraParameters.FLASH_MODE_OFF)
self._camera.startPreview()
# Need this for Nexus 5
self._camera.setPreviewTexture(SurfaceTexture(0))
def instance():
return AndroidFlash()

View File

@ -0,0 +1,82 @@
'''
Android GPS
-----------
'''
from plyer.facades import GPS
from plyer.platforms.android import activity
from jnius import autoclass, java_method, PythonJavaClass
Looper = autoclass('android.os.Looper')
LocationManager = autoclass('android.location.LocationManager')
Context = autoclass('android.content.Context')
class _LocationListener(PythonJavaClass):
__javainterfaces__ = ['android/location/LocationListener']
def __init__(self, root):
self.root = root
super().__init__()
@java_method('(Landroid/location/Location;)V')
def onLocationChanged(self, location):
self.root.on_location(
lat=location.getLatitude(),
lon=location.getLongitude(),
speed=location.getSpeed(),
bearing=location.getBearing(),
altitude=location.getAltitude(),
accuracy=location.getAccuracy())
@java_method('(Ljava/lang/String;)V')
def onProviderEnabled(self, status):
if self.root.on_status:
self.root.on_status('provider-enabled', status)
@java_method('(Ljava/lang/String;)V')
def onProviderDisabled(self, status):
if self.root.on_status:
self.root.on_status('provider-disabled', status)
@java_method('(Ljava/lang/String;ILandroid/os/Bundle;)V')
def onStatusChanged(self, provider, status, extras):
if self.root.on_status:
s_status = 'unknown'
if status == 0x00:
s_status = 'out-of-service'
elif status == 0x01:
s_status = 'temporarily-unavailable'
elif status == 0x02:
s_status = 'available'
self.root.on_status('provider-status', '{}: {}'.format(
provider, s_status))
class AndroidGPS(GPS):
def _configure(self):
if not hasattr(self, '_location_manager'):
self._location_manager = activity.getSystemService(
Context.LOCATION_SERVICE
)
self._location_listener = _LocationListener(self)
def _start(self, **kwargs):
min_time = kwargs.get('minTime')
min_distance = kwargs.get('minDistance')
providers = self._location_manager.getProviders(False).toArray()
for provider in providers:
self._location_manager.requestLocationUpdates(
provider,
min_time, # minTime, in milliseconds
min_distance, # minDistance, in meters
self._location_listener,
Looper.getMainLooper())
def _stop(self):
self._location_manager.removeUpdates(self._location_listener)
def instance():
return AndroidGPS()

View File

@ -0,0 +1,84 @@
'''
Android gravity
---------------------
'''
from jnius import autoclass
from jnius import cast
from jnius import java_method
from jnius import PythonJavaClass
from plyer.facades import Gravity
from plyer.platforms.android import activity
Context = autoclass('android.content.Context')
Sensor = autoclass('android.hardware.Sensor')
SensorManager = autoclass('android.hardware.SensorManager')
class GravitySensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
service = activity.getSystemService(Context.SENSOR_SERVICE)
self.SensorManager = cast('android.hardware.SensorManager', service)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_GRAVITY
)
self.values = [None, None, None]
def enable(self):
self.SensorManager.registerListener(
self,
self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.values = event.values[:3]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
pass
class AndroidGravity(Gravity):
def __init__(self):
super().__init__()
self.state = False
def _enable(self):
if not self.state:
self.listener = GravitySensorListener()
self.listener.enable()
self.state = True
def _disable(self):
if self.state:
self.state = False
self.listener.disable()
del self.listener
def _get_gravity(self):
if self.state:
return tuple(self.listener.values)
else:
return (None, None, None)
def __del__(self):
if self.state:
self._disable()
super().__del__()
def instance():
return AndroidGravity()

View File

@ -0,0 +1,119 @@
'''
Android Gyroscope
-----------------
'''
from plyer.facades import Gyroscope
from jnius import PythonJavaClass, java_method, autoclass, cast
from plyer.platforms.android import activity
Context = autoclass('android.content.Context')
Sensor = autoclass('android.hardware.Sensor')
SensorManager = autoclass('android.hardware.SensorManager')
class GyroscopeSensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
self.SensorManager = cast(
'android.hardware.SensorManager',
activity.getSystemService(Context.SENSOR_SERVICE)
)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_GYROSCOPE
)
self.values = [None, None, None]
def enable(self):
self.SensorManager.registerListener(
self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.values = event.values[:3]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
# Maybe, do something in future?
pass
class GyroUncalibratedSensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
service = activity.getSystemService(Context.SENSOR_SERVICE)
self.SensorManager = cast('android.hardware.SensorManager', service)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_GYROSCOPE_UNCALIBRATED)
self.values = [None, None, None, None, None, None]
def enable(self):
self.SensorManager.registerListener(
self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.values = event.values[:6]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
pass
class AndroidGyroscope(Gyroscope):
def __init__(self):
super().__init__()
self.bState = False
def _enable(self):
if (not self.bState):
self.listenerg = GyroscopeSensorListener()
self.listenergu = GyroUncalibratedSensorListener()
self.listenerg.enable()
self.listenergu.enable()
self.bState = True
def _disable(self):
if (self.bState):
self.bState = False
self.listenerg.disable()
self.listenergu.disable()
del self.listenerg
del self.listenergu
def _get_orientation(self):
if (self.bState):
return tuple(self.listenerg.values)
else:
return (None, None, None)
def _get_rotation_uncalib(self):
if (self.bState):
return tuple(self.listenergu.values)
else:
return (None, None, None, None, None, None)
def __del__(self):
if(self.bState):
self._disable()
super().__del__()
def instance():
return AndroidGyroscope()

View File

@ -0,0 +1,107 @@
from jnius import autoclass
from jnius import cast
from jnius import java_method
from jnius import PythonJavaClass
from math import exp
from plyer.facades import Humidity
from plyer.platforms.android import activity
ActivityInfo = autoclass('android.content.pm.ActivityInfo')
Context = autoclass('android.content.Context')
Sensor = autoclass('android.hardware.Sensor')
SensorManager = autoclass('android.hardware.SensorManager')
class RelativeHumiditySensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
service = activity.getSystemService(Context.SENSOR_SERVICE)
self.SensorManager = cast('android.hardware.SensorManager', service)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_RELATIVE_HUMIDITY)
self.value = None
def enable(self):
self.SensorManager.registerListener(self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.value = event.values[0]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
pass
class AmbientTemperatureSensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
service = activity.getSystemService(Context.SENSOR_SERVICE)
self.SensorManager = cast('android.hardware.SensorManager', service)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_AMBIENT_TEMPERATURE)
self.value = None
def enable(self):
self.SensorManager.registerListener(self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.value = event.values[0]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
pass
class AndroidHumidity(Humidity):
def __init__(self):
self.state = False
def _get_humidity(self):
if self.state:
m = 17.62
Tn = 243.12
Ta = 216.7
Rh = self.listener_r.value
Tc = self.listener_a.value
A = 6.112
K = 273.15
humidity = (Ta * (Rh / 100) * A * exp(m * Tc / (Tn + Tc))
/ (K + Tc))
return humidity
def _enable(self):
if not self.state:
self.listener_r = RelativeHumiditySensorListener()
self.listener_a = AmbientTemperatureSensorListener()
self.listener_r.enable()
self.listener_a.enable()
self.state = True
def _disable(self):
if self.state:
self.listener_r.disable()
self.listener_a.disable()
self.state = False
delattr(self, 'listener_r')
delattr(self, 'listener_a')
def instance():
return AndroidHumidity()

View File

@ -0,0 +1,55 @@
from jnius import autoclass
from plyer.facades import IrBlaster
from plyer.platforms.android import activity, SDK_INT, ANDROID_VERSION
if SDK_INT >= 19:
Context = autoclass('android.content.Context')
ir_manager = activity.getSystemService(Context.CONSUMER_IR_SERVICE)
else:
ir_manager = None
class AndroidIrBlaster(IrBlaster):
def _exists(self):
if ir_manager and ir_manager.hasIrEmitter():
return True
return False
@property
def multiply_pulse(self):
'''Android 4.4.3+ uses microseconds instead of period counts
'''
return not (SDK_INT == 19
and int(str(ANDROID_VERSION.RELEASE).rsplit('.', 1)[-1])
< 3)
def _get_frequencies(self):
if not ir_manager:
return None
if hasattr(self, '_frequencies'):
return self._frequencies
ir_frequencies = ir_manager.getCarrierFrequencies()
if not ir_frequencies:
return []
frequencies = []
for freqrange in ir_frequencies:
freq = (freqrange.getMinFrequency(), freqrange.getMaxFrequency())
frequencies.append(freq)
self._frequencies = frequencies
return frequencies
def _transmit(self, frequency, pattern, mode):
if self.multiply_pulse and mode == 'period':
pattern = self.periods_to_microseconds(frequency, pattern)
elif not self.multiply_pulse and mode == 'microseconds':
pattern = self.microseconds_to_periods(frequency, pattern)
ir_manager.transmit(frequency, pattern)
def instance():
return AndroidIrBlaster()

View File

@ -0,0 +1,25 @@
from plyer.facades import Keystore
from plyer.platforms.android import activity
class AndroidKeystore(Keystore):
def _set_key(self, servicename, key, value, **kwargs):
mode = kwargs.get("mode", 0)
settings = activity.getSharedPreferences(servicename, mode)
editor = settings.edit()
editor.putString(key, value)
editor.commit()
def _get_key(self, servicename, key, **kwargs):
mode = kwargs.get("mode", 0)
default = kwargs.get("default", "__None")
settings = activity.getSharedPreferences(servicename, mode)
ret = settings.getString(key, default)
if ret == "__None":
ret = None
return ret
def instance():
return AndroidKeystore()

View File

@ -0,0 +1,63 @@
from jnius import autoclass
from jnius import cast
from jnius import java_method
from jnius import PythonJavaClass
from plyer.facades import Light
from plyer.platforms.android import activity
Context = autoclass('android.content.Context')
Sensor = autoclass('android.hardware.Sensor')
SensorManager = autoclass('android.hardware.SensorManager')
class LightSensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
service = activity.getSystemService(Context.SENSOR_SERVICE)
self.SensorManager = cast('android.hardware.SensorManager', service)
self.sensor = self.SensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
self.value = None
def enable(self):
self.SensorManager.registerListener(
self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.value = event.values[0]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
pass
class AndroidLight(Light):
listener = None
def _get_illumination(self):
if self.listener and self.listener.value:
light = self.listener.value
return light
def _enable(self):
if not self.listener:
self.listener = LightSensorListener()
self.listener.enable()
def _disable(self):
if self.listener:
self.listener.disable()
delattr(self, 'listener')
def instance():
return AndroidLight()

View File

@ -0,0 +1,208 @@
'''
Module of Android API for plyer.notification.
.. versionadded:: 1.0.0
.. versionchanged:: 1.4.0
Fixed notifications not displaying due to missing NotificationChannel
required by Android Oreo 8.0+ (API 26+).
.. versionchanged:: 1.4.0
Added simple toaster notification.
.. versionchanged:: 1.4.0
Fixed notifications not displaying big icons properly.
Added option for custom big icon via `icon`.
'''
from android import python_act
from android.runnable import run_on_ui_thread
from jnius import autoclass, cast
from plyer.facades import Notification
from plyer.platforms.android import activity, SDK_INT
AndroidString = autoclass('java.lang.String')
Context = autoclass('android.content.Context')
NotificationBuilder = autoclass('android.app.Notification$Builder')
NotificationManager = autoclass('android.app.NotificationManager')
PendingIntent = autoclass('android.app.PendingIntent')
Intent = autoclass('android.content.Intent')
Toast = autoclass('android.widget.Toast')
BitmapFactory = autoclass('android.graphics.BitmapFactory')
Icon = autoclass("android.graphics.drawable.Icon")
class AndroidNotification(Notification):
'''
Implementation of Android notification API.
.. versionadded:: 1.0.0
'''
def __init__(self):
package_name = activity.getPackageName()
self._ns = None
self._channel_id = package_name
pm = activity.getPackageManager()
info = pm.getActivityInfo(activity.getComponentName(), 0)
if info.icon == 0:
# Take the application icon instead.
info = pm.getApplicationInfo(package_name, 0)
self._app_icon = info.icon
def _get_notification_service(self):
if not self._ns:
self._ns = cast(NotificationManager, activity.getSystemService(
Context.NOTIFICATION_SERVICE
))
return self._ns
def _build_notification_channel(self, name):
'''
Create a NotificationChannel using channel id of the application
package name (com.xyz, org.xyz, ...) and channel name same as the
provided notification title if the API is high enough, otherwise
do nothing.
.. versionadded:: 1.4.0
'''
if SDK_INT < 26:
return
channel = autoclass('android.app.NotificationChannel')
app_channel = channel(
self._channel_id, name, NotificationManager.IMPORTANCE_DEFAULT
)
self._get_notification_service().createNotificationChannel(
app_channel
)
return app_channel
@run_on_ui_thread
def _toast(self, message):
'''
Display a popup-like small notification at the bottom of the screen.
.. versionadded:: 1.4.0
'''
Toast.makeText(
activity,
cast('java.lang.CharSequence', AndroidString(message)),
Toast.LENGTH_LONG
).show()
def _set_icons(self, notification, icon=None, notification_icon=None):
'''
Set the small application icon displayed at the top panel together with
WiFi, battery percentage and time and the big optional icon (preferably
PNG format with transparent parts) displayed directly in the
notification body.
.. versionadded:: 1.4.0
'''
if notification_icon == None:
app_icon = self._app_icon
else:
notification_icon_bitmap = BitmapFactory.decodeFile(notification_icon)
app_icon = Icon.createWithBitmap(notification_icon_bitmap)
notification.setSmallIcon(app_icon)
bitmap_icon = app_icon
if icon is not None:
bitmap_icon = BitmapFactory.decodeFile(icon)
notification.setLargeIcon(bitmap_icon)
elif icon == '':
# we don't want the big icon set,
# only the small one in the top panel
pass
else:
bitmap_icon = BitmapFactory.decodeResource(
python_act.getResources(), app_icon
)
notification.setLargeIcon(bitmap_icon)
def _build_notification(self, title):
'''
.. versionadded:: 1.4.0
'''
if SDK_INT < 26:
noti = NotificationBuilder(activity)
else:
self._channel = self._build_notification_channel(title)
noti = NotificationBuilder(activity, self._channel_id)
return noti
@staticmethod
def _set_open_behavior(notification):
'''
Open the source application when user opens the notification.
.. versionadded:: 1.4.0
'''
# create Intent that navigates back to the application
app_context = activity.getApplication().getApplicationContext()
notification_intent = Intent(app_context, python_act)
# set flags to run our application Activity
notification_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
notification_intent.setAction(Intent.ACTION_MAIN)
notification_intent.addCategory(Intent.CATEGORY_LAUNCHER)
# get our application Activity
pending_intent = PendingIntent.getActivity(
app_context, 0, notification_intent, 0
)
notification.setContentIntent(pending_intent)
notification.setAutoCancel(True)
def _open_notification(self, notification):
if SDK_INT >= 16:
notification = notification.build()
else:
notification = notification.getNotification()
self._get_notification_service().notify(0, notification)
def _notify(self, **kwargs):
noti = None
message = kwargs.get('message').encode('utf-8')
ticker = kwargs.get('ticker').encode('utf-8')
title = AndroidString(
kwargs.get('title', '').encode('utf-8')
)
icon = kwargs.get('app_icon')
notification_icon = kwargs.get('notification_icon')
# decide whether toast only or proper notification
if kwargs.get('toast'):
self._toast(message)
return
else:
noti = self._build_notification(title)
# set basic properties for notification
noti.setContentTitle(title)
noti.setContentText(AndroidString(message))
noti.setTicker(AndroidString(ticker))
# set additional flags for notification
self._set_icons(noti, icon=icon, notification_icon=notification_icon)
self._set_open_behavior(noti)
# launch
self._open_notification(noti)
def instance():
'''
Instance for facade proxy.
'''
return AndroidNotification()

View File

@ -0,0 +1,43 @@
from jnius import autoclass
from plyer.platforms.android import activity
from plyer.facades import Orientation
ActivityInfo = autoclass('android.content.pm.ActivityInfo')
class AndroidOrientation(Orientation):
def _set_landscape(self, **kwargs):
reverse = kwargs.get('reverse')
if reverse:
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE)
else:
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
def _set_portrait(self, **kwargs):
reverse = kwargs.get('reverse')
if reverse:
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT)
else:
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
def _set_sensor(self, **kwargs):
mode = kwargs.get('mode')
if mode == 'any':
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_SENSOR)
elif mode == 'landscape':
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE)
elif mode == 'portrait':
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT)
def instance():
return AndroidOrientation()

View File

@ -0,0 +1,69 @@
from jnius import autoclass
from jnius import cast
from jnius import java_method
from jnius import PythonJavaClass
from plyer.platforms.android import activity
from plyer.facades import Proximity
ActivityInfo = autoclass('android.content.pm.ActivityInfo')
Context = autoclass('android.content.Context')
Sensor = autoclass('android.hardware.Sensor')
SensorManager = autoclass('android.hardware.SensorManager')
class ProximitySensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
service = activity.getSystemService(Context.SENSOR_SERVICE)
self.SensorManager = cast('android.hardware.SensorManager', service)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_PROXIMITY)
self.value = None
def enable(self):
self.SensorManager.registerListener(
self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.value = event.values[0]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
pass
class AndroidProximity(Proximity):
listener = None
def _enable(self, **kwargs):
if not self.listener:
self.listener = ProximitySensorListener()
self.listener.enable()
def _disable(self, **kwargs):
if self.listener:
self.listener.disable()
delattr(self, 'listener')
def _get_proximity(self):
if self.listener:
value = self.listener.value
# value is 0.0 when proxime sensor is covered. In other case
# value is 5.0 because in smartphone, optical proximity sensors
# are used.
return value < 5.0
def instance():
return AndroidProximity()

View File

@ -0,0 +1,25 @@
'''
Android SMS
-----------
'''
from jnius import autoclass
from plyer.facades import Sms
SmsManager = autoclass('android.telephony.SmsManager')
class AndroidSms(Sms):
def _send(self, **kwargs):
sms = SmsManager.getDefault()
recipient = kwargs.get('recipient')
message = kwargs.get('message')
if sms:
sms.sendTextMessage(recipient, None, message, None, None)
def instance():
return AndroidSms()

View File

@ -0,0 +1,118 @@
from jnius import autoclass
from jnius import cast
from jnius import java_method
from jnius import PythonJavaClass
from plyer.platforms.android import activity
from plyer.facades import SpatialOrientation
Context = autoclass('android.content.Context')
Sensor = autoclass('android.hardware.Sensor')
SensorManager = autoclass('android.hardware.SensorManager')
class AccelerometerSensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
self.SensorManager = cast(
'android.hardware.SensorManager',
activity.getSystemService(Context.SENSOR_SERVICE)
)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_ACCELEROMETER
)
self.values = [None, None, None]
def enable(self):
self.SensorManager.registerListener(
self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.values = event.values[:3]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
pass
class MagnetometerSensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
service = activity.getSystemService(Context.SENSOR_SERVICE)
self.SensorManager = cast('android.hardware.SensorManager', service)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_MAGNETIC_FIELD)
self.values = [None, None, None]
def enable(self):
self.SensorManager.registerListener(
self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.values = event.values[:3]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
pass
class AndroidSpOrientation(SpatialOrientation):
def __init__(self):
self.state = False
def _get_orientation(self):
if self.state:
rotation = [0] * 9
inclination = [0] * 9
gravity = []
geomagnetic = []
gravity = self.listener_a.values
geomagnetic = self.listener_m.values
if gravity[0] is not None and geomagnetic[0] is not None:
ff_state = SensorManager.getRotationMatrix(
rotation, inclination,
gravity, geomagnetic
)
if ff_state:
values = [0, 0, 0]
values = SensorManager.getOrientation(
rotation, values
)
return values
def _enable_listener(self, **kwargs):
if not self.state:
self.listener_a = AccelerometerSensorListener()
self.listener_m = MagnetometerSensorListener()
self.listener_a.enable()
self.listener_m.enable()
self.state = True
def _disable_listener(self, **kwargs):
if self.state:
self.listener_a.disable()
self.listener_m.disable()
self.state = False
delattr(self, 'listener_a')
delattr(self, 'listener_m')
def instance():
return AndroidSpOrientation()

View File

@ -0,0 +1,69 @@
'''
Android Storage Path
--------------------
'''
from os import listdir, access, R_OK
from os.path import join
from plyer.facades import StoragePath
from jnius import autoclass
from android import mActivity
Environment = autoclass('android.os.Environment')
Context = autoclass('android.content.Context')
class AndroidStoragePath(StoragePath):
def _get_home_dir(self):
return Environment.getDataDirectory().getAbsolutePath()
def _get_external_storage_dir(self):
return Environment.getExternalStorageDirectory().getAbsolutePath()
def _get_sdcard_dir(self):
'''
.. versionadded:: 1.4.0
'''
# folder in /storage/ that is readable
# and is not internal SD card
path = None
for folder in listdir('/storage'):
folder = join('/storage', folder)
if folder in self._get_external_storage_dir():
continue
if not access(folder, R_OK):
continue
path = folder
break
return path
def _get_root_dir(self):
return Environment.getRootDirectory().getAbsolutePath()
def _get_documents_dir(self):
return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS).getAbsolutePath()
def _get_downloads_dir(self):
return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()
def _get_videos_dir(self):
return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES).getAbsolutePath()
def _get_music_dir(self):
return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MUSIC).getAbsolutePath()
def _get_pictures_dir(self):
return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).getAbsolutePath()
def _get_application_dir(self):
return mActivity.getFilesDir().getParentFile().getParent()
def instance():
return AndroidStoragePath()

View File

@ -0,0 +1,252 @@
from android.runnable import run_on_ui_thread
from jnius import autoclass
from jnius import java_method
from jnius import PythonJavaClass
from plyer.facades import STT
from plyer.platforms.android import activity
ArrayList = autoclass('java.util.ArrayList')
Bundle = autoclass('android.os.Bundle')
Context = autoclass('android.content.Context')
Intent = autoclass('android.content.Intent')
RecognizerIntent = autoclass('android.speech.RecognizerIntent')
RecognitionListener = autoclass('android.speech.RecognitionListener')
SpeechRecognizer = autoclass('android.speech.SpeechRecognizer')
SpeechResults = SpeechRecognizer.RESULTS_RECOGNITION
class SpeechListener(PythonJavaClass):
__javainterfaces__ = ['android/speech/RecognitionListener']
# class variables because PythonJavaClass class failed
# to see them later in getters and setters
_error_callback = None
_result_callback = None
_partial_result_callback = None
_volume_callback = None
def __init__(self):
super().__init__()
# overwrite class variables in the object
self._error_callback = None
self._result_callback = None
self._partial_result_callback = None
self._volume_callback = None
# error handling
@property
def error_callback(self):
return self._error_callback
@error_callback.setter
def error_callback(self, callback):
'''
Set error callback. It is called when error occurs.
:param callback: function with one parameter for error message
'''
self._error_callback = callback
# result handling
@property
def result_callback(self):
return self._result_callback
@result_callback.setter
def result_callback(self, callback):
'''
Set result callback. It is called when results are received.
:param callback: function with one parameter for lists of strings
'''
self._result_callback = callback
@property
def partial_result_callback(self):
return self._partial_result_callback
@partial_result_callback.setter
def partial_result_callback(self, callback):
'''
Set partial result callback. It is called when partial results are
received while the listener is still in listening mode.
:param callback: function with one parameter for lists of strings
'''
self._partial_result_callback = callback
# voice changes handling
@property
def volume_callback(self):
return self._volume_callback
@volume_callback.setter
def volume_callback(self, callback):
'''
Set volume voice callback.
It is called when loudness of the voice changes.
:param callback: function with one parameter for volume RMS dB (float).
'''
self._volume_callback = callback
# Implementation Java Interfaces
@java_method('()V')
def onBeginningOfSpeech(self):
pass
@java_method('([B)V')
def onBufferReceived(self, buffer):
pass
@java_method('()V')
def onEndOfSpeech(self):
pass
@java_method('(I)V')
def onError(self, error):
msg = ''
if error == SpeechRecognizer.ERROR_AUDIO:
msg = 'audio'
if error == SpeechRecognizer.ERROR_CLIENT:
msg = 'client'
if error == SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
msg = 'insufficient_permissions'
if error == SpeechRecognizer.ERROR_NETWORK:
msg = 'network'
if error == SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
msg = 'network_timeout'
if error == SpeechRecognizer.ERROR_NO_MATCH:
msg = 'no_match'
if error == SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
msg = 'recognizer_busy'
if error == SpeechRecognizer.ERROR_SERVER:
msg = 'server'
if error == SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
msg = 'speech_timeout'
if msg and self.error_callback:
self.error_callback('error:' + msg)
@java_method('(ILandroid/os/Bundle;)V')
def onEvent(self, event_type, params):
pass
@java_method('(Landroid/os/Bundle;)V')
def onPartialResults(self, results):
texts = []
matches = results.getStringArrayList(SpeechResults)
for match in matches.toArray():
if isinstance(match, bytes):
match = match.decode('utf-8')
texts.append(match)
if texts and self.partial_result_callback:
self.partial_result_callback(texts)
@java_method('(Landroid/os/Bundle;)V')
def onReadyForSpeech(self, params):
pass
@java_method('(Landroid/os/Bundle;)V')
def onResults(self, results):
texts = []
matches = results.getStringArrayList(SpeechResults)
for match in matches.toArray():
if isinstance(match, bytes):
match = match.decode('utf-8')
texts.append(match)
if texts and self.result_callback:
self.result_callback(texts)
@java_method('(F)V')
def onRmsChanged(self, rmsdB):
if self.volume_callback:
self.volume_callback(rmsdB)
class AndroidSpeech(STT):
'''
Android Speech Implementation.
Android class `SpeechRecognizer`'s listening deactivates automatically.
Class methods `_on_error()`, `_on_result()` listeners. You can find
documentation here:
https://developer.android.com/reference/android/speech/RecognitionListener
'''
def _on_error(self, msg):
self.errors.append(msg)
self.stop()
def _on_result(self, messages):
self.results.extend(messages)
self.stop()
def _on_partial(self, messages):
self.partial_results.extend(messages)
@run_on_ui_thread
def _start(self):
intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
intent.putExtra(
RecognizerIntent.EXTRA_CALLING_PACKAGE,
activity.getPackageName()
)
# language preferences
intent.putExtra(
RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, self.language
)
intent.putExtra(
RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH
)
# results settings
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1000)
intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, True)
if self.prefer_offline:
intent.putExtra(RecognizerIntent.EXTRA_PREFER_OFFLINE, True)
# listener and callbacks
listener = SpeechListener()
listener.error_callback = self._on_error
listener.result_callback = self._on_result
listener.partial_result_callback = self._on_partial
# create recognizer and start
self.speech = SpeechRecognizer.createSpeechRecognizer(activity)
self.speech.setRecognitionListener(listener)
self.speech.startListening(intent)
@run_on_ui_thread
def _stop(self):
if not self.speech:
return
# stop listening
self.speech.stopListening()
# free object
self.speech.destroy()
self.speech = None
def _exist(self):
return bool(
SpeechRecognizer.isRecognitionAvailable(activity)
)
def instance():
return AndroidSpeech()

View File

@ -0,0 +1,66 @@
from jnius import autoclass
from jnius import cast
from jnius import java_method
from jnius import PythonJavaClass
from plyer.facades import Temperature
from plyer.platforms.android import activity
ActivityInfo = autoclass('android.content.pm.ActivityInfo')
Context = autoclass('android.content.Context')
Sensor = autoclass('android.hardware.Sensor')
SensorManager = autoclass('android.hardware.SensorManager')
class TemperatureSensorListener(PythonJavaClass):
__javainterfaces__ = ['android/hardware/SensorEventListener']
def __init__(self):
super().__init__()
service = activity.getSystemService(Context.SENSOR_SERVICE)
self.SensorManager = cast('android.hardware.SensorManager', service)
self.sensor = self.SensorManager.getDefaultSensor(
Sensor.TYPE_AMBIENT_TEMPERATURE)
self.value = None
def enable(self):
self.SensorManager.registerListener(
self, self.sensor,
SensorManager.SENSOR_DELAY_NORMAL
)
def disable(self):
self.SensorManager.unregisterListener(self, self.sensor)
@java_method('(Landroid/hardware/SensorEvent;)V')
def onSensorChanged(self, event):
self.value = event.values[0]
@java_method('(Landroid/hardware/Sensor;I)V')
def onAccuracyChanged(self, sensor, accuracy):
pass
class AndroidTemperature(Temperature):
listener = None
def _get_temperature(self):
if self.listener and self.listener.value:
temperature = self.listener.value
return temperature
def _enable(self):
if not self.listener:
self.listener = TemperatureSensorListener()
self.listener.enable()
def _disable(self):
if self.listener:
self.listener.disable()
delattr(self, 'listener')
def instance():
return AndroidTemperature()

View File

@ -0,0 +1,34 @@
from time import sleep
from jnius import autoclass
from plyer.facades import TTS
from plyer.platforms.android import activity
Locale = autoclass('java.util.Locale')
TextToSpeech = autoclass('android.speech.tts.TextToSpeech')
class AndroidTextToSpeech(TTS):
def _speak(self, **kwargs):
tts = TextToSpeech(activity, None)
tts.setLanguage(Locale.US)
retries = 0 # First try rarely succeeds due to some timing issue
message = kwargs.get('message')
# first try for while loop
speak_status = tts.speak(
message, TextToSpeech.QUEUE_FLUSH, None
)
# -1 indicates error. Let's wait and then try again
while retries < 100 and speak_status == -1:
sleep(0.1)
retries += 1
speak_status = tts.speak(
message, TextToSpeech.QUEUE_FLUSH, None
)
def instance():
return AndroidTextToSpeech()

View File

@ -0,0 +1,28 @@
'''
Module of Android API for plyer.uniqueid.
'''
from jnius import autoclass
from plyer.platforms.android import activity
from plyer.facades import UniqueID
Secure = autoclass('android.provider.Settings$Secure')
class AndroidUniqueID(UniqueID):
'''
Implementation of Android uniqueid API.
'''
def _get_uid(self):
return Secure.getString(
activity.getContentResolver(),
Secure.ANDROID_ID
)
def instance():
'''
Instance for facade proxy.
'''
return AndroidUniqueID()

View File

@ -0,0 +1,63 @@
"""Implementation Vibrator for Android."""
from jnius import autoclass, cast
from plyer.facades import Vibrator
from plyer.platforms.android import activity
from plyer.platforms.android import SDK_INT
Context = autoclass("android.content.Context")
vibrator_service = activity.getSystemService(Context.VIBRATOR_SERVICE)
vibrator = cast("android.os.Vibrator", vibrator_service)
if SDK_INT >= 26:
VibrationEffect = autoclass("android.os.VibrationEffect")
class AndroidVibrator(Vibrator):
"""Android Vibrator class.
Supported features:
* vibrate for some period of time.
* vibrate from given pattern.
* cancel vibration.
* check whether Vibrator exists.
"""
def _vibrate(self, time=None, **kwargs):
if vibrator:
if SDK_INT >= 26:
vibrator.vibrate(
VibrationEffect.createOneShot(
int(1000 * time), VibrationEffect.DEFAULT_AMPLITUDE
)
)
else:
vibrator.vibrate(int(1000 * time))
def _pattern(self, pattern=None, repeat=None, **kwargs):
pattern = [int(1000 * time) for time in pattern]
if vibrator:
if SDK_INT >= 26:
vibrator.vibrate(
VibrationEffect.createWaveform(pattern, repeat)
)
else:
vibrator.vibrate(pattern, repeat)
def _exists(self, **kwargs):
if SDK_INT >= 11:
return vibrator.hasVibrator()
elif vibrator_service is None:
raise NotImplementedError()
return True
def _cancel(self, **kwargs):
vibrator.cancel()
def instance():
"""Returns Vibrator with android features.
:return: instance of class AndroidVibrator
"""
return AndroidVibrator()

View File

View File

@ -0,0 +1,34 @@
'''
iOS accelerometer
-----------------
Taken from: http://pyobjus.readthedocs.org/en/latest/pyobjus_ios.html \
#accessing-accelerometer
'''
from plyer.facades import Accelerometer
from pyobjus import autoclass
class IosAccelerometer(Accelerometer):
def __init__(self):
super().__init__()
self.bridge = autoclass('bridge').alloc().init()
self.bridge.motionManager.setAccelerometerUpdateInterval_(0.1)
def _enable(self):
self.bridge.startAccelerometer()
def _disable(self):
self.bridge.stopAccelerometer()
def _get_acceleration(self):
return (
self.bridge.ac_x,
self.bridge.ac_y,
self.bridge.ac_z)
def instance():
return IosAccelerometer()

View File

@ -0,0 +1,31 @@
'''
iOS Barometer
-------------
'''
from plyer.facades import Barometer
from pyobjus import autoclass
class iOSBarometer(Barometer):
def __init__(self):
super().__init__()
self.bridge = autoclass('bridge').alloc().init()
def _enable(self):
self.bridge.startRelativeAltitude()
def _disable(self):
self.bridge.stopRelativeAltitude()
def _get_pressure(self):
'''
1 kPa = 10 hPa
'''
return (
self.bridge.pressure * 10)
def instance():
return iOSBarometer()

View File

@ -0,0 +1,47 @@
'''
Module of iOS API for plyer.battery.
'''
from pyobjus import autoclass
from pyobjus.dylib_manager import load_framework
from plyer.facades import Battery
load_framework('/System/Library/Frameworks/UIKit.framework')
UIDevice = autoclass('UIDevice')
class IOSBattery(Battery):
'''
Implementation of iOS battery API.
'''
def __init__(self):
super().__init__()
self.device = UIDevice.currentDevice()
def _get_state(self):
status = {"isCharging": None, "percentage": None}
if not self.device.batteryMonitoringEnabled:
self.device.setBatteryMonitoringEnabled_(True)
if self.device.batteryState == 0:
is_charging = None
elif self.device.batteryState == 2:
is_charging = True
else:
is_charging = False
percentage = self.device.batteryLevel * 100.
status['isCharging'] = is_charging
status['percentage'] = percentage
return status
def instance():
'''
Instance for facade proxy.
'''
return IOSBattery()

View File

@ -0,0 +1,27 @@
'''
iOS Brightness
--------------
'''
from pyobjus import autoclass
from plyer.facades import Brightness
from pyobjus.dylib_manager import load_framework
load_framework('/System/Library/Frameworks/UIKit.framework')
UIScreen = autoclass('UIScreen')
class iOSBrightness(Brightness):
def __init__(self):
self.screen = UIScreen.mainScreen()
def _current_level(self):
return self.screen.brightness * 100
def set_level(self, level):
self.screen.brightness = level / 100
def instance():
return iOSBrightness()

View File

@ -0,0 +1,29 @@
'''
IOS Call
----------
'''
from plyer.facades import Call
from pyobjus import autoclass, objc_str
NSURL = autoclass('NSURL')
NSString = autoclass('NSString')
UIApplication = autoclass('UIApplication')
class IOSCall(Call):
def _makecall(self, **kwargs):
tel = kwargs.get('tel')
url = "tel://" + tel
nsurl = NSURL.alloc().initWithString_(objc_str(url))
UIApplication.sharedApplication().openURL_(nsurl)
def _dialcall(self, **kwargs):
pass
# Not possible, Access not provided by iPhone SDK
def instance():
return IOSCall()

View File

@ -0,0 +1,52 @@
from os import remove
from plyer.facades import Camera
from plyer.utils import reify
class iOSCamera(Camera):
@reify
def photos(self):
# pyPhotoLibrary is a ios recipe/module that
# interacts with the gallery and the camera on ios.
from photolibrary import PhotosLibrary
return PhotosLibrary()
def _take_picture(self, on_complete, filename=None):
assert(on_complete is not None)
self.on_complete = on_complete
self.filename = filename
photos = self.photos
if not photos.isCameraAvailable():
# no camera hardware
return False
photos.bind(on_image_captured=self.capture_callback)
self._capture_filename = filename
photos.capture_image(filename)
return True
def capture_callback(self, photolibrary):
# Image was chosen
# unbind
self.photos.unbind(on_image_captured=self.capture_callback)
if self.on_complete(self.filename):
self._remove(self.filename)
def _take_video(self, on_complete, filename=None):
assert(on_complete is not None)
raise NotImplementedError
def _remove(self, fn):
try:
remove(fn)
except OSError:
print('Could not remove photo!')
def instance():
return iOSCamera()

View File

@ -0,0 +1,43 @@
'''
iOS Compass
-----------
'''
from plyer.facades import Compass
from pyobjus import autoclass
class IosCompass(Compass):
def __init__(self):
super().__init__()
self.bridge = autoclass('bridge').alloc().init()
self.bridge.motionManager.setMagnetometerUpdateInterval_(0.1)
self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1)
def _enable(self):
self.bridge.startMagnetometer()
self.bridge.startDeviceMotionWithReferenceFrame()
def _disable(self):
self.bridge.stopMagnetometer()
self.bridge.stopDeviceMotion()
def _get_orientation(self):
return (
self.bridge.mf_x,
self.bridge.mf_y,
self.bridge.mf_z)
def _get_field_uncalib(self):
return (
self.bridge.mg_x,
self.bridge.mg_y,
self.bridge.mg_z,
self.bridge.mg_x - self.bridge.mf_x,
self.bridge.mg_y - self.bridge.mf_y,
self.bridge.mg_z - self.bridge.mf_z)
def instance():
return IosCompass()

View File

@ -0,0 +1,52 @@
'''
Module of iOS API for plyer.email.
'''
try:
from urllib.parse import quote
except ImportError:
from urllib import quote
from plyer.facades import Email
from pyobjus import autoclass, objc_str
from pyobjus.dylib_manager import load_framework
load_framework('/System/Library/Frameworks/UIKit.framework')
NSURL = autoclass('NSURL')
NSString = autoclass('NSString')
UIApplication = autoclass('UIApplication')
class IOSEmail(Email):
'''
Implementation of iOS battery API.
'''
def _send(self, **kwargs):
recipient = kwargs.get('recipient')
subject = kwargs.get('subject')
text = kwargs.get('text')
uri = "mailto:"
if recipient:
uri += str(recipient)
if subject:
uri += "?" if "?" not in uri else "&"
uri += "subject="
uri += quote(str(subject))
if text:
uri += "?" if "?" not in uri else "&"
uri += "body="
uri += quote(str(text))
nsurl = NSURL.alloc().initWithString_(objc_str(uri))
UIApplication.sharedApplication().openURL_(nsurl)
def instance():
'''
Instance for facade proxy.
'''
return IOSEmail()

View File

@ -0,0 +1,81 @@
'''
IOS file chooser
--------------------
This module houses the iOS implementation of the plyer FileChooser.
.. versionadded:: 1.4.4
'''
from plyer.facades import FileChooser
from pyobjus import autoclass, protocol
from pyobjus.dylib_manager import load_framework
load_framework('/System/Library/Frameworks/Photos.framework')
class IOSFileChooser(FileChooser):
'''
FileChooser implementation for IOS using
the built-in file browser via UIImagePickerController.
.. versionadded:: 1.4.0
'''
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._on_selection = None
def _file_selection_dialog(self, *args, **kwargs):
"""
Function called when action is required, A "mode" parameter specifies
which and is one of "open", "save" or "dir".
"""
self._on_selection = kwargs["on_selection"]
if kwargs["mode"] == "open":
self._open()
else:
raise NotImplementedError()
def _get_picker(self):
"""
Return an instantiated and configured UIImagePickerController.
"""
picker = autoclass("UIImagePickerController")
po = picker.alloc().init()
po.sourceType = 0
po.delegate = self
return po
def _open(self):
"""
Launch the native iOS file browser. Upon selection, the
`imagePickerController_didFinishPickingMediaWithInfo_` delegate is
called where we close the file browser and handle the result.
"""
picker = self._get_picker()
UIApplication = autoclass('UIApplication')
vc = UIApplication.sharedApplication().keyWindow.rootViewController()
vc.presentViewController_animated_completion_(picker, True, None)
@protocol('UIImagePickerControllerDelegate')
def imagePickerController_didFinishPickingMediaWithInfo_(
self, image_picker, frozen_dict):
"""
Delegate which handles the result of the image selection process.
"""
image_picker.dismissViewControllerAnimated_completion_(True, None)
# Note: We need to call this Objective C class as there is currently
# no way to call a non-class function via pyobjus. And here,
# we have to use the `UIImagePNGRepresentation` to get the png
# representation. For this, please ensure you are using an
# appropriate version of kivy-ios.
native_image_picker = autoclass("NativeImagePicker").alloc().init()
path = native_image_picker.writeToPNG_(frozen_dict)
self._on_selection([path.UTF8String()])
def instance():
return IOSFileChooser()

View File

@ -0,0 +1,50 @@
# coding=utf-8
"""
Flash
-----
"""
from plyer.facades import Flash
from pyobjus import autoclass
NSString = autoclass("NSString")
AVCaptureDevice = autoclass("AVCaptureDevice")
AVMediaTypeVideo = NSString.alloc().initWithUTF8String_("vide")
AVCaptureTorchModeOff = 0
AVCaptureTorchModeOn = 1
class IosFlash(Flash):
_camera = None
def _on(self):
if self._camera is None:
self._camera_open()
if not self._camera:
return
self._camera.lockForConfiguration_(None)
try:
self._camera.setTorchMode(AVCaptureTorchModeOn)
finally:
self._camera.unlockForConfiguration()
def _off(self):
if not self._camera:
return
self._camera.lockForConfiguration_(None)
try:
self._camera.setTorchMode(AVCaptureTorchModeOff)
finally:
self._camera.unlockForConfiguration()
def _release(self):
pass
def _camera_open(self):
device = AVCaptureDevice.defaultDeviceWithMediaType_(AVMediaTypeVideo)
if not device:
return
self._camera = device
def instance():
return IosFlash()

View File

@ -0,0 +1,80 @@
'''
iOS GPS
-----------
'''
from pyobjus import autoclass, protocol
from pyobjus.dylib_manager import load_framework
from plyer.facades import GPS
load_framework('/System/Library/Frameworks/CoreLocation.framework')
CLLocationManager = autoclass('CLLocationManager')
class IosGPS(GPS):
def _configure(self):
if not hasattr(self, '_location_manager'):
self._location_manager = CLLocationManager.alloc().init()
def _start(self, **kwargs):
self._location_manager.delegate = self
self._location_manager.requestWhenInUseAuthorization()
# NSLocationWhenInUseUsageDescription key must exist in Info.plist
# file. When the authorization prompt is displayed your app goes
# into pause mode and if your app doesn't support background mode
# it will crash.
self._location_manager.startUpdatingLocation()
def _stop(self):
self._location_manager.stopUpdatingLocation()
@protocol('CLLocationManagerDelegate')
def locationManager_didChangeAuthorizationStatus_(self, manager, status):
if self.on_status:
s_status = ''
provider_status = ''
provider = 'standard-ios-provider'
if status == 0:
provider_status = 'provider-disabled'
s_status = 'notDetermined'
elif status == 1:
provider_status = 'provider-enabled'
s_status = 'restricted'
elif status == 2:
provider_status = 'provider-disabled'
s_status = 'denied'
elif status == 3:
provider_status = 'provider-enabled'
s_status = 'authorizedAlways'
elif status == 4:
provider_status = 'provider-enabled'
s_status = 'authorizedWhenInUse'
self.on_status(provider_status, '{}: {}'.format(
provider, s_status))
@protocol('CLLocationManagerDelegate')
def locationManager_didUpdateLocations_(self, manager, locations):
location = manager.location
description = location.description.UTF8String()
split_description = description.split('<')[-1].split('>')[0].split(',')
lat, lon = [float(coord) for coord in split_description]
acc = float(description.split(' +/- ')[-1].split('m ')[0])
speed = location.speed
altitude = location.altitude
course = location.course
self.on_location(
lat=lat,
lon=lon,
speed=speed,
bearing=course,
altitude=altitude,
accuracy=acc)
def instance():
return IosGPS()

View File

@ -0,0 +1,31 @@
'''
iOS Gravity
-----------
'''
from plyer.facades import Gravity
from pyobjus import autoclass
class iOSGravity(Gravity):
def __init__(self):
self.bridge = autoclass('bridge').alloc().init()
self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1)
def _enable(self):
self.bridge.startDeviceMotion()
def _disable(self):
self.bridge.stopDeviceMotion()
def _get_gravity(self):
return (
self.bridge.g_x,
self.bridge.g_y,
self.bridge.g_z)
def instance():
return iOSGravity()

View File

@ -0,0 +1,55 @@
'''
iOS Gyroscope
---------------------
'''
from plyer.facades import Gyroscope
from pyobjus import autoclass
from pyobjus.dylib_manager import load_framework
load_framework('/System/Library/Frameworks/UIKit.framework')
UIDevice = autoclass('UIDevice')
device = UIDevice.currentDevice()
class IosGyroscope(Gyroscope):
def __init__(self):
super().__init__()
self.bridge = autoclass('bridge').alloc().init()
if int(device.systemVersion.UTF8String().split('.')[0]) <= 4:
self.bridge.motionManager.setGyroscopeUpdateInterval_(0.1)
else:
self.bridge.motionManager.setGyroUpdateInterval_(0.1)
self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1)
def _enable(self):
self.bridge.startGyroscope()
self.bridge.startDeviceMotion()
def _disable(self):
self.bridge.stopGyroscope()
self.bridge.stopDeviceMotion()
def _get_orientation(self):
return (
self.bridge.rotation_rate_x,
self.bridge.rotation_rate_y,
self.bridge.rotation_rate_z)
def _get_rotation_uncalib(self):
return (
self.bridge.gy_x,
self.bridge.gy_y,
self.bridge.gy_z,
self.bridge.gy_x - self.bridge.rotation_rate_x,
self.bridge.gy_y - self.bridge.rotation_rate_y,
self.bridge.gy_z - self.bridge.rotation_rate_z)
def instance():
return IosGyroscope()

View File

@ -0,0 +1,23 @@
from plyer.facades import Keystore
from pyobjus import autoclass, objc_str
NSUserDefaults = autoclass('NSUserDefaults')
class IosKeystore(Keystore):
def _set_key(self, servicename, key, value, **kwargs):
NSUserDefaults.standardUserDefaults().setObject_forKey_(
objc_str(value), objc_str(key))
def _get_key(self, servicename, key, **kwargs):
ret = NSUserDefaults.standardUserDefaults().stringForKey_(
objc_str(key))
if ret is not None:
return ret.UTF8String()
else:
return ret
def instance():
return IosKeystore()

View File

@ -0,0 +1,43 @@
'''
IOS Sms
----------
'''
from plyer.facades import Sms
from pyobjus import autoclass, objc_str
from pyobjus.dylib_manager import load_framework
NSURL = autoclass('NSURL')
NSString = autoclass('NSString')
UIApplication = autoclass('UIApplication')
load_framework('/System/Library/Frameworks/MessageUI.framework')
class IOSSms(Sms):
def _send(self, **kwargs):
'''
This method provides sending messages to recipients.
Expects 2 parameters in kwargs:
- recipient: String type
- message: String type
Opens a message interface with recipient and message information.
'''
recipient = kwargs.get('recipient')
message = kwargs.get('message')
url = "sms:"
if recipient:
# Apple has not supported multiple recipients yet.
url += str(recipient)
if message:
# Apple has to supported it yet.
pass
nsurl = NSURL.alloc().initWithString_(objc_str(url))
UIApplication.sharedApplication().openURL_(nsurl)
def instance():
return IOSSms()

View File

@ -0,0 +1,31 @@
'''
iOS Spatial Orientation
-----------------------
'''
from plyer.facades import SpatialOrientation
from pyobjus import autoclass
class iOSSpatialOrientation(SpatialOrientation):
def __init__(self):
self.bridge = autoclass('bridge').alloc().init()
self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1)
def _enable_listener(self):
self.bridge.startDeviceMotion()
def _disable_listener(self):
self.bridge.stopDeviceMotion()
def _get_orientation(self):
return (
self.bridge.sp_yaw,
self.bridge.sp_pitch,
self.bridge.sp_roll)
def instance():
return iOSSpatialOrientation()

View File

@ -0,0 +1,62 @@
'''
iOS Storage Path
--------------------
'''
from plyer.facades import StoragePath
from pyobjus import autoclass
import os
NSFileManager = autoclass('NSFileManager')
# Directory constants (NSSearchPathDirectory enumeration)
NSApplicationDirectory = 1
NSDocumentDirectory = 9
NSDownloadsDirectory = 15
NSMoviesDirectory = 17
NSMusicDirectory = 18
NSPicturesDirectory = 19
class iOSStoragePath(StoragePath):
def __init__(self):
self.defaultManager = NSFileManager.defaultManager()
def _get_home_dir(self):
return os.path.expanduser('~/')
def _get_external_storage_dir(self):
return 'This feature is not implemented for this platform.'
def _get_root_dir(self):
return 'This feature is not implemented for this platform.'
def _get_documents_dir(self):
return self.defaultManager.URLsForDirectory_inDomains_(
NSDocumentDirectory, 1).firstObject().absoluteString.UTF8String()
def _get_downloads_dir(self):
return self.defaultManager.URLsForDirectory_inDomains_(
NSDownloadsDirectory, 1).firstObject().absoluteString.UTF8String()
def _get_videos_dir(self):
return self.defaultManager.URLsForDirectory_inDomains_(
NSMoviesDirectory, 1).firstObject().absoluteString.UTF8String()
def _get_music_dir(self):
return self.defaultManager.URLsForDirectory_inDomains_(
NSMusicDirectory, 1).firstObject().absoluteString.UTF8String()
def _get_pictures_dir(self):
return self.defaultManager.URLsForDirectory_inDomains_(
NSPicturesDirectory, 1).firstObject().absoluteString.UTF8String()
def _get_application_dir(self):
return self.defaultManager.URLsForDirectory_inDomains_(
NSApplicationDirectory, 1).firstObject().absoluteString.\
UTF8String()
def instance():
return iOSStoragePath()

View File

@ -0,0 +1,37 @@
from pyobjus import autoclass, objc_str
from pyobjus.dylib_manager import load_framework
from plyer.facades import TTS
load_framework('/System/Library/Frameworks/AVFoundation.framework')
AVSpeechUtterance = autoclass('AVSpeechUtterance')
AVSpeechSynthesizer = autoclass('AVSpeechSynthesizer')
AVSpeechSynthesisVoice = autoclass('AVSpeechSynthesisVoice')
class iOSTextToSpeech(TTS):
def __init__(self):
super().__init__()
self.synth = AVSpeechSynthesizer.alloc().init()
self.voice = None
def _set_locale(self, locale="en-US"):
self.voice = AVSpeechSynthesisVoice.voiceWithLanguage_(
objc_str(locale)
)
def _speak(self, **kwargs):
message = kwargs.get('message')
if(not self.voice):
self._set_locale()
utterance = \
AVSpeechUtterance.speechUtteranceWithString_(objc_str(message))
utterance.voice = self.voice
self.synth.speakUtterance_(utterance)
def instance():
return iOSTextToSpeech()

View File

@ -0,0 +1,27 @@
'''
Module of iOS API for plyer.uniqueid.
'''
from pyobjus import autoclass
from pyobjus.dylib_manager import load_framework
from plyer.facades import UniqueID
load_framework('/System/Library/Frameworks/UIKit.framework')
UIDevice = autoclass('UIDevice')
class IOSUniqueID(UniqueID):
'''
Implementation of iOS uniqueid API.
'''
def _get_uid(self):
uuid = UIDevice.currentDevice().identifierForVendor.UUIDString()
return uuid.UTF8String()
def instance():
'''
Instance for facade proxy.
'''
return IOSUniqueID()

View File

@ -0,0 +1,43 @@
'''Implementation Vibrator for iOS.
Install: Add AudioToolbox framework to your application.
'''
import ctypes
from plyer.facades import Vibrator
class IosVibrator(Vibrator):
'''iOS Vibrator class.
iOS doesn't support any feature.
All time, pattern, repetition are ignored.
'''
def __init__(self):
super().__init__()
try:
self._func = ctypes.CDLL(None).AudioServicesPlaySystemSound
except AttributeError:
self._func = None
def _vibrate(self, time=None, **kwargs):
# kSystemSoundID_Vibrate is 0x00000FFF
self._func(0xFFF)
def _pattern(self, pattern=None, repeat=None, **kwargs):
self._vibrate()
def _exists(self, **kwargs):
return self._func is not None
def _cancel(self, **kwargs):
pass
def instance():
'''Returns Vibrator
:return: instance of class IosVibrator
'''
return IosVibrator()

View File

View File

@ -0,0 +1,35 @@
'''
Linux accelerometer
---------------------
'''
from plyer.facades import Accelerometer
import glob
import re
class LinuxAccelerometer(Accelerometer):
def _enable(self):
pass
def _disable(self):
pass
def _get_acceleration(self):
try:
pos = glob.glob("/sys/devices/platform/*/position")[0]
except IndexError:
raise Exception('Could not enable accelerometer!')
with open(pos, "r") as p:
t = p.read()
coords = re.findall(r"[-]?\d+\.?\d*", t)
# Apparently the acceleration on sysfs goes from -1000 to 1000.
# I divide it by 100 to make it equivalent to Android.
# The negative is because the coordinates are inverted on Linux
return [float(i) / -100 for i in coords]
def instance():
return LinuxAccelerometer()

View File

@ -0,0 +1,102 @@
'''
Module of Linux API for plyer.battery.
'''
from math import floor
from os import environ
from os.path import exists, join
from subprocess import Popen, PIPE
from plyer.facades import Battery
from plyer.utils import whereis_exe
class LinuxBattery(Battery):
'''
Implementation of Linux battery API via accessing the sysclass power_supply
path from the kernel.
'''
def _get_state(self):
status = {"isCharging": None, "percentage": None}
kernel_bat_path = join('/sys', 'class', 'power_supply', 'BAT0')
uevent = join(kernel_bat_path, 'uevent')
with open(uevent) as fle:
lines = [
line.decode('utf-8').strip()
for line in fle.readlines()
]
output = {
line.split('=')[0]: line.split('=')[1]
for line in lines
}
is_charging = output['POWER_SUPPLY_STATUS'] == 'Charging'
total = float(output['POWER_SUPPLY_CHARGE_FULL'])
now = float(output['POWER_SUPPLY_CHARGE_NOW'])
capacity = floor(now / total * 100)
status['percentage'] = capacity
status['isCharging'] = is_charging
return status
class UPowerBattery(Battery):
'''
Implementation of UPower battery API.
'''
def _get_state(self):
# if no LANG specified, return empty string
old_lang = environ.get('LANG', '')
environ['LANG'] = 'C'
status = {"isCharging": None, "percentage": None}
# We are supporting only one battery now
# this will fail if there is no object with such path,
# however it's safer than 'upower -d' which provides
# multiple unrelated 'state' and 'percentage' keywords
dev = "/org/freedesktop/UPower/devices/battery_BAT0"
upower_process = Popen(
["upower", "--show-info", dev],
stdout=PIPE
)
output = upower_process.communicate()[0].decode()
environ['LANG'] = old_lang
if not output:
return status
state = percentage = None
for line in output.splitlines():
if 'state' in line:
state = line.rpartition(':')[-1].strip()
if 'percentage' in line:
percentage = line.rpartition(':')[-1].strip()[:-1]
# switching decimal comma to dot
# (different LC_NUMERIC locale)
percentage = float(
percentage.replace(',', '.')
)
if state:
status['isCharging'] = state == "charging"
status['percentage'] = percentage
return status
def instance():
'''
Instance for facade proxy.
'''
import sys
if whereis_exe('upower'):
return UPowerBattery()
sys.stderr.write("upower not found.")
if exists(join('/sys', 'class', 'power_supply', 'BAT0')):
return LinuxBattery()
return Battery()

View File

@ -0,0 +1,29 @@
'''
Linux Brightness
----------------
'''
from plyer.facades import Brightness
import subprocess
import os
class LinuxBrightness(Brightness):
def __init__(self):
if os.system("which xbacklight"):
msg = ("It looks like 'xbacklight' is not installed. Try "
"installing it with your distribution's package manager.")
raise Exception(msg)
def _current_level(self):
cr_level = subprocess.check_output(["xbacklight", "-get"])
return str(cr_level)
def _set_level(self, level):
subprocess.call(["xbacklight", "-set", str(level)])
def instance():
return LinuxBrightness()

View File

@ -0,0 +1,116 @@
'''
Module of Linux API for plyer.cpu.
'''
from os.path import join
from os import environ, listdir
from subprocess import Popen, PIPE
from plyer.facades import CPU
from plyer.utils import whereis_exe
class LinuxCPU(CPU):
'''
Implementation of Linux CPU API.
'''
def _sockets(self):
# physical CPU sockets (or slots) on motherboard
sockets = [] # list of CPU ids from kernel
# open Linux kernel data file for CPU
with open('/proc/cpuinfo', 'rb') as fle:
lines = fle.readlines()
# go through the lines and obtain physical CPU ids
for line in lines:
line = line.decode('utf-8')
if 'physical id' not in line:
continue
cpuid = line.split(':')[1].strip()
sockets.append(cpuid)
# total sockets is the length of unique CPU ids from kernel
sockets = len(set(sockets))
return sockets
def _physical(self):
# cores
physical = [] # list of CPU ids from kernel
# open Linux kernel data file for CPU
with open('/proc/cpuinfo', 'rb') as fle:
lines = fle.readlines()
# go through the lines and obtain CPU core ids
for line in lines:
line = line.decode('utf-8')
if 'core id' not in line:
continue
cpuid = line.split(':')[1].strip()
physical.append(cpuid)
# total cores (socket * core per socket)
# is the length of unique CPU core ids from kernel
physical = len(set(physical))
return physical
def _logical(self):
# cores * threads
logical = None
old_lang = environ.get('LANG', '')
environ['LANG'] = 'C'
_logical = Popen(['nproc', '--all'], stdout=PIPE)
output = _logical.communicate()[0].decode('utf-8').strip()
if output:
logical = int(output)
environ['LANG'] = old_lang
return logical
def _cache(self):
values = {key: 0 for key in ('L1', 'L2', 'L3')}
cpu_path = join('/sys', 'devices', 'system', 'cpu')
# get present cores from kernel device
with open(join(cpu_path, 'present')) as fle:
present = fle.read().decode('utf-8')
present = present.strip().split('-')
if len(present) == 2:
present = range(int(present[1]) + 1)
else:
present = [present[0]]
cores = ['cpu{}'.format(i) for i in present]
for core in cores:
indicies = [
# get 'indexN' files from 'cache' folder assuming
# the filename is in range index0 to index99
# in case a wild 'index_whatevercontent' file appears
fle for fle in listdir(join(cpu_path, core, 'cache'))
if fle.startswith('index') and len(fle) <= len('index') + 2
]
for index in indicies:
index_type = join(cpu_path, core, 'cache', index, 'level')
with open(index_type, 'rb') as fle:
cache_level = fle.read().decode('utf-8').strip()
values['L{}'.format(cache_level)] += 1
return values
@staticmethod
def _numa():
return
def instance():
'''
Instance for facade proxy.
'''
import sys
if whereis_exe('nproc'):
return LinuxCPU()
sys.stderr.write("nproc not found.")
return CPU()

View File

@ -0,0 +1,23 @@
'''
Module of Linux API for plyer.devicename.
'''
import socket
from plyer.facades import DeviceName
class LinuxDeviceName(DeviceName):
'''
Implementation of Linux DeviceName API.
'''
def _get_device_name(self):
hostname = socket.gethostname()
return hostname
def instance():
'''
Instance for facade proxy.
'''
return LinuxDeviceName()

View File

@ -0,0 +1,47 @@
'''
Module of Linux API for plyer.email.
'''
import subprocess
try:
from urllib.parse import quote
except ImportError:
from urllib import quote
from plyer.facades import Email
from plyer.utils import whereis_exe
class LinuxEmail(Email):
'''
Implementation of Linux email API.
'''
def _send(self, **kwargs):
recipient = kwargs.get('recipient')
subject = kwargs.get('subject')
text = kwargs.get('text')
uri = "mailto:"
if recipient:
uri += str(recipient)
if subject:
uri += "?" if "?" not in uri else "&"
uri += "subject="
uri += quote(str(subject))
if text:
uri += "?" if "?" not in uri else "&"
uri += "body="
uri += quote(str(text))
subprocess.Popen(["xdg-open", uri])
def instance():
'''
Instance for facade proxy.
'''
import sys
if whereis_exe('xdg-open'):
return LinuxEmail()
sys.stderr.write("xdg-open not found.")
return Email()

View File

@ -0,0 +1,269 @@
'''
Linux file chooser
------------------
'''
from plyer.facades import FileChooser
from distutils.spawn import find_executable as which
import os
import subprocess as sp
import time
class SubprocessFileChooser:
'''A file chooser implementation that allows using
subprocess back-ends.
Normally you only need to override _gen_cmdline, executable,
separator and successretcode.
'''
executable = ""
'''The name of the executable of the back-end.
'''
separator = "|"
'''The separator used by the back-end. Override this for automatic
splitting, or override _split_output.
'''
successretcode = 0
'''The return code which is returned when the user doesn't close the
dialog without choosing anything, or when the app doesn't crash.
'''
path = None
multiple = False
filters = []
preview = False
title = None
icon = None
show_hidden = False
def __init__(self, *args, **kwargs):
self._handle_selection = kwargs.pop(
'on_selection', self._handle_selection
)
# Simulate Kivy's behavior
for i in kwargs:
setattr(self, i, kwargs[i])
@staticmethod
def _handle_selection(selection):
'''
Dummy placeholder for returning selection from chooser.
'''
return selection
_process = None
def _run_command(self, cmd):
self._process = sp.Popen(cmd, stdout=sp.PIPE)
while True:
ret = self._process.poll()
if ret is not None:
if ret == self.successretcode:
out = self._process.communicate()[0].strip().decode('utf8')
return self._set_and_return_selection(
self._split_output(out))
else:
return self._set_and_return_selection(None)
time.sleep(0.1)
def _set_and_return_selection(self, value):
self.selection = value
self._handle_selection(value)
return value
def _split_output(self, out):
'''This methods receives the output of the back-end and turns
it into a list of paths.
'''
return out.split(self.separator)
def _gen_cmdline(self):
'''Returns the command line of the back-end, based on the current
properties. You need to override this.
'''
raise NotImplementedError()
def run(self):
return self._run_command(self._gen_cmdline())
class ZenityFileChooser(SubprocessFileChooser):
'''A FileChooser implementation using Zenity (on GNU/Linux).
Not implemented features:
* show_hidden
* preview
'''
executable = "zenity"
separator = "|"
successretcode = 0
def _gen_cmdline(self):
cmdline = [
which(self.executable),
"--file-selection",
"--confirm-overwrite"
]
if self.multiple:
cmdline += ["--multiple"]
if self.mode == "save":
cmdline += ["--save"]
elif self.mode == "dir":
cmdline += ["--directory"]
if self.path:
cmdline += ["--filename", self.path]
if self.title:
cmdline += ["--name", self.title]
if self.icon:
cmdline += ["--window-icon", self.icon]
for f in self.filters:
if type(f) == str:
cmdline += ["--file-filter", f]
else:
cmdline += [
"--file-filter",
"{name} | {flt}".format(name=f[0], flt=" ".join(f[1:]))
]
return cmdline
class KDialogFileChooser(SubprocessFileChooser):
'''A FileChooser implementation using KDialog (on GNU/Linux).
Not implemented features:
* show_hidden
* preview
'''
executable = "kdialog"
separator = "\n"
successretcode = 0
def _gen_cmdline(self):
cmdline = [which(self.executable)]
filt = []
for f in self.filters:
if type(f) == str:
filt += [f]
else:
filt += list(f[1:])
if self.mode == "dir":
cmdline += [
"--getexistingdirectory",
(self.path if self.path else os.path.expanduser("~"))
]
elif self.mode == "save":
cmdline += [
"--getsavefilename",
(self.path if self.path else os.path.expanduser("~")),
" ".join(filt)
]
else:
cmdline += [
"--getopenfilename",
(self.path if self.path else os.path.expanduser("~")),
" ".join(filt)
]
if self.multiple:
cmdline += ["--multiple", "--separate-output"]
if self.title:
cmdline += ["--title", self.title]
if self.icon:
cmdline += ["--icon", self.icon]
return cmdline
class YADFileChooser(SubprocessFileChooser):
'''A NativeFileChooser implementation using YAD (on GNU/Linux).
Not implemented features:
* show_hidden
'''
executable = "yad"
separator = "|?|"
successretcode = 0
def _gen_cmdline(self):
cmdline = [
which(self.executable),
"--file-selection",
"--confirm-overwrite",
"--geometry",
"800x600+150+150"
]
if self.multiple:
cmdline += ["--multiple", "--separator", self.separator]
if self.mode == "save":
cmdline += ["--save"]
elif self.mode == "dir":
cmdline += ["--directory"]
if self.preview:
cmdline += ["--add-preview"]
if self.path:
cmdline += ["--filename", self.path]
if self.title:
cmdline += ["--name", self.title]
if self.icon:
cmdline += ["--window-icon", self.icon]
for f in self.filters:
if type(f) == str:
cmdline += ["--file-filter", f]
else:
cmdline += [
"--file-filter",
"{name} | {flt}".format(name=f[0], flt=" ".join(f[1:]))
]
return cmdline
CHOOSERS = {
"gnome": ZenityFileChooser,
"kde": KDialogFileChooser,
"yad": YADFileChooser
}
class LinuxFileChooser(FileChooser):
'''FileChooser implementation for GNu/Linux. Accepts one additional
keyword argument, *desktop_override*, which, if set, overrides the
back-end that will be used. Set it to "gnome" for Zenity, to "kde"
for KDialog and to "yad" for YAD (Yet Another Dialog).
If set to None or not set, a default one will be picked based on
the running desktop environment and installed back-ends.
'''
desktop = None
if (str(os.environ.get("XDG_CURRENT_DESKTOP")).lower() == "kde"
and which("kdialog")):
desktop = "kde"
elif (str(os.environ.get("DESKTOP_SESSION")).lower() == "trinity"
and which('kdialog')):
desktop = "kde"
elif which("yad"):
desktop = "yad"
elif which("zenity"):
desktop = "gnome"
def _file_selection_dialog(self, desktop_override=desktop, **kwargs):
if not desktop_override:
desktop_override = self.desktop
# This means we couldn't find any back-end
if not desktop_override:
raise OSError("No back-end available. Please install one.")
chooser = CHOOSERS[desktop_override]
c = chooser(**kwargs)
return c.run()
def instance():
return LinuxFileChooser()

View File

@ -0,0 +1,19 @@
try:
import keyring
except ImportError:
raise NotImplementedError()
from plyer.facades import Keystore
class LinuxKeystore(Keystore):
def _set_key(self, servicename, key, value, **kwargs):
keyring.set_password(servicename, key, value)
def _get_key(self, servicename, key, **kwargs):
return keyring.get_password(servicename, key)
def instance():
return LinuxKeystore()

Some files were not shown because too many files have changed in this diff Show More