Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9964cb6
fixed issue with missing argument. Added function to get connected ssid
GvozdevLeonid Sep 27, 2024
59050f9
updated
GvozdevLeonid Sep 27, 2024
31809b7
updated
GvozdevLeonid Oct 6, 2024
1b0443b
fixed issue with wifi on linux
GvozdevLeonid Oct 7, 2024
66132ba
added gps for macos
GvozdevLeonid Nov 13, 2024
bc89636
fixed issue
GvozdevLeonid Nov 13, 2024
81aebf9
updated
GvozdevLeonid Nov 13, 2024
be2f646
updated
GvozdevLeonid Nov 13, 2024
99065a1
updated
GvozdevLeonid Nov 13, 2024
7d78abd
updated
GvozdevLeonid Nov 13, 2024
f0a3a62
updated
GvozdevLeonid Nov 13, 2024
1dc9724
updated
GvozdevLeonid Nov 13, 2024
4eb88a4
updated
GvozdevLeonid Nov 23, 2024
94fc154
updated
GvozdevLeonid Nov 23, 2024
36ada23
fixed issue
GvozdevLeonid Mar 10, 2025
dd8cf8a
fixed issue with on_status callback
GvozdevLeonid Mar 10, 2025
e644a96
Merge branch 'kivy:master' into master
GvozdevLeonid Mar 10, 2025
54f1dd8
updated
GvozdevLeonid Mar 24, 2025
d624173
Implemented getting the real name of the device
GvozdevLeonid Mar 24, 2025
4bff763
Merge branch 'master' of github.com:GvozdevLeonid/plyer
GvozdevLeonid Mar 24, 2025
3669f18
get real device name?
GvozdevLeonid Mar 24, 2025
e028f32
updated
GvozdevLeonid Mar 24, 2025
520bdde
fixed issue
GvozdevLeonid Mar 24, 2025
c6c150d
fixed?
GvozdevLeonid Mar 24, 2025
40c7a47
added getting screen orientation
GvozdevLeonid Jul 29, 2025
c5ff872
added encryption
GvozdevLeonid Jul 30, 2025
1468c95
updated
GvozdevLeonid Oct 8, 2025
e06c169
fixed issue
GvozdevLeonid Oct 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ implement the API in the easiest way, depending on the current platform.

| Platform | Android | iOS | Windows | macOS | Linux |
| ------------------------------ |:-------:|:---:|:-------:|:-----:|:-----:|
| Accelerometer | ✔ | ✔ | | ✔ | ✔ |
| Accelerometer | ✔ | ✔ | | ✔ | ✔ |
| Audio recording | ✔ | | ✔ | ✔ | |
| Barometer | ✔ | ✔ | | | |
| Battery | ✔ | ✔ | ✔ | ✔ | ✔ |
Expand All @@ -51,7 +51,7 @@ implement the API in the easiest way, depending on the current platform.
| Devicename | ✔ | | ✔ | ✔ | ✔ |
| Email (open mail client) | ✔ | ✔ | ✔ | ✔ | ✔ |
| Flash | ✔ | ✔ | | | |
| GPS | ✔ | ✔ | | | |
| GPS | ✔ | ✔ | | | |
| Gravity | ✔ | ✔ | | | |
| Gyroscope | ✔ | ✔ | | | |
| Humidity | ✔ | | | | |
Expand Down
9 changes: 9 additions & 0 deletions plyer/facades/orientation.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ class Orientation:
Orientation facade.
'''

def get_orientation(self) -> str:
'''
Return current screen rotation (landscape, landscape-reversed, portrait, portrait-reversed, unknown)
'''
return self._get_landscape()

def set_landscape(self, reverse=False):
'''
Rotate the app to a landscape orientation.
Expand Down Expand Up @@ -71,6 +77,9 @@ def set_sensor(self, mode='any'):

# private

def _get_landscape(self, **kwargs):
raise NotImplementedError()

def _set_landscape(self, **kwargs):
raise NotImplementedError()

Expand Down
9 changes: 9 additions & 0 deletions plyer/facades/wifi.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ def is_connected(self, interface=None):
'''
return self._is_connected(interface=interface)

def connected_ssid(self, interface=None):
'''
Return returns ssid of connected WiFi interface.
'''
return self._connected_ssid(interface=interface)

@property
def interfaces(self):
'''
Expand Down Expand Up @@ -165,6 +171,9 @@ def _is_enabled(self):
def _is_connected(self, interface=None):
raise NotImplementedError()

def _connected_ssid(self, interface=None):
raise NotImplementedError()

def _start_scanning(self, interface=None):
raise NotImplementedError()

Expand Down
15 changes: 12 additions & 3 deletions plyer/platforms/android/devicename.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
from jnius import autoclass
from plyer.facades import DeviceName

Build = autoclass('android.os.Build')


class AndroidDeviceName(DeviceName):
'''
Expand All @@ -23,7 +21,18 @@ def _get_device_name(self):

Thereby making this method more backward compatible.
"""
return Build.MODEL

Build = autoclass('android.os.Build')
PythonActivity = autoclass('org.kivy.android.PythonActivity')
SettingsGlobal = autoclass('android.provider.Settings$Global')

context = PythonActivity.mActivity
name = SettingsGlobal.getString(context.getContentResolver(), SettingsGlobal.DEVICE_NAME)

if not name:
name = Build.MODEL

return name


def instance():
Expand Down
69 changes: 65 additions & 4 deletions plyer/platforms/android/keystore.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import base64
import json

from jnius import autoclass

from plyer.facades import Keystore
from plyer.platforms.android import activity


class AndroidKeystore(Keystore):

def _set_key(self, servicename, key, value, **kwargs):
cipher_text, iv = self.encrypt_value(value, servicename)

value = json.dumps({
'cipher': base64.b64encode(cipher_text).decode(),
'iv': base64.b64encode(iv).decode()
})

mode = kwargs.get("mode", 0)
settings = activity.getSharedPreferences(servicename, mode)
editor = settings.edit()
Expand All @@ -14,12 +25,62 @@ def _set_key(self, servicename, key, value, **kwargs):
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
if ret == default:
return None

blob = json.loads(ret)
return self.decrypt_value(servicename, blob)

def encrypt_value(self, value, servicename):
Cipher = autoclass('javax.crypto.Cipher')
secret_key = self.get_secret_key(servicename)

cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, secret_key)

ciphertext = cipher.doFinal(value.encode("utf-8"))
iv = cipher.getIV()
return ciphertext, iv

def decrypt_value(self, servicename, data):
GCMParameterSpec = autoclass('javax.crypto.spec.GCMParameterSpec')
Cipher = autoclass('javax.crypto.Cipher')
String = autoclass('java.lang.String')

secret_key = self.get_secret_key(servicename)
cipher = Cipher.getInstance("AES/GCM/NoPadding")

iv = base64.b64decode(data['iv'])
ct = base64.b64decode(data['cipher'])

spec = GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, secret_key, spec)
plaintext_bytes = cipher.doFinal(ct)
return str(String(plaintext_bytes, "UTF-8"))

@staticmethod
def get_secret_key(servicename):
KeyProperties = autoclass('android.security.keystore.KeyProperties')
KeyGenerator = autoclass('javax.crypto.KeyGenerator')
KeyGenParameterSpec = autoclass('android.security.keystore.KeyGenParameterSpec$Builder')
KeyStore = autoclass('java.security.KeyStore')

key_store = KeyStore.getInstance("AndroidKeyStore")
key_store.load(None)

if not key_store.containsAlias(servicename):
builder = KeyGenParameterSpec(servicename,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
builder.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
builder.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
key_gen = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
key_gen.init(builder.build())
key_gen.generateKey()

return key_store.getKey(servicename, None)

def instance():
return AndroidKeystore()
13 changes: 13 additions & 0 deletions plyer/platforms/android/orientation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from plyer.facades import Orientation

ActivityInfo = autoclass('android.content.pm.ActivityInfo')
Surface = autoclass('android.view.Surface')


class AndroidOrientation(Orientation):
Expand Down Expand Up @@ -38,6 +39,18 @@ def _set_sensor(self, **kwargs):
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT)

def _get_orientation(self):
orientations = {
Surface.ROTATION_0: 'portrait',
Surface.ROTATION_90: 'landscape',
Surface.ROTATION_180: 'portrait-reversed',
Surface.ROTATION_270: 'landscape-reversed',
}

rotation = activity.getWindowManager().getDefaultDisplay().getRotation()

return orientations.get(rotation, 'unknown')


def instance():
return AndroidOrientation()
29 changes: 29 additions & 0 deletions plyer/platforms/ios/devicename.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'''
Module of macOS API for plyer.devicename.
'''

from pyobjus import autoclass
from pyobjus.dylib_manager import load_framework

from plyer.facades import DeviceName

load_framework('/System/Library/Frameworks/UIKit.framework')


class IosDeviceName(DeviceName):
'''
Implementation of IOS DeviceName API.
'''

def _get_device_name(self):
UIDevice = autoclass('UIDevice')
device = UIDevice.currentDevice()
device_name = device.name.UTF8String()
return device_name


def instance():
'''
Instance for facade proxy.
'''
return IosDeviceName()
27 changes: 25 additions & 2 deletions plyer/platforms/linux/orientation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class LinuxOrientation(Orientation):

def _set_landscape(self, **kwargs):
self.rotate = 'normal'
self.rotate = 'inverted' if kwargs['reverse'] else 'normal'
self.screen = sb.check_output(
"xrandr -q | grep ' connected' | head -n 1 | cut -d ' ' -f1",
shell=True
Expand All @@ -14,14 +14,37 @@ def _set_landscape(self, **kwargs):
sb.call(["xrandr", "--output", self.screen, "--rotate", self.rotate])

def _set_portrait(self, **kwargs):
self.rotate = 'left'
self.rotate = 'right' if kwargs['reverse'] else 'left'
self.screen = sb.check_output(
"xrandr -q | grep ' connected' | head -n 1 | cut -d ' ' -f1",
shell=True
)
self.screen = self.screen.decode('utf-8').split('\n')[0]
sb.call(["xrandr", "--output", self.screen, "--rotate", self.rotate])

def _get_orientation(self, **kwargs):
self.screen = sb.check_output(
"xrandr -q | grep ' connected' | head -n 1 | cut -d ' ' -f1",
shell=True
)
self.screen = self.screen.decode('utf-8').split('\n')[0]

try:
orientation = sb.check_output(
f"xrandr -q --verbose | grep {self.screen} | sed 's/primary //' | awk '{{print $5}}'",
shell=True
).decode('utf-8').strip()
except Exception:
return 'unknown'

orientations = {
'normal': 'landscape',
'inverted': 'landscape-reversed',
'left': 'portrait',
'right': 'portrait-reversed'
}
return orientations.get(orientation, 'unknown')


def instance():
return LinuxOrientation()
30 changes: 30 additions & 0 deletions plyer/platforms/linux/wifi.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,21 @@ def _is_connected(self, interface=None):

return connected

def _connected_ssid(self, interface=None):
if not self._is_enabled():
self._enable()

ssid = None
ssid_proc = Popen(['nmcli', '-t', '-f', 'ACTIVE,NAME', 'connection'], stdout=PIPE)
ssid_lines = ssid_proc.communicate()[0].decode('utf-8').splitlines()
for ssid_line in ssid_lines:
active, ssid_value = ssid_line.split(':')
if active == 'yes':
ssid = ssid_value
break

return ssid

def _start_scanning(self, interface=None):
'''
Start scanning for available Wi-Fi networks
Expand Down Expand Up @@ -359,6 +374,21 @@ def _is_connected(self, interface=None):

return connected

def _connected_ssid(self, interface=None):
if not self._is_enabled():
self._enable()

ssid = None
ssid_proc = Popen(['nmcli', '-t', '-f', 'ACTIVE,NAME', 'connection'], stdout=PIPE)
ssid_lines = ssid_proc.communicate()[0].decode('utf-8').splitlines()
for ssid_line in ssid_lines:
active, ssid_value = ssid_line.split(':')
if active == 'yes':
ssid = ssid_value
break

return ssid

def _start_scanning(self, interface=None):
'''
Returns all the network information.
Expand Down
12 changes: 9 additions & 3 deletions plyer/platforms/macosx/devicename.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,24 @@
Module of MacOSX API for plyer.devicename.
'''

import socket
from pyobjus import autoclass
from pyobjus.dylib_manager import INCLUDE, load_framework

from plyer.facades import DeviceName

load_framework(INCLUDE.Foundation)


class OSXDeviceName(DeviceName):
'''
Implementation of MacOSX DeviceName API.
'''

def _get_device_name(self):
hostname = socket.gethostname()
return hostname
NSHost = autoclass('NSHost')
current_host = NSHost.currentHost()
name = current_host.localizedName.UTF8String()
return name


def instance():
Expand Down
Loading