diff --git a/android-cpp/QuickStartTasksCPP/app/build.gradle.kts b/android-cpp/QuickStartTasksCPP/app/build.gradle.kts index 6a60dff9f..3370a2eae 100644 --- a/android-cpp/QuickStartTasksCPP/app/build.gradle.kts +++ b/android-cpp/QuickStartTasksCPP/app/build.gradle.kts @@ -158,5 +158,6 @@ dependencies { debugImplementation(libs.androidx.ui.test.manifest) // Ditto C++ SDK - implementation("live.ditto:ditto-cpp:4.10.0") + implementation("live.ditto:ditto-cpp:4.10.2") + } diff --git a/android-cpp/QuickStartTasksCPP/app/src/main/cpp/tasks_peer.cpp b/android-cpp/QuickStartTasksCPP/app/src/main/cpp/tasks_peer.cpp index f73032ebe..b7b318817 100644 --- a/android-cpp/QuickStartTasksCPP/app/src/main/cpp/tasks_peer.cpp +++ b/android-cpp/QuickStartTasksCPP/app/src/main/cpp/tasks_peer.cpp @@ -131,7 +131,7 @@ class TasksPeer::Impl { // NOLINT(cppcoreguidelines-special-member-functions) ditto->start_sync(); tasks_subscription = - ditto->sync().register_subscription("SELECT * FROM tasks"); + ditto->get_sync().register_subscription("SELECT * FROM tasks"); } void stop_sync() { @@ -139,8 +139,10 @@ class TasksPeer::Impl { // NOLINT(cppcoreguidelines-special-member-functions) return; } - tasks_subscription->cancel(); - tasks_subscription.reset(); + if (tasks_subscription) { + tasks_subscription->cancel(); + tasks_subscription.reset(); + } ditto->stop_sync(); } diff --git a/android-cpp/QuickStartTasksCPP/app/src/main/cpp/taskslib.cpp b/android-cpp/QuickStartTasksCPP/app/src/main/cpp/taskslib.cpp index 364d60398..b0dcebde0 100644 --- a/android-cpp/QuickStartTasksCPP/app/src/main/cpp/taskslib.cpp +++ b/android-cpp/QuickStartTasksCPP/app/src/main/cpp/taskslib.cpp @@ -172,7 +172,6 @@ Java_live_ditto_quickstart_tasks_TasksLib_stopSync(JNIEnv *env, jobject thiz) { throw_java_illegal_state_exception(env, "TasksLib has not been initialized"); return; } - remove_observer(env); peer->stop_sync(); } catch (const std::exception &err) { __android_log_print(ANDROID_LOG_ERROR, TAG, "stopSync failed: %s", err.what()); diff --git a/android-cpp/QuickStartTasksCPP/app/src/main/java/live/ditto/quickstart/tasks/list/TasksListScreen.kt b/android-cpp/QuickStartTasksCPP/app/src/main/java/live/ditto/quickstart/tasks/list/TasksListScreen.kt index e66f9a8f3..b1bf01894 100644 --- a/android-cpp/QuickStartTasksCPP/app/src/main/java/live/ditto/quickstart/tasks/list/TasksListScreen.kt +++ b/android-cpp/QuickStartTasksCPP/app/src/main/java/live/ditto/quickstart/tasks/list/TasksListScreen.kt @@ -2,6 +2,7 @@ package live.ditto.quickstart.tasks.list import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -18,6 +19,7 @@ import androidx.compose.material3.FloatingActionButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar @@ -28,6 +30,7 @@ import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource @@ -48,6 +51,7 @@ import java.util.UUID fun TasksListScreen(navController: NavController) { val tasksListViewModel: TasksListScreenViewModel = viewModel() val tasks: List by tasksListViewModel.tasks.observeAsState(emptyList()) + val syncEnabled: Boolean by tasksListViewModel.syncEnabled.observeAsState(true) var showDeleteDialog by remember { mutableStateOf(false) } var deleteDialogTaskId by remember { mutableStateOf("") } @@ -80,7 +84,23 @@ fun TasksListScreen(navController: NavController) { colors = TopAppBarDefaults.topAppBarColors( containerColor = colorResource(id = R.color.blue_700), titleContentColor = Color.White - ) + ), + actions = { + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = "Sync", + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(end = 10.dp), + color = Color.White + ) + Switch( + checked = syncEnabled, + onCheckedChange = { isChecked -> + tasksListViewModel.setSyncEnabled(isChecked) + } + ) + } + } ) }, floatingActionButton = { diff --git a/android-cpp/QuickStartTasksCPP/app/src/main/java/live/ditto/quickstart/tasks/list/TasksListScreenViewModel.kt b/android-cpp/QuickStartTasksCPP/app/src/main/java/live/ditto/quickstart/tasks/list/TasksListScreenViewModel.kt index e52a5557b..20ce0ea65 100644 --- a/android-cpp/QuickStartTasksCPP/app/src/main/java/live/ditto/quickstart/tasks/list/TasksListScreenViewModel.kt +++ b/android-cpp/QuickStartTasksCPP/app/src/main/java/live/ditto/quickstart/tasks/list/TasksListScreenViewModel.kt @@ -1,14 +1,26 @@ package live.ditto.quickstart.tasks.list +import android.content.Context import android.util.Log +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.preferencesDataStore +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import live.ditto.quickstart.tasks.TasksApplication import live.ditto.quickstart.tasks.TasksLib import live.ditto.quickstart.tasks.TasksObserver import live.ditto.quickstart.tasks.data.Task +// The value of the Sync switch is stored in persistent settings +private val Context.preferencesDataStore by preferencesDataStore("tasks_list_settings") +private val SYNC_ENABLED_KEY = booleanPreferencesKey("sync_enabled") + class TasksListScreenViewModel : ViewModel() { companion object { @@ -24,13 +36,46 @@ class TasksListScreenViewModel : ViewModel() { } } + private val preferencesDataStore = TasksApplication.applicationContext().preferencesDataStore + val tasks: MutableLiveData> = MutableLiveData(emptyList()) private val updateHandler: UpdateHandler = UpdateHandler() + private val _syncEnabled = MutableLiveData(true) + val syncEnabled: LiveData = _syncEnabled + + fun setSyncEnabled(enabled: Boolean) { + viewModelScope.launch { + preferencesDataStore.edit { settings -> + settings[SYNC_ENABLED_KEY] = enabled + } + _syncEnabled.value = enabled + + if (enabled && !TasksLib.isSyncActive()) { + try { + TasksLib.startSync() + } catch (e: Exception) { + Log.e(TAG, "Unable to start sync", e) + } + } else if (!enabled && TasksLib.isSyncActive()) { + try { + TasksLib.stopSync() + } catch (e: Exception) { + Log.e(TAG, "Unable to stop sync", e) + } + } + } + } + init { viewModelScope.launch { TasksLib.insertInitialDocuments() + + setSyncEnabled( + preferencesDataStore.data.map { prefs -> prefs[SYNC_ENABLED_KEY] ?: true }.first() + ) + TasksLib.setTasksObserver(updateHandler) } }