diff --git a/android-java/README.md b/android-java/README.md
index 73e210320..ad34bf0d7 100644
--- a/android-java/README.md
+++ b/android-java/README.md
@@ -13,7 +13,7 @@ After you have completed the [common prerequisites] you will need the following:
## Documentation
- [Install Guide](https://docs.ditto.live/sdk/latest/install-guides/java/android)
-- [API Reference](https://software.ditto.live/android/Ditto/4.11.1/api-reference/)
+- [API Reference](https://software.ditto.live/android/Ditto/5.0.0/api-reference/)
- [SDK Release Notes](https://docs.ditto.live/sdk/latest/release-notes/java)
[common prerequisites]: https://github.com/getditto/quickstart#common-prerequisites
@@ -23,8 +23,8 @@ After you have completed the [common prerequisites] you will need the following:
Assuming you have Android Studio and other prerequisites installed, you can
build and run the app by following these steps:
-1. Create an application at . Make note of the app ID and online playground token.
-2. Copy the `.env.sample` file at the top level of the `quickstart` repo to `.env` and add your App ID, Online Playground Token, Auth URL, and Websocket URL.
+1. Create an application at . Make note of the database ID (used to be called app ID) and online playground token.
+2. Copy the `.env.sample` file at the top level of the `quickstart` repo to `.env` and add your Database ID (used to be called AppId), Online Playground Token, and Auth URL.
3. Launch Android Studio and open the `quickstart/android-java` directory.
4. In Android Studio, select a connected Android device, or create and launch an Android emulator and select it as the destination, then choose the **Run > Run 'app'** menu item.
@@ -36,8 +36,9 @@ Compatible with Android Automotive OS (AAOS)
## A Guided Tour of the Android App Source Code
-The Android app is a simple to-do list app that demonstrates how to use the Ditto Android SDK to sync data with other devices.
-It is implemented using Java and Android Views using an Activity and a programmatically implemented RecyclerView.
+The Android app is a simple to-do list app that demonstrates how to use the Ditto Android SDK to sync data with other devices. It is implemented using Java and Android Views using an Activity and a programmatically implemented RecyclerView.
+
+The Ditto v5 SDK ships as a Kotlin module (`com.ditto:ditto-kotlin-android`) and exposes some APIs as `suspend` functions. To keep the application code idiomatic Java, this project uses a small Kotlin bridge file, `DittoHelper.kt`, that wraps the suspending APIs with `runBlocking` and exposes `@JvmStatic` entry points. All application logic — `MainActivity`, `Task`, and `TaskAdapter` — remains in Java.
It is assumed that the reader is familiar with Android development and with Java/Activity/RecyclerView, but needs some guidance on how to use Ditto. The following is a summary of the key parts of integration with Ditto.
@@ -54,7 +55,7 @@ This line in `gradle/libs.versions.toml` specifies which version of the Ditto
SDK to use:
```kotlin
-ditto = "4.11.1"
+ditto = "5.0.0"
```
To use a newer version of the SDK, change the version number on this line.
diff --git a/android-java/app/build.gradle.kts b/android-java/app/build.gradle.kts
index 125486953..11840d22c 100644
--- a/android-java/app/build.gradle.kts
+++ b/android-java/app/build.gradle.kts
@@ -1,12 +1,11 @@
import com.android.build.api.variant.BuildConfigField
import java.io.FileInputStream
-import java.io.FileNotFoundException
import java.util.Properties
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
- alias(libs.plugins.kotlin.compose)
}
fun loadEnvProperties(): Properties {
@@ -19,8 +18,7 @@ fun loadEnvProperties(): Properties {
val requiredEnvVars = listOf(
"DITTO_APP_ID",
"DITTO_PLAYGROUND_TOKEN",
- "DITTO_AUTH_URL",
- "DITTO_WEBSOCKET_URL"
+ "DITTO_AUTH_URL"
)
for (envVar in requiredEnvVars) {
@@ -33,11 +31,14 @@ fun loadEnvProperties(): Properties {
}
// Define BuildConfig.DITTO_APP_ID, BuildConfig.DITTO_PLAYGROUND_TOKEN,
-// BuildConfig.DITTO_CUSTOM_AUTH_URL, BuildConfig.DITTO_WEBSOCKET_URL
-// based on values in the .env file
+// and BuildConfig.DITTO_AUTH_URL based on values in the .env file
//
// More information can be found here:
// https://docs.ditto.live/sdk/latest/install-guides/java/android#integrating-and-initializing
+fun envValue(prop: Properties, key: String): String {
+ return prop[key]?.toString()?.trim('"') ?: ""
+}
+
androidComponents {
onVariants {
val prop = loadEnvProperties()
@@ -45,7 +46,7 @@ androidComponents {
"DITTO_APP_ID",
BuildConfigField(
"String",
- "\"${prop["DITTO_APP_ID"]}\"",
+ "\"${envValue(prop, "DITTO_APP_ID")}\"",
"Ditto application ID"
)
)
@@ -53,7 +54,7 @@ androidComponents {
"DITTO_PLAYGROUND_TOKEN",
BuildConfigField(
"String",
- "\"${prop["DITTO_PLAYGROUND_TOKEN"]}\"",
+ "\"${envValue(prop, "DITTO_PLAYGROUND_TOKEN")}\"",
"Ditto online playground authentication token"
)
)
@@ -62,19 +63,10 @@ androidComponents {
"DITTO_AUTH_URL",
BuildConfigField(
"String",
- "\"${prop["DITTO_AUTH_URL"]}\"",
+ "\"${envValue(prop, "DITTO_AUTH_URL")}\"",
"Ditto Auth URL"
)
)
-
- it.buildConfigFields.put(
- "DITTO_WEBSOCKET_URL",
- BuildConfigField(
- "String",
- "\"${prop["DITTO_WEBSOCKET_URL"]}\"",
- "Ditto Websocket URL"
- )
- )
}
}
@@ -86,7 +78,7 @@ android {
defaultConfig {
applicationId = "com.example.dittotasks"
minSdk = 24
- targetSdk = 34
+ targetSdk = 35
versionCode = 1
versionName = "1.0"
@@ -109,12 +101,8 @@ android {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
- kotlinOptions {
- jvmTarget = "11"
- }
buildFeatures {
buildConfig = true
- compose = true
}
// This ensures Ditto can produce meaningful stack traces
packaging {
@@ -125,16 +113,16 @@ android {
}
}
+kotlin {
+ compilerOptions {
+ jvmTarget.set(JvmTarget.JVM_11)
+ }
+}
+
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.activity.compose)
- implementation(platform(libs.androidx.compose.bom))
- implementation(libs.androidx.ui)
- implementation(libs.androidx.ui.graphics)
- implementation(libs.androidx.ui.tooling.preview)
- implementation(libs.androidx.material3)
implementation(libs.ditto)
implementation(libs.androidx.recyclerview)
implementation(libs.material)
@@ -143,8 +131,4 @@ dependencies {
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.6.1")
- androidTestImplementation(platform(libs.androidx.compose.bom))
- androidTestImplementation(libs.androidx.ui.test.junit4)
- debugImplementation(libs.androidx.ui.tooling)
- debugImplementation(libs.androidx.ui.test.manifest)
}
diff --git a/android-java/app/src/main/java/com/example/dittotasks/DittoHelper.kt b/android-java/app/src/main/java/com/example/dittotasks/DittoHelper.kt
new file mode 100644
index 000000000..bf3a6e3c2
--- /dev/null
+++ b/android-java/app/src/main/java/com/example/dittotasks/DittoHelper.kt
@@ -0,0 +1,75 @@
+package com.example.dittotasks
+
+import com.ditto.kotlin.Ditto
+import com.ditto.kotlin.DittoAuthenticationProvider
+import com.ditto.kotlin.DittoConfig
+import com.ditto.kotlin.DittoException
+import com.ditto.kotlin.DittoFactory
+import com.ditto.kotlin.DittoQueryResult
+import com.ditto.kotlin.DittoStoreObserver
+import com.ditto.kotlin.DittoSyncSubscription
+import kotlinx.coroutines.runBlocking
+import java.util.function.Consumer
+
+/**
+ * Bridges Ditto v5 Kotlin SDK suspend functions for Java callers.
+ */
+object DittoHelper {
+
+ @JvmStatic
+ fun createDitto(appId: String, serverUrl: String): Ditto {
+ val config = DittoConfig(
+ databaseId = appId,
+ connect = DittoConfig.Connect.Server(serverUrl)
+ )
+ return DittoFactory.create(config)
+ }
+
+ @JvmStatic
+ fun setupAuth(ditto: Ditto, token: String) {
+ ditto.auth?.let { auth ->
+ auth.expirationHandler = { dittoInstance, _ ->
+ dittoInstance.auth?.login(token, DittoAuthenticationProvider.development())
+ }
+ }
+ }
+
+ @JvmStatic
+ @Throws(DittoException::class)
+ fun execute(ditto: Ditto, query: String, args: Map) {
+ runBlocking {
+ ditto.store.execute(query, args)
+ }
+ }
+
+ @JvmStatic
+ fun registerSubscription(ditto: Ditto, query: String): DittoSyncSubscription {
+ return ditto.sync.registerSubscription(query)
+ }
+
+ @JvmStatic
+ fun registerObserver(
+ ditto: Ditto,
+ query: String,
+ callback: Consumer
+ ): DittoStoreObserver {
+ return ditto.store.registerObserver(query) { result ->
+ callback.accept(result)
+ }
+ }
+
+ @JvmStatic
+ fun startSync(ditto: Ditto) {
+ ditto.sync.start()
+ }
+
+ @JvmStatic
+ fun stopSync(ditto: Ditto) {
+ ditto.sync.stop()
+ }
+
+ @JvmStatic
+ fun isSyncActive(ditto: Ditto): Boolean {
+ return ditto.sync.isActive
+ }
+}
diff --git a/android-java/app/src/main/java/com/example/dittotasks/MainActivity.java b/android-java/app/src/main/java/com/example/dittotasks/MainActivity.java
index f06dce793..5df56efc9 100644
--- a/android-java/app/src/main/java/com/example/dittotasks/MainActivity.java
+++ b/android-java/app/src/main/java/com/example/dittotasks/MainActivity.java
@@ -1,7 +1,10 @@
package com.example.dittotasks;
+import android.Manifest;
import android.app.AlertDialog;
+import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -15,25 +18,19 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.ditto.kotlin.Ditto;
+import com.ditto.kotlin.DittoStoreObserver;
+import com.ditto.kotlin.DittoSyncSubscription;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
-import kotlin.Unit;
-import live.ditto.Ditto;
-import live.ditto.DittoDependencies;
-import live.ditto.DittoError;
-import live.ditto.DittoIdentity;
-import live.ditto.DittoStoreObserver;
-import live.ditto.DittoSyncSubscription;
-import live.ditto.android.DefaultAndroidDittoDependencies;
-import live.ditto.transports.DittoSyncPermissions;
-import live.ditto.transports.DittoTransportConfig;
-
public class MainActivity extends ComponentActivity {
private TaskAdapter taskAdapter;
private SwitchCompat syncSwitch;
@@ -42,13 +39,9 @@ public class MainActivity extends ComponentActivity {
DittoSyncSubscription taskSubscription;
DittoStoreObserver taskObserver;
- private String DITTO_APP_ID = BuildConfig.DITTO_APP_ID;
- private String DITTO_PLAYGROUND_TOKEN = BuildConfig.DITTO_PLAYGROUND_TOKEN;
- private String DITTO_AUTH_URL = BuildConfig.DITTO_AUTH_URL;
- private String DITTO_WEBSOCKET_URL = BuildConfig.DITTO_WEBSOCKET_URL;
-
- // This is required to be set to false to use the correct URLs
- private Boolean DITTO_ENABLE_CLOUD_SYNC = true;
+ private final String dittoAppId = BuildConfig.DITTO_APP_ID;
+ private final String dittoPlaygroundToken = BuildConfig.DITTO_PLAYGROUND_TOKEN;
+ private final String dittoAuthUrl = BuildConfig.DITTO_AUTH_URL;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -65,10 +58,10 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
// Populate connection info (only in debug builds)
if(BuildConfig.DEBUG) {
TextView appId = findViewById(R.id.ditto_app_id);
- appId.setText(String.format("App ID: %s", DITTO_APP_ID));
+ appId.setText(String.format("App ID: %s", dittoAppId));
TextView playgroundToken = findViewById(R.id.ditto_playground_token);
- playgroundToken.setText(String.format("Playground Token: %s", DITTO_PLAYGROUND_TOKEN));
+ playgroundToken.setText(String.format("Playground Token: %s", dittoPlaygroundToken));
} else {
// Hide credential views in production
findViewById(R.id.ditto_app_id).setVisibility(View.GONE);
@@ -98,22 +91,17 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
taskAdapter.setOnTaskLongPressListener(this::showEditTaskModal);
// Initialize empty list - Ditto observer will populate it
- taskAdapter.setTasks(List.of());
+ taskAdapter.setTasks(Collections.emptyList());
}
void initDitto() {
Log.d("DittoInit", "=== Starting Ditto initialization ===");
-
- // Enable Ditto's internal debug logging (if available)
- Log.d("DittoInit", "Ditto Logger class not available in this version, using Android Log instead");
-
- Log.d("DittoInit", "DITTO_APP_ID: " + DITTO_APP_ID);
- Log.d("DittoInit", "DITTO_PLAYGROUND_TOKEN: " + (DITTO_PLAYGROUND_TOKEN != null ? "Present" : "NULL"));
- Log.d("DittoInit", "DITTO_AUTH_URL: " + DITTO_AUTH_URL);
- Log.d("DittoInit", "DITTO_WEBSOCKET_URL: " + DITTO_WEBSOCKET_URL);
- Log.d("DittoInit", "DITTO_ENABLE_CLOUD_SYNC: " + DITTO_ENABLE_CLOUD_SYNC);
-
+
+ Log.d("DittoInit", "DITTO_APP_ID: " + dittoAppId);
+ Log.d("DittoInit", "DITTO_PLAYGROUND_TOKEN: " + (dittoPlaygroundToken != null ? "Present" : "NULL"));
+ Log.d("DittoInit", "DITTO_AUTH_URL: " + dittoAuthUrl);
+
// Skip permission requests during testing to avoid permission dialogs
if (!isInstrumentationTest()) {
Log.d("DittoInit", "Requesting permissions...");
@@ -124,78 +112,43 @@ void initDitto() {
Log.d("DittoInit", "Starting Ditto SDK initialization...");
try {
- Log.d("DittoInit", "Creating AndroidDependencies...");
- DittoDependencies androidDependencies = new DefaultAndroidDittoDependencies(getApplicationContext());
- Log.d("DittoInit", "AndroidDependencies created successfully");
-
- /*
- * Setup Ditto Identity
- * https://docs.ditto.live/sdk/latest/install-guides/java#integrating-and-initializing
- */
- Log.d("DittoInit", "Creating DittoIdentity.OnlinePlayground...");
- var identity = new DittoIdentity
- .OnlinePlayground(
- androidDependencies,
- DITTO_APP_ID,
- DITTO_PLAYGROUND_TOKEN,
- DITTO_ENABLE_CLOUD_SYNC, // This is required to be set to false to use the correct URLs
- DITTO_AUTH_URL);
- Log.d("DittoInit", "DittoIdentity created successfully");
-
+ // Create Ditto with server connection
+ // https://docs.ditto.live/sdk/latest/install-guides/java#integrating-and-initializing
Log.d("DittoInit", "Creating Ditto instance...");
- ditto = new Ditto(androidDependencies, identity);
+ ditto = DittoHelper.createDitto(dittoAppId, dittoAuthUrl);
Log.d("DittoInit", "Ditto instance created successfully");
- //https://docs.ditto.live/sdk/latest/sync/customizing-transport-configurations
- Log.d("DittoInit", "Updating transport config...");
- ditto.updateTransportConfig(config -> {
- config.getConnect().getWebsocketUrls().add(DITTO_WEBSOCKET_URL);
-
- // lambda must return Kotlin Unit which corresponds to 'void' in Java
- return kotlin.Unit.INSTANCE;
- });
- Log.d("DittoInit", "Transport config updated");
-
- // disable sync with v3 peers, required for DQL
- Log.d("DittoInit", "Disabling sync with v3...");
- ditto.disableSyncWithV3();
- Log.d("DittoInit", "Sync with v3 disabled");
-
- // Disable DQL strict mode
- // when set to false, collection definitions are no longer required. SELECT queries will return and display all fields by default.
- // https://docs.ditto.live/dql/strict-mode
- Log.d("DittoInit", "Setting DQL strict mode to false...");
- ditto.store.execute("ALTER SYSTEM SET DQL_STRICT_MODE = false");
- Log.d("DittoInit", "DQL strict mode disabled");
+ // Set up authentication handler (must be set before sync.start())
+ Log.d("DittoInit", "Setting up authentication...");
+ DittoHelper.setupAuth(ditto, dittoPlaygroundToken);
+ Log.d("DittoInit", "Authentication configured");
// register subscription
// https://docs.ditto.live/sdk/latest/sync/syncing-data#creating-subscriptions
Log.d("DittoInit", "Registering subscription...");
- taskSubscription = ditto.sync.registerSubscription("SELECT * FROM tasks");
+ taskSubscription = DittoHelper.registerSubscription(ditto, "SELECT * FROM tasks");
Log.d("DittoInit", "Subscription registered");
// register observer for live query
// https://docs.ditto.live/sdk/latest/crud/observing-data-changes#setting-up-store-observers
Log.d("DittoInit", "Registering observer...");
- taskObserver = ditto.store.registerObserver("SELECT * FROM tasks WHERE deleted=false ORDER BY title ASC", null, result -> {
- Log.d("DittoInit", "Observer callback triggered with " + result.getItems().size() + " items");
- var tasks = result.getItems().stream().map(Task::fromQueryItem).collect(Collectors.toCollection(ArrayList::new));
- runOnUiThread(() -> {
- Log.d("DittoInit", "Updating UI with " + tasks.size() + " tasks");
- taskAdapter.setTasks(new ArrayList<>(tasks));
- });
- return Unit.INSTANCE;
- });
+ taskObserver = DittoHelper.registerObserver(ditto,
+ "SELECT * FROM tasks WHERE deleted=false ORDER BY title ASC",
+ result -> {
+ Log.d("DittoInit", "Observer callback triggered with " + result.getItems().size() + " items");
+ var tasks = result.getItems().stream().map(Task::fromQueryItem).collect(Collectors.toCollection(ArrayList::new));
+ runOnUiThread(() -> {
+ Log.d("DittoInit", "Updating UI with " + tasks.size() + " tasks");
+ taskAdapter.setTasks(new ArrayList<>(tasks));
+ });
+ });
Log.d("DittoInit", "Observer registered");
Log.d("DittoInit", "Starting Ditto sync...");
- ditto.startSync();
+ DittoHelper.startSync(ditto);
Log.d("DittoInit", "=== Ditto initialization completed successfully ===");
- } catch (DittoError e) {
- Log.e("DittoInit", "DittoError during initialization: " + e.getMessage(), e);
- e.printStackTrace();
} catch (Exception e) {
- Log.e("DittoInit", "Unexpected error during Ditto initialization: " + e.getMessage(), e);
+ Log.e("DittoInit", "Error during Ditto initialization: " + e.getMessage(), e);
e.printStackTrace();
}
}
@@ -213,41 +166,69 @@ private boolean isInstrumentationTest() {
// Request permissions for Ditto
// https://docs.ditto.live/sdk/latest/install-guides/java#requesting-permissions-at-runtime
void requestPermissions() {
- DittoSyncPermissions permissions = new DittoSyncPermissions(this);
- String[] missing = permissions.missingPermissions(permissions.requiredPermissions());
+ List permissions = new ArrayList<>();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ permissions.add(Manifest.permission.BLUETOOTH_ADVERTISE);
+ permissions.add(Manifest.permission.BLUETOOTH_CONNECT);
+ permissions.add(Manifest.permission.BLUETOOTH_SCAN);
+ }
+ if (Build.VERSION.SDK_INT <= 32) {
+ permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
+ }
+ if (Build.VERSION.SDK_INT <= 30) {
+ permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ permissions.add(Manifest.permission.NEARBY_WIFI_DEVICES);
+ }
+
+ String[] missing = permissions.stream()
+ .filter(p -> checkSelfPermission(p) != PackageManager.PERMISSION_GRANTED)
+ .toArray(String[]::new);
+
if (missing.length > 0) {
this.requestPermissions(missing, 0);
}
}
private void createTask(String title) {
+ if (ditto == null) {
+ Log.i("MainActivity", "Ditto disabled - create task ignored: " + title);
+ return;
+ }
+
HashMap task = new HashMap<>();
task.put("title", title);
task.put("done", false);
task.put("deleted", false);
- HashMap args = new HashMap<>();
- args.put("task", task);
- try {
+ Map args = Collections.singletonMap("task", task);
+ try {
// Add tasks into the ditto collection using DQL INSERT statement
// https://docs.ditto.live/sdk/latest/crud/write#inserting-documents
- ditto.store.execute("INSERT INTO tasks DOCUMENTS (:task)", args);
- } catch (DittoError e) {
+ DittoHelper.execute(ditto, "INSERT INTO tasks DOCUMENTS (:task)", args);
+ } catch (Exception e) {
e.printStackTrace();
}
}
private void editTaskTitle(Task task, String newTitle) {
- HashMap args = new HashMap<>();
+ if (ditto == null) {
+ Log.i("MainActivity", "Ditto disabled - edit task ignored: " + task.getTitle());
+ return;
+ }
+
+ Map args = new HashMap<>();
args.put("id", task.getId());
args.put("title", newTitle);
try {
// Update tasks into the ditto collection using DQL UPDATE statement
// https://docs.ditto.live/sdk/latest/crud/update#updating
- ditto.store.execute("UPDATE tasks SET title=:title WHERE _id=:id", args);
- } catch (DittoError e) {
+ DittoHelper.execute(ditto, "UPDATE tasks SET title=:title WHERE _id=:id", args);
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -257,16 +238,16 @@ private void toggleTask(Task task) {
Log.i("MainActivity", "Ditto disabled - toggle task ignored: " + task.getTitle());
return;
}
-
- HashMap args = new HashMap<>();
+
+ Map args = new HashMap<>();
args.put("id", task.getId());
args.put("done", !task.isDone());
try {
// Update tasks into the ditto collection using DQL UPDATE statement
// https://docs.ditto.live/sdk/latest/crud/update#updating
- ditto.store.execute("UPDATE tasks SET done=:done WHERE _id=:id", args);
- } catch (DittoError e) {
+ DittoHelper.execute(ditto, "UPDATE tasks SET done=:done WHERE _id=:id", args);
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -276,14 +257,14 @@ private void deleteTask(Task task) {
Log.i("MainActivity", "Ditto disabled - delete task ignored: " + task.getTitle());
return;
}
-
- HashMap args = new HashMap<>();
- args.put("id", task.getId());
+
+ Map args = Collections.singletonMap("id", task.getId());
+
try {
// UPDATE DQL Statement using Soft-Delete pattern
// https://docs.ditto.live/sdk/latest/crud/delete#soft-delete-pattern
- ditto.store.execute("UPDATE tasks SET deleted=true WHERE _id=:id", args);
- } catch (DittoError e) {
+ DittoHelper.execute(ditto, "UPDATE tasks SET deleted=true WHERE _id=:id", args);
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -293,7 +274,7 @@ private void toggleSync() {
return;
}
- boolean isSyncActive = ditto.isSyncActive();
+ boolean isSyncActive = DittoHelper.isSyncActive(ditto);
var nextColor = isSyncActive ? null : ColorStateList.valueOf(0xFFBB86FC);
var nextText = isSyncActive ? "Sync Inactive" : "Sync Active";
@@ -301,14 +282,14 @@ private void toggleSync() {
// https://docs.ditto.live/sdk/latest/sync/start-and-stop-sync
try {
if (isSyncActive) {
- ditto.stopSync();
+ DittoHelper.stopSync(ditto);
} else {
- ditto.startSync();
+ DittoHelper.startSync(ditto);
}
syncSwitch.setChecked(!isSyncActive);
syncSwitch.setTrackTintList(nextColor);
syncSwitch.setText(nextText);
- } catch (DittoError e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
diff --git a/android-java/app/src/main/java/com/example/dittotasks/Task.java b/android-java/app/src/main/java/com/example/dittotasks/Task.java
index f9f71d105..a48450ebf 100644
--- a/android-java/app/src/main/java/com/example/dittotasks/Task.java
+++ b/android-java/app/src/main/java/com/example/dittotasks/Task.java
@@ -1,37 +1,42 @@
package com.example.dittotasks;
-import java.util.Optional;
+import com.ditto.kotlin.DittoQueryResultItem;
-import live.ditto.DittoQueryResultItem;
+import org.json.JSONException;
+import org.json.JSONObject;
public class Task {
- private Optional id;
- private String title;
- private boolean done;
- private boolean deleted;
+ private final String id;
+ private final String title;
+ private final boolean done;
+ private final boolean deleted;
public Task(String title) {
this(null, title, false, false);
}
public Task(String id, String title, boolean done, boolean deleted) {
- this.id = Optional.ofNullable(id);
+ this.id = id;
this.title = title;
this.done = done;
this.deleted = deleted;
}
public static Task fromQueryItem(DittoQueryResultItem item) {
- var map = item.getValue();
- return new Task(
- (String) map.get("_id"),
- (String) map.get("title"),
- Boolean.TRUE.equals(map.get("done")),
- Boolean.TRUE.equals(map.get("deleted")));
+ try {
+ JSONObject json = new JSONObject(item.jsonString());
+ return new Task(
+ json.isNull("_id") ? null : json.optString("_id", null),
+ json.isNull("title") ? null : json.optString("title", null),
+ json.optBoolean("done", false),
+ json.optBoolean("deleted", false));
+ } catch (JSONException e) {
+ throw new RuntimeException("Failed to parse task from query result", e);
+ }
}
public String getId() {
- return id.orElse(null);
+ return id;
}
public String getTitle() {
diff --git a/android-java/app/src/test/java/com/example/dittotasks/ExampleUnitTest.java b/android-java/app/src/test/java/com/example/dittotasks/ExampleUnitTest.java
new file mode 100644
index 000000000..fcd67efe3
--- /dev/null
+++ b/android-java/app/src/test/java/com/example/dittotasks/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.example.dittotasks;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
diff --git a/android-java/app/src/test/java/com/example/dittotasks/ExampleUnitTest.kt b/android-java/app/src/test/java/com/example/dittotasks/ExampleUnitTest.kt
deleted file mode 100644
index 835cd961c..000000000
--- a/android-java/app/src/test/java/com/example/dittotasks/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.example.dittotasks
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file
diff --git a/android-java/build.gradle.kts b/android-java/build.gradle.kts
index 952b93066..922f55110 100644
--- a/android-java/build.gradle.kts
+++ b/android-java/build.gradle.kts
@@ -2,5 +2,4 @@
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
- alias(libs.plugins.kotlin.compose) apply false
}
\ No newline at end of file
diff --git a/android-java/gradle/libs.versions.toml b/android-java/gradle/libs.versions.toml
index 3cb010091..ef0498e31 100644
--- a/android-java/gradle/libs.versions.toml
+++ b/android-java/gradle/libs.versions.toml
@@ -1,41 +1,25 @@
[versions]
-ditto = "4.14.3"
+ditto = "5.0.0"
agp = "8.7.3"
-constraintlayout = "2.2.0"
kotlin = "2.0.0"
coreKtx = "1.10.1"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.6.1"
-activityCompose = "1.8.0"
-composeBom = "2024.04.01"
material = "1.12.0"
recyclerview = "1.3.2"
-recyclerviewV7 = "28.0.0"
[libraries]
-androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
-ditto = { module = "live.ditto:ditto", version.ref = "ditto" }
+ditto = { group = "com.ditto", name = "ditto-kotlin-android", version.ref = "ditto" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
-androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
-androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
-androidx-ui = { group = "androidx.compose.ui", name = "ui" }
-androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
-androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
-androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
-androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
-androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
-androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerview" }
material = { module = "com.google.android.material:material", version.ref = "material" }
-recyclerview-v7 = { module = "com.android.support:recyclerview-v7", version.ref = "recyclerviewV7" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
-kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }