Documentation and example to integrate Beepiz monitoring control from a third-party Android app.
Starting with versionCode 165 (available through the public beta channel on the Google Play Store), the Beepiz Android app allows third-party apps to do the following:
- Listen Beepiz monitoring state
- Listen if Beepiz requires to be opened for configuration
- Start monitoring
- Stop Monitoring
This is guarded by a "dangerous" permission that the controlling app must declare, and request on API level 23 and newer.
The example application has been designed to provide leak-free binding with Beepiz, and is capable of re-establishing the connection automatically after Beepiz process death or package replacement (caused by update from the Play Store).
It relies on structured-concurrency from kotlinx.coroutines to avoid leaking memory, and the ServiceConnection that we need to listen to Beepiz monitoring and config state.
Note that it uses Android Gradle Plugin 3.5.0-beta05, but you can downgrade it to 3.4.1 if needed from the root build.gradle.
The integration uses Android's built-in secure IPC (inter-process communication), permission system and broadcast.
All the constants needed in this integration can be found in the BeepizBindingConstants file.
First, in the AndroidManifest.xml
file of a library module in your project, or of the application
module of your app, add the following permission usage declaration under the opening <manifest>
tag:
<uses-permission android:name="net.suivideflotte.dati.permission.CONTROL_MONITORING" />
See constant permission
in BeepizBindingConstants.
The permission mentioned above is marked as dangerous to prevent abuse, so on API level 23 (Android 6) and newer, you'll need to request it at runtime, which will cause the system to ask the user, and will allow him to revoke it at any time from the device settings (note that revoking a permission kills the process, so in a process life, you don't have to handle permission being revoked).
The example application includes an example on how to request a permission with ease in the
MainActivity, with a single function call to ensurePermissionOrFinishAndCancel
. In case you
don't want to finish your Activity
and cancel the current coroutine but want the same ease of use,
you can use ensurePermission
also available in this sample.
See constants bindAction
and packageName
in BeepizBindingConstants.
After ensuring the Beepiz app is installed, create an Intent with the action
"bind_monitoring_state"
, and specify the package
of Beepiz (net.suivideflotte.dati
).
You can then bind to the service using this Intent
using bindService
with the
flag Context.BIND_AUTO_CREATE
.
It is your responsibility to handle bindService
returning false
, and managing the
ServiceConnection
lifecycle to not leak it. The example app includes an example on how to do it
in a simple way at use-site, leveraging cancellable coroutines and basic language constructs like
while loops to recover from disconnections.
You can navigate the source from Android Studio using ctrl/cmd + click, starting from MainActivity.
Most of the code of the example is reusable (BeepizControlUiImpl
being an exception), feel free to
make copies into your project.
Android official documentation on bound services can be found here.
See constants CURRENTLY_MONITORING
, CURRENTLY_NOT_MONITORING
, ARG1_REQUIRES_CONFIG
and
ARG1_CONFIG_OK
in BeepizBindingConstants.
Create a custom Handler subclass and override the handleMessage
function/method.
Dot not keep a reference to the [Message] instances after handleMessage
returns as they are
immediately recycled after it for all sort of events through your app (including user input events).
The what
field/property of the messages incoming from Beepiz can currently have two values:
0
when Beepiz is *not monitoring (CURRENTLY_NOT_MONITORING
)1
when Beepiz is currently monitoring (CURRENTLY_MONITORING
)
The arg1
field/property can also have two values:
0
when Beepiz config is OK (ARG1_CONFIG_OK
). You can start Beepiz monitoring if not running.1
when Beepiz requires to be launched for user configuration (ARG1_REQUIRES_CONFIG
).
See constants REGISTER_CLIENT
and UNREGISTER_CLIENT
in BeepizBindingConstants.
What is described here must happen when the onServiceConnected
function/method of your
ServiceConnection subclass is called.
Create a Messenger instance for your client, passing an instance of your custom Handler
subclass
that you have prepared in previous step.
Create a Messenger
instance for the service from the IBinder
received in onServiceConnected
.
Create a register message by calling Messenger.obtain()
, and apply the following configuration to
it:
- Set
what
to1
(REGISTER_CLIENT
) - Set
replyTo
to the clientMessenger
instance you have setup before. - If the device API level is 22 or newer, call
setAsynchronous(true)
to reduce latency.
Send that register message to the service Messenger
you created.
You'll receive current state in handleMessage
from your custom Handler
instance you passed to
your client Messenger
, and all subsequent updates.
When you no longer need to listen to state changes from Beepiz, you need to unregister then unbind the service.
To unregister, do exactly the same as for registering, but instead of 1
, set what
to 0
(UNREGISTER_CLIENT
). You can then unbind the service using unbindService.
Beware that all calls to Messenger.send
can throw a DeadObjectException
in case Beepiz is killed.
You need to catch it and treat it as a service disconnection. We recommend to unbind the service
when this happens, ensure the app is still installed and retry binding if so.
In the example, the service is simply unbound in the finally
block, but you may have to do it
differently if you don't use coroutines.
See constants startAction
and stopAction
in BeepizBindingConstants.
When Beepiz does not require to be open for configuration, you can start monitoring directly from
your app by sending a broadcast with the sendBroadcast function/method. The Intent
action needs
to be "start_monitoring"
, and you have to specify the package
of Beepiz
(net.suivideflotte.dati
).
When Beepiz monitoring is running, you can stop it by doing exactly the same as for starting it,
but passing with the Intent
action "stop_monitoring"
instead.
Note: You can reproduce the cases where Beepiz requires in-app configuration by disabling location in the device settings. There are other cases that might make Beepiz require in-app configuration, but this one is the easiest to replicate, and all the cases need to be handled the same way by your app: suggesting the user to open Beepiz, or open-it directly on any start/stop request, instead of actually starting or stopping the monitoring.