Included local plyer
This commit is contained in:
parent
25f0d52260
commit
f23855fb68
|
@ -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)
|
|
@ -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
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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!")
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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
Loading…
Reference in New Issue