Installation
Setup
- Ensure your device has Android 7.1 or higher
- Ensure that
mavenCentral()
is in the project-level build.gradle file like so:
buildscript { repositories { mavenCentral() }}
- In the individual module build.gradle file add:
dependencies { // ... implementation "live.ditto:ditto:1.1.9"}
android { // ...
compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }}
- Configure your manifest with the following permissions. Ditto's required permissions have changed since Android 12. Refer to Android Platform Permissions for details.
<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_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.ACCESS_FINE_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_WIFI_MULTICAST_STATE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
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:
try { val androidDependencies = AndroidDittoDependencies(context) val identity = DittoIdentity.OnlinePlaygroundV2(androidDependencies, appID = "REPLACE_ME_WITH_YOUR_APP_ID", token = "REPLACE_ME_WITH_YOUR_PLAYGROUND_TOKEN") val ditto = Ditto(androidDependencies, identity) ditto.tryStartSync()} catch(e: DittoError) { Log.e("Ditto error", e.message!!)}
DittoDependencies androidDependencies = new DefaultAndroidDittoDependencies(this.context);DittoIdentity identity = new DittoIdentity.OnlinePlaygroundV2(androidDependencies, "REPLACE_ME_WITH_YOUR_APP_ID", "YOUR_PLAYGROUND_TOKEN_HERE");Ditto ditto = new Ditto(androidDependencies, identity);
try { ditto.tryStartSync();} catch(DittoError e) { //handle error}
Android Platform Permissions
The Android operating system restricts access to some 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.
First you must add the following permissions to your app's AndroidManifest.xml
file.
Then at runtime your app must prompt the user to request certain permissions.
Modern Manifest (API Level > 30)
<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_SCAN" android:usesPermissionFlags="neverForLocation" /><uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /><uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /><uses-permission android:name="android.permission.ACCESS_FINE_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_WIFI_MULTICAST_STATE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /><uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
Since the Ditto SDK does not use BLE to locate the user physically it assumes you are using the neverForLocation
flag on BLUETOOTH_SCAN
. On Android 12, this means that ACCESS_FINE_LOCATION
is no longer required. If you wish to use ACCESS_FINE_LOCATION
on an app targeting API level 31+, you must check for and request this permission on your own. Refer to the Android Documentation for details.
Legacy Manifest (API Level <= 30)
The following manifest is sufficient for apps that are not yet targeting Android 12.
<uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /><uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
In these older Android versions, ACCESS_FINE_LOCATION
is mandatory and Ditto will check that it is present.
Runtime Permissions
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. For example, a fragment can use a method like this:
In Kotlin, DittoSyncPermissions
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, parts of Bluetooth LE and WiFi Direct require location permission. Ditto will operate without it but syncronization may be impossible in certain scenarios.
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // We ignore the result - Ditto will automatically notice when the permission is granted String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION}; ActivityCompat.requestPermissions(this, permissions, 0);}
For more information about requesting permissions in a user-friendly way refer to Android's documentation: Request App Permissions.
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()}