androidchaquopybeeware

How to access the Android vibrator API in BeeWare?


I'm developing an Android application in BeeWare. I want to use the vibrator to call the user's attention to certain events. However, as far as I've looked there is no direct vibration control support in BeeWare, e.g. no methods in Toga's App class, etc. Is there any other way to access the Android vibrator API?


Solution

  • The chaquopy SDK is part of the BeeWare distribution, it allows direct access to Android API's.

    To control the vibrator from an Android BeeWare app, first edit pyproject.toml to enable access to the vibration API:

    android_manifest_extra_content = """
    <!-- ... other permissions as required by your project... -->
    <uses-permission android:name="android.permission.VIBRATE" />
    """
    

    Then (re-)create your Android build with:

    briefcase create android
    

    This has to be done even if the Android build already exists, otherwise AndroidManifest.xml won't be updated with the new permission.

    Finally, use the chaquopy wrappers to access the Android Vibrator class and its dependencies. For example:

    from time import time
    
    from java import jclass
    from android.content import Context
    from android.os import VibrationEffect
    
    
    class Vibrator:
        r'''Proxy to the Android vibrator API.
        '''
        def __init__(self):
            r'''Create a new vibrator API client.
            '''
            context = jclass('org.beeware.android.MainActivity').singletonThis
            self.__vibrator = context.getSystemService(Context.VIBRATOR_SERVICE)
            self.__deadline = 0
    
        def vibrate(self, milliseconds=1000, amplitude=VibrationEffect.DEFAULT_AMPLITUDE, override=False):
            r'''Perform a vibration of the given amplitude by the given duration.
            '''
            now = time()
    
            # Cancel an ongoing vibration if an override is requested.
            if override:
                self.cancel()
    
            # Ignore this call if the last requested vibration is still ongoing.
            if now < self.__deadline:
                return
    
            self.__deadline = now + milliseconds * 0.001
            effect = VibrationEffect.createOneShot(milliseconds, amplitude)
            self.__vibrator.vibrate(effect)
    
        def cancel(self):
            r'''Cancel an ongoing vibration.
            '''
            self.__vibrator.cancel()
            self.__deadline = 0