Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 9 additions & 12 deletions android-kotlin/QuickStartTasks/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,26 @@ androidComponents {
)

buildConfigFields.forEach { (key, description) ->
val rawValue = prop[key]?.toString()?.trim('"') ?: ""
it.buildConfigFields.put(
key,
BuildConfigField("String", "\"${prop[key]}\"", description)
BuildConfigField("String", "\"$rawValue\"", description)
)
}
}
}

android {
namespace = "live.ditto.quickstart.tasks"
compileSdk = 35
compileSdk = 36

lint {
baseline = file("lint-baseline.xml")
}

defaultConfig {
applicationId = "live.ditto.quickstart.tasks"
minSdk = 23
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "1.0"
Expand All @@ -83,23 +84,19 @@ android {
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
}

buildFeatures {
buildConfig = true
compose = true
}

composeOptions {
kotlinCompilerExtensionVersion = "1.5.14"
}

packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
Expand All @@ -111,6 +108,7 @@ dependencies {
// Core Android
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.datastore.preferences)
Expand All @@ -122,7 +120,6 @@ dependencies {
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.runtime.livedata)

// Dependency Injection
implementation(platform(libs.koin.bom))
Expand All @@ -132,7 +129,7 @@ dependencies {
implementation(libs.koin.androidx.compose.navigation)

// Ditto SDK
implementation(libs.live.ditto)
implementation(libs.com.ditto)

// Testing
testImplementation(libs.junit)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
package live.ditto.quickstart.tasks

import live.ditto.*
import com.ditto.kotlin.*

class DittoHandler {
companion object {
lateinit var ditto: Ditto
private set

fun initialize(config: DittoConfig) {
if (::ditto.isInitialized) {
throw IllegalStateException("Ditto is already initialized")
}
ditto = DittoFactory.create(config = config)
}

val isInitialized: Boolean
get() = ::ditto.isInitialized
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,14 @@ package live.ditto.quickstart.tasks
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import live.ditto.transports.DittoSyncPermissions
import android.os.StrictMode
import androidx.activity.enableEdgeToEdge
import com.ditto.kotlin.transports.DittoSyncPermissions

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)

if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.penaltyLog() // Log violations to logcat
.build()
)
}

setContent {
Root()
}
Expand All @@ -28,14 +19,11 @@ class MainActivity : ComponentActivity() {
}

private fun requestMissingPermissions() {
// requesting permissions at runtime
// Requesting permissions at runtime
// https://docs.ditto.live/sdk/latest/install-guides/kotlin#requesting-permissions-at-runtime
val missingPermissions = DittoSyncPermissions(this).missingPermissions()
if (missingPermissions.isNotEmpty()) {
this.requestPermissions(missingPermissions, 0)
}
}
}



Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,14 @@ package live.ditto.quickstart.tasks

import android.app.Application
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import live.ditto.Ditto
import live.ditto.DittoIdentity
import live.ditto.android.DefaultAndroidDittoDependencies
import live.ditto.quickstart.tasks.DittoHandler.Companion.ditto
import com.ditto.kotlin.DittoAuthenticationProvider
import com.ditto.kotlin.DittoConfig
import com.ditto.kotlin.DittoFactory
import com.ditto.kotlin.DittoLog

class TasksApplication : Application() {

// Create a CoroutineScope
// Use SupervisorJob so if one coroutine launched in this scope fails, it doesn't cancel the scope
//
// https://developer.android.com/kotlin/coroutines/coroutines-adv
// Dispatchers.IO - This dispatcher is optimized to perform disk or network I/O outside of the main thread.
private val ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

private val tag = "TaskApplication"

companion object {
private var instance: TasksApplication? = null
Expand All @@ -35,43 +25,47 @@ class TasksApplication : Application() {

override fun onCreate() {
super.onCreate()
ioScope.launch {
setupDitto()
}
initializeDitto()
setupAuthentication()
}

private suspend fun setupDitto() {
val androidDependencies = DefaultAndroidDittoDependencies(applicationContext)

//read values from build.gradle.kts (Module:app) which reads from environment file
val appId = BuildConfig.DITTO_APP_ID
val token = BuildConfig.DITTO_PLAYGROUND_TOKEN
val authUrl = BuildConfig.DITTO_AUTH_URL
val webSocketURL = BuildConfig.DITTO_WEBSOCKET_URL

val enableDittoCloudSync = false
private fun initializeDitto() {
try {
val config = DittoConfig(
databaseId = BuildConfig.DITTO_APP_ID,
connect = DittoConfig.Connect.Server(url = BuildConfig.DITTO_AUTH_URL)
)

/*
* Setup Ditto Identity
* https://docs.ditto.live/sdk/latest/install-guides/kotlin#integrating-and-initializing
*/
val identity = DittoIdentity.OnlinePlayground(
dependencies = androidDependencies,
appId = appId,
token = token,
customAuthUrl = authUrl,
enableDittoCloudSync = enableDittoCloudSync // This is required to be set to false to use the correct URLs
)

ditto = Ditto(androidDependencies, identity)
ditto.updateTransportConfig { config ->
// Set the Ditto Websocket URL
config.connect.websocketUrls.add(webSocketURL)
DittoHandler.initialize(config)
DittoLog.d(tag, "Ditto instance created successfully")
} catch (ex: Throwable) {
DittoLog.e(tag, "Failed to initialize Ditto: $ex")
throw ex
}
}

ditto.store.execute("ALTER SYSTEM SET DQL_STRICT_MODE = false")
private fun setupAuthentication() {
try {
val token = BuildConfig.DITTO_PLAYGROUND_TOKEN

// disable sync with v3 peers, required for DQL
ditto.disableSyncWithV3()
// Set the expiration handler before starting sync
// https://docs.ditto.live/sdk/latest/sync/authentication
DittoHandler.ditto.auth?.let { auth ->
auth.expirationHandler = { ditto, _ ->
try {
val clientInfo = ditto.auth?.login(
token = token,
provider = DittoAuthenticationProvider.development()
)
DittoLog.d(tag, "Auth response: $clientInfo")
} catch (ex: Throwable) {
DittoLog.e(tag, "Authentication failed: $ex")
}
}
}
DittoLog.d(tag, "Ditto authentication setup complete")
} catch (ex: Throwable) {
DittoLog.e(tag, "Failed to setup authentication: $ex")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ data class Task(
val done: Boolean = false,
val deleted: Boolean = false,
) {
fun toMap(): Map<String, Any?> = mapOf(
"_id" to _id,
"title" to title,
"done" to done,
"deleted" to deleted
)

companion object {
private const val TAG = "Task"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import live.ditto.quickstart.tasks.R
Expand All @@ -26,9 +26,9 @@ fun EditScreen(navController: NavController, taskId: String?) {

val topBarTitle = if (taskId == null) "New Task" else "Edit Task"

val title: String by editScreenViewModel.title.observeAsState("")
val done: Boolean by editScreenViewModel.done.observeAsState(initial = false)
val canDelete: Boolean by editScreenViewModel.canDelete.observeAsState(initial = false)
val title: String by editScreenViewModel.title.collectAsStateWithLifecycle()
val done: Boolean by editScreenViewModel.done.collectAsStateWithLifecycle()
val canDelete: Boolean by editScreenViewModel.canDelete.collectAsStateWithLifecycle()

Scaffold(
topBar = {
Expand All @@ -46,9 +46,9 @@ fun EditScreen(navController: NavController, taskId: String?) {
EditForm(
canDelete = canDelete,
title = title,
onTitleTextChange = { editScreenViewModel.title.value = it },
onTitleTextChange = { editScreenViewModel.setTitle(it) },
done = done,
onDoneChanged = { editScreenViewModel.done.value = it },
onDoneChanged = { editScreenViewModel.setDone(it) },
onSaveButtonClicked = {
editScreenViewModel.save()
navController.popBackStack()
Expand Down
Loading
Loading