← Volver al listado de tecnologías
APIs Nativas
Plyer - API Cross-Platform
Plyer proporciona acceso a funciones nativas de forma unificada.
Instalación
pip install plyer
buildozer.spec
requirements = python3,kivy,plyer
Funciones de Plyer
| Función | Plataformas | Descripción |
|---|---|---|
accelerometer | Android, iOS | Acelerómetro |
battery | Android, iOS, Linux, Windows | Estado batería |
camera | Android, iOS | Tomar fotos |
compass | Android, iOS | Brújula |
gps | Android, iOS | Ubicación GPS |
notification | Android, iOS, Linux, Windows | Notificaciones |
sms | Android | Enviar SMS |
vibrator | Android, iOS | Vibración |
tts | Android, iOS | Texto a voz |
filechooser | Todas | Selector archivos |
GPS / Ubicación
from plyer import gps
class GPSApp(App):
def build(self):
gps.configure(on_location=self.on_location)
return MiWidget()
def iniciar_gps(self):
gps.start(minTime=1000, minDistance=1)
def detener_gps(self):
gps.stop()
def on_location(self, **kwargs):
lat = kwargs.get('lat')
lon = kwargs.get('lon')
print(f'Ubicación: {lat}, {lon}')
Permisos Necesarios
# buildozer.spec
android.permissions = ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION
Notificaciones
from plyer import notification
notification.notify(
title='Mi App',
message='Tienes un nuevo mensaje',
app_name='MiApp',
app_icon='assets/icon.png',
timeout=10
)
Cámara
from plyer import camera
from os.path import join
from kivy.app import App
class CamaraApp(App):
def capturar(self):
ruta = join(self.user_data_dir, 'foto.jpg')
camera.take_picture(
filename=ruta,
on_complete=self.foto_lista
)
def foto_lista(self, filepath):
print(f'Foto guardada: {filepath}')
Permisos
android.permissions = CAMERA,WRITE_EXTERNAL_STORAGE
Vibración
from plyer import vibrator
# Vibrar por 0.5 segundos
vibrator.vibrate(time=0.5)
# Patrón de vibración
vibrator.pattern(pattern=[0, 0.2, 0.1, 0.2])
# Verificar soporte
if vibrator.exists():
vibrator.vibrate()
Permisos
android.permissions = VIBRATE
Batería
from plyer import battery
estado = battery.status
print(f'Nivel: {estado["percentage"]}%')
print(f'Cargando: {estado["isCharging"]}')
Selector de Archivos
from plyer import filechooser
def seleccionar_archivo():
filechooser.open_file(
on_selection=archivo_seleccionado,
filters=['*.png', '*.jpg']
)
def archivo_seleccionado(selection):
if selection:
print(f'Archivo: {selection[0]}')
Texto a Voz (TTS)
from plyer import tts
tts.speak('Hola, esto es texto a voz')
Pyjnius - Acceso a Java/Android
Para funciones no disponibles en Plyer, usa Pyjnius directamente.
Instalación
pip install pyjnius
buildozer.spec
requirements = python3,kivy,pyjnius
Ejemplo: Toast Android
from jnius import autoclass
def mostrar_toast(mensaje):
PythonActivity = autoclass('org.kivy.android.PythonActivity')
Toast = autoclass('android.widget.Toast')
String = autoclass('java.lang.String')
activity = PythonActivity.mActivity
def _toast(dt):
Toast.makeText(
activity,
String(mensaje),
Toast.LENGTH_SHORT
).show()
from kivy.clock import Clock
Clock.schedule_once(_toast, 0)
Ejemplo: Compartir
from jnius import autoclass, cast
def compartir(texto, titulo='Compartir'):
Intent = autoclass('android.content.Intent')
String = autoclass('java.lang.String')
PythonActivity = autoclass('org.kivy.android.PythonActivity')
intent = Intent()
intent.setAction(Intent.ACTION_SEND)
intent.putExtra(Intent.EXTRA_TEXT, String(texto))
intent.setType('text/plain')
chooser = Intent.createChooser(intent, String(titulo))
PythonActivity.mActivity.startActivity(chooser)
Ejemplo: Abrir URL
from jnius import autoclass
def abrir_url(url):
Intent = autoclass('android.content.Intent')
Uri = autoclass('android.net.Uri')
PythonActivity = autoclass('org.kivy.android.PythonActivity')
intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
PythonActivity.mActivity.startActivity(intent)
Clases Android Comunes
| Clase | Uso |
|---|---|
PythonActivity.mActivity | Activity actual |
android.content.Intent | Intents |
android.widget.Toast | Mensajes toast |
android.net.Uri | URIs |
android.os.Build | Info del dispositivo |
android.provider.Settings | Configuración |
Verificar Plataforma
from kivy.utils import platform
if platform == 'android':
from jnius import autoclass
# Código Android
elif platform == 'ios':
# Código iOS
else:
# Desktop
pass
Ciclo de Vida Android
from kivy.app import App
class MiApp(App):
def on_pause(self):
# App en segundo plano
# Guardar estado, detener GPS, etc.
return True # Permitir pausar
def on_resume(self):
# App vuelve a primer plano
pass
def on_stop(self):
# App terminando
pass
Almacenamiento
from kivy.app import App
class MiApp(App):
def guardar_datos(self, datos):
# user_data_dir es persistente
ruta = f'{self.user_data_dir}/datos.json'
with open(ruta, 'w') as f:
json.dump(datos, f)
def cargar_datos(self):
ruta = f'{self.user_data_dir}/datos.json'
try:
with open(ruta) as f:
return json.load(f)
except FileNotFoundError:
return {}
Pyobjus - iOS
Para iOS, usa Pyobjus en lugar de Pyjnius.
from pyobjus import autoclass
# Ejemplo: Vibración en iOS
AudioToolbox = autoclass('AudioToolbox')
AudioToolbox.AudioServicesPlaySystemSound(1352)
Ejemplo Completo: App con GPS
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.utils import platform
class GPSWidget(BoxLayout):
ubicacion = StringProperty('Esperando GPS...')
class GPSApp(App):
def build(self):
self.widget = GPSWidget()
return self.widget
def on_start(self):
if platform == 'android':
from plyer import gps
gps.configure(on_location=self.on_ubicacion)
gps.start(minTime=1000, minDistance=1)
def on_ubicacion(self, **kwargs):
lat = kwargs.get('lat', 0)
lon = kwargs.get('lon', 0)
self.widget.ubicacion = f'{lat:.6f}, {lon:.6f}'
def on_pause(self):
return True
def on_resume(self):
pass
Testing de APIs Nativas
# test_apis_nativas.py
import unittest
from kivy.utils import platform
class TestPlataforma(unittest.TestCase):
def test_platform_valida(self):
plataformas = ['android', 'ios', 'linux', 'macosx', 'win']
self.assertIn(platform, plataformas)
def test_plyer_importable(self):
from plyer import notification
self.assertTrue(hasattr(notification, 'notify'))
def test_vibrator_existe(self):
from plyer import vibrator
self.assertTrue(hasattr(vibrator, 'vibrate'))
class TestMock(unittest.TestCase):
"""Tests que funcionan sin dispositivo real"""
def test_notification_mock(self):
from unittest.mock import MagicMock
from plyer import notification
notification.notify = MagicMock()
notification.notify(title='Test', message='Hola')
notification.notify.assert_called_once()
if __name__ == '__main__':
unittest.main()
Probar en Dispositivo con ADB
# Probar vibración
adb shell cmd vibrator vibrate 500
# Ver estado de batería
adb shell dumpsys battery
# Simular nivel de batería
adb shell dumpsys battery set level 20
# Probar notificaciones
adb shell cmd notification post -t "Test" "Mensaje de prueba"
# Ver ubicación actual
adb shell dumpsys location | grep "last location"
# Tomar foto con cámara
adb shell am start -a android.media.action.IMAGE_CAPTURE
Resumen
- Plyer proporciona API unificada multiplataforma
- Pyjnius accede directamente a clases Java/Android
- Verificar plataforma con
kivy.utils.platform on_pause/on_resumemanejan ciclo de vida móviluser_data_dirpara almacenamiento persistente