Installation
Compatibility
info
Supported on Android 6 (minSdk
= 23) and above with compileSdk
>= 31.
For Java on desktop apps, please contact us at support@ditto.live.
LAN | P2P WiFi | Bluetooth | WebSockets | |
---|---|---|---|---|
Android | ✓ | ✓ | ✓ | ✓ |
Java on Desktop | Contact us | x | Contact us | x |
Setup
- Ensure your device has Android 6 or higher
- Ensure that
mavenCentral()
is in the project-level build.gradle file like so:
allprojects { repositories { mavenCentral() }}
- In the individual module build.gradle file add:
dependencies { // ... implementation "live.ditto:ditto:4.4.5"}
Configure your manifest with the following permissions. Ditto's required permissions have changed since Android 13. Refer to Android Platform Permissions for details.
Access the portal to create a new app. Apps created on the portal will automatically sync data between them and also to the Ditto Big Peer. Each app created on the portal has a unique
appID
which can be seen on your app's settings page once the app has been created. This ID is used in subsequent sections to configure your Ditto instance.Create your Ditto instance as below. We recommend placing this in your Application.onCreate method. Note that the
Context
you want to reference here is the Application levelContext
, rather than whatever Activity you might happen to be instantiating the Ditto instance from initially. This is because you need to ensure that your app is keeping a single Ditto instance alive for the entire lifetime of the application so it's not going out of scope or getting garbage collected.
try { val androidDependencies = DefaultAndroidDittoDependencies(context) val identity = DittoIdentity.OnlinePlayground( androidDependencies, appId = "REPLACE_ME_WITH_YOUR_APP_ID", token = "REPLACE_ME_WITH_YOUR_PLAYGROUND_TOKEN" ) DittoLogger.minimumLogLevel = DittoLogLevel.DEBUG ditto = Ditto(androidDependencies, identity) ditto.startSync()} catch (e: DittoError) { Log.e("Ditto error", e.message!!)}
DittoDependencies androidDependencies = new DefaultAndroidDittoDependencies(this.context);DittoLogger.setMinimumLogLevel(DittoLogLevel.DEBUG);DittoIdentity identity = new DittoIdentity.OnlinePlayground(androidDependencies, "REPLACE_ME_WITH_YOUR_APP_ID", "YOUR_PLAYGROUND_TOKEN_HERE");Ditto ditto = new Ditto(androidDependencies, identity);
try { ditto.startSync();} catch(DittoError e) { //handle error}
Android Platform Permissions
The Android operating system restricts access to some device functionality for the sake of user control and privacy. Therefore, to unlock the full capabilities of Ditto, it is essential to configure your app so that it requests all the permissions that it needs.
Then at runtime your app must prompt the user to request certain permissions.
Android Manifest Permissions
The Ditto Android SDK includes a set of permissions that are required to use all the device features necessary to enable sync. The permissions below will be automatically merged into your app's final manifest.
<manifest xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /><uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" tools:targetApi="s" /><uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:targetApi="s" /><uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" tools:targetApi="s" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="32" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /><uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" /><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /><uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" android:usesPermissionFlags="neverForLocation" tools:targetApi="tiramisu" />
The tools:targetApi
attribute causes the permission to only be requested on
devices running the specified API level or higher. This avoids errors on older
OS versions that do not recognize the permission.
The android:maxSdkVersion
attribute causes that permission to only be requested
on devices running the specified SDK level or lower. This avoids asking for more
permissions than Ditto needs, however it will prevent your app from being able
to request the permission on devices running a newer OS version. To opt out of
this behavior and request the permission on all OS versions, see the following
snippet:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" tools:remove="android:maxSdkVersion" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" tools:remove="android:maxSdkVersion" />
info
Note that you may need to add the xmlns:tools="http://schemas.android.com/tools"
namespace attribute to your app's root <manifest>
tag as shown in the
AndroidManifest.xml
example above.
This will configure your app's build to ignore the android:maxSdkVersion
attribute in our SDK allowing the permission to be requested on any SDK version.
This techniquie can be used to tweak any permissions to your liking.
For more details, see the documentation for the Bluetooth and WiFi Aware permissions in the Android Documentation.
Runtime Permissions
Bluetooth LE and WiFi Aware require the user to allow permissions at runtime.
Your apps must ensure all required permissions for sync have been requested from
the user. The Android Ditto SDK provides a DittoSyncPermissions
helper which
makes this easy. Call the following in your Activity or Fragment's onCreate
method:
The DittoSyncPermissions
object requires a Context. You can get the context by
invoking getApplicationContext()
, getContext()
, getBaseContext()
or this
when in a class that extends from Context, such as the Application, Activity, Service
and IntentService classes.
fun checkPermissions() { val missing = DittoSyncPermissions(this).missingPermissions() if (missing.isNotEmpty()) { this.requestPermissions(missing, 0) }}
Alternatively, requireActivity()
is a way to force the code to only work on a
Fragment that has a Context.
fun checkPermissions() { val activity = requireActivity() val missing = DittoSyncPermissions(activity).missingPermissions() if (missing.isNotEmpty()) { activity.requestPermissions(missing, 0) }}
On Android there may be a noticeable delay between when the user grants location
access and when Ditto notices the new permission. For this reason it is
recommended to call refreshPermissions()
whenever a relevant permission might
have changed. This will force an immediate check. If a permission has become
available your app can begin syncing as quickly as possible.
override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) // Regardless of the outcome, tell Ditto that permissions maybe changed ditto?.refreshPermissions()}
void checkLocationPermission() { DittoSyncPermissions permissions = new DittoSyncPermissions(this); String[] missing = permissions.missingPermissions(permissions.requiredPermissions()); if (missing.length > 0) { this.requestPermissions(missing, 0); }}
On Android there may be a noticeable delay between when the user grants location
access and when Ditto notices the new permission. For this reason it is
recommended to call refreshPermissions()
whenever a relevant permission might
have changed. This will force an immediate check. If a permission has become
available your app can begin syncing as quickly as possible.
@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); this.ditto.refreshPermissions();}
For more information about requesting permissions in a user-friendly way refer to Android's documentation: Request App Permissions.