From 97bb33a67900e876f42da8f55556b640796ba500 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 18 Aug 2025 19:51:35 +0100 Subject: [PATCH 1/8] feat[live-objects]: add `example` module showcasing LiveObjects features Added an Android Compose-based example application demonstrating Ably Live Objects integration. Includes tasks management and color voting screens. --- build.gradle.kts | 3 + example/.gitignore | 1 + example/README.md | 72 +++++ example/build.gradle.kts | 78 +++++ example/proguard-rules.pro | 21 ++ example/src/main/AndroidManifest.xml | 24 ++ .../java/com/ably/example/MainActivity.kt | 53 ++++ .../src/main/java/com/ably/example/Utils.kt | 284 ++++++++++++++++++ .../ably/example/screen/ColorVotingScreen.kt | 157 ++++++++++ .../com/ably/example/screen/MainScreen.kt | 58 ++++ .../example/screen/TaskManagementScreen.kt | 257 ++++++++++++++++ .../java/com/ably/example/ui/theme/Color.kt | 11 + .../java/com/ably/example/ui/theme/Theme.kt | 46 +++ .../java/com/ably/example/ui/theme/Type.kt | 34 +++ .../main/res/drawable/launch_background.xml | 12 + .../main/res/mipmap-anydpi/ic_launcher.xml | 5 + .../res/mipmap-anydpi/ic_launcher_round.xml | 5 + .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 11753 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 43259 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 18953 bytes example/src/main/res/values/colors.xml | 10 + .../res/values/ic_launcher_background.xml | 4 + example/src/main/res/values/strings.xml | 3 + example/src/main/res/values/themes.xml | 4 + gradle/libs.versions.toml | 26 +- settings.gradle.kts | 1 + 26 files changed, 1168 insertions(+), 1 deletion(-) create mode 100644 example/.gitignore create mode 100644 example/README.md create mode 100644 example/build.gradle.kts create mode 100644 example/proguard-rules.pro create mode 100644 example/src/main/AndroidManifest.xml create mode 100644 example/src/main/java/com/ably/example/MainActivity.kt create mode 100644 example/src/main/java/com/ably/example/Utils.kt create mode 100644 example/src/main/java/com/ably/example/screen/ColorVotingScreen.kt create mode 100644 example/src/main/java/com/ably/example/screen/MainScreen.kt create mode 100644 example/src/main/java/com/ably/example/screen/TaskManagementScreen.kt create mode 100644 example/src/main/java/com/ably/example/ui/theme/Color.kt create mode 100644 example/src/main/java/com/ably/example/ui/theme/Theme.kt create mode 100644 example/src/main/java/com/ably/example/ui/theme/Type.kt create mode 100644 example/src/main/res/drawable/launch_background.xml create mode 100644 example/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 example/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 example/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 example/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 example/src/main/res/values/colors.xml create mode 100644 example/src/main/res/values/ic_launcher_background.xml create mode 100644 example/src/main/res/values/strings.xml create mode 100644 example/src/main/res/values/themes.xml diff --git a/build.gradle.kts b/build.gradle.kts index c741178c7..a98b165b6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,6 +8,9 @@ plugins { alias(libs.plugins.maven.publish) apply false alias(libs.plugins.lombok) apply false alias(libs.plugins.test.retry) apply false + alias(libs.plugins.android.application) apply false + alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.kotlin.compose) apply false } subprojects { diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/example/.gitignore @@ -0,0 +1 @@ +/build diff --git a/example/README.md b/example/README.md new file mode 100644 index 000000000..b43b21095 --- /dev/null +++ b/example/README.md @@ -0,0 +1,72 @@ +# Example App using Live Objects + +This demo app showcases Ably Live Objects functionality with two interactive features: + +- **Color Voting**: Real-time voting system where users can vote for their favorite color (Red, Green, Blue) and see live vote counts synchronized across all devices +- **Task Management**: Collaborative task management where users can add, edit, and delete tasks that sync in real-time across all connected devices + +Follow the steps below to get started with the Live Objects demo app + +## Prerequisites + +Ensure you have the following installed: +- [Android Studio](https://developer.android.com/studio) (latest stable version) +- Java 17 or higher +- Android SDK with API Level 34 or higher + +Add your Ably key to the `local.properties` file: + +```properties +sdk.dir=/path/to/android/sdk + +ABLY_KEY=xxxx:yyyyyy +``` + +## Steps to Run the App + +1. Open in Android Studio + + - Open Android Studio. + - Select File > Open and navigate to the cloned repository. + - Open the project. + +2. Sync Gradle + + - Wait for Gradle to sync automatically. + - If it doesn’t, click on Sync Project with Gradle Files in the toolbar. + +3. Configure an Emulator or Device + + - Set up an emulator or connect a physical Android device. + - Ensure the device is configured with at least Android 5.0 (API 21). + +4. Run the App + + - Select your emulator or connected device in the device selector dropdown. + - Click on the Run button ▶️ in the toolbar or press Shift + F10. + +5. View the App + + Once the build is complete, the app will be installed and launched on the selected device or emulator. + +## What You'll See + +The app opens with two tabs: + +1. **Color Voting Tab**: + - Vote for Red, Green, or Blue colors + - See real-time vote counts that update instantly across all devices + - Reset all votes with the "Reset all" button + +2. **Task Management Tab**: + - Add new tasks using the text input and "Add Task" button + - Edit existing tasks by clicking the edit icon + - Delete individual tasks or remove all tasks at once + - See the total task count and real-time updates as tasks are modified + +To see the real-time synchronization in action, run the app on multiple devices or emulators with the same Ably key. + +## Troubleshooting + +- SDK Not Found: Install missing SDK versions from File > Settings > Appearance & Behavior > System Settings > Android SDK. +- Build Failures: Check the error logs and resolve dependencies or configuration issues. diff --git a/example/build.gradle.kts b/example/build.gradle.kts new file mode 100644 index 000000000..994d2bcc3 --- /dev/null +++ b/example/build.gradle.kts @@ -0,0 +1,78 @@ +import java.io.FileInputStream +import java.io.InputStreamReader +import java.util.Properties + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) +} + +android { + namespace = "com.ably.example" + compileSdk = 35 + + defaultConfig { + applicationId = "com.ably.example" + minSdk = 30 + targetSdk = 35 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + buildConfigField("String", "ABLY_KEY", "\"${getLocalProperty("ABLY_KEY") ?: ""}\"") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + compose = true + buildConfig = true + } +} + +dependencies { + implementation(libs.core.ktx) + implementation(libs.lifecycle.runtime.ktx) + implementation(libs.activity.compose) + implementation(platform(libs.compose.bom)) + implementation(libs.ui) + implementation(libs.ui.graphics) + implementation(libs.ui.tooling.preview) + implementation(libs.material3) + + implementation(project(":live-objects")) + implementation(project(":android")) + + implementation(libs.navigation.compose) + + testImplementation(libs.junit) + androidTestImplementation(libs.ext.junit) + androidTestImplementation(libs.espresso.core) + androidTestImplementation(platform(libs.compose.bom)) + androidTestImplementation(libs.ui.test.junit4) + debugImplementation(libs.ui.tooling) + debugImplementation(libs.ui.test.manifest) +} + +fun getLocalProperty(key: String, file: String = "local.properties"): String? { + val properties = Properties() + val localProperties = File(file) + if (!localProperties.isFile) return null + InputStreamReader(FileInputStream(localProperties), Charsets.UTF_8).use { reader -> + properties.load(reader) + } + return properties.getProperty(key) +} diff --git a/example/proguard-rules.pro b/example/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/example/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/example/src/main/AndroidManifest.xml b/example/src/main/AndroidManifest.xml new file mode 100644 index 000000000..d8893208c --- /dev/null +++ b/example/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/example/src/main/java/com/ably/example/MainActivity.kt b/example/src/main/java/com/ably/example/MainActivity.kt new file mode 100644 index 000000000..f0605d5a7 --- /dev/null +++ b/example/src/main/java/com/ably/example/MainActivity.kt @@ -0,0 +1,53 @@ +package com.ably.example + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import com.ably.example.screen.MainScreen +import com.ably.example.ui.theme.AblyTheme +import io.ably.lib.realtime.AblyRealtime +import io.ably.lib.types.ClientOptions +import io.ably.lib.util.Log + +class MainActivity : ComponentActivity() { + private val realtimeClient: AblyRealtime by lazy { + AblyRealtime( + ClientOptions().apply { + key = BuildConfig.ABLY_KEY + logLevel = Log.VERBOSE + autoConnect = false + } + ) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + AblyTheme { + MainScreen(realtimeClient) + } + } + } + + override fun onStart() { + super.onStart() + realtimeClient.connect() + } + + override fun onStop() { + super.onStop() + realtimeClient.close() + } + + override fun onResume() { + super.onResume() + realtimeClient.connect() + } + + override fun onPause() { + super.onPause() + realtimeClient.close() + } +} diff --git a/example/src/main/java/com/ably/example/Utils.kt b/example/src/main/java/com/ably/example/Utils.kt new file mode 100644 index 000000000..d24e114e2 --- /dev/null +++ b/example/src/main/java/com/ably/example/Utils.kt @@ -0,0 +1,284 @@ +package com.ably.example + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import io.ably.lib.objects.RealtimeObjects +import io.ably.lib.objects.ObjectsCallback +import io.ably.lib.objects.type.counter.LiveCounter +import io.ably.lib.objects.type.counter.LiveCounterUpdate +import io.ably.lib.objects.type.map.LiveMap +import io.ably.lib.objects.type.map.LiveMapUpdate +import io.ably.lib.objects.type.map.LiveMapUpdate.Change.UPDATED +import io.ably.lib.objects.type.map.LiveMapValue +import io.ably.lib.realtime.AblyRealtime +import io.ably.lib.realtime.Channel +import io.ably.lib.realtime.ChannelState +import io.ably.lib.realtime.ChannelStateListener +import io.ably.lib.types.AblyException +import io.ably.lib.types.ChannelMode +import io.ably.lib.types.ChannelOptions +import io.ably.lib.types.ErrorInfo +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlin.coroutines.resume + +suspend fun RealtimeObjects.getRootCoroutines(): LiveMap = suspendCancellableCoroutine { continuation -> + getRootAsync(object : ObjectsCallback { + override fun onSuccess(result: LiveMap?) { + continuation.resume(result!!) + } + + override fun onError(exception: AblyException?) { + continuation.cancel(exception) + } + }) +} + +suspend fun RealtimeObjects.createCounterCoroutine(): LiveCounter = suspendCancellableCoroutine { continuation -> + createCounterAsync(object : ObjectsCallback { + override fun onSuccess(result: LiveCounter?) { + continuation.resume(result!!) + } + + override fun onError(exception: AblyException?) { + continuation.cancel(exception) + } + }) +} + +suspend fun RealtimeObjects.createMapCoroutine(): LiveMap = suspendCancellableCoroutine { continuation -> + createMapAsync(object : ObjectsCallback { + override fun onSuccess(result: LiveMap?) { + continuation.resume(result!!) + } + + override fun onError(exception: AblyException?) { + continuation.cancel(exception) + } + }) +} + +suspend fun LiveCounter.incrementCoroutine(amount: Int): Unit = suspendCancellableCoroutine { continuation -> + incrementAsync(amount, object : ObjectsCallback { + override fun onSuccess(result: Void?) { + continuation.resume(Unit) + } + + override fun onError(exception: AblyException?) { + continuation.cancel(exception) + } + }) +} + +suspend fun LiveCounter.decrementCoroutine(amount: Int): Unit = suspendCancellableCoroutine { continuation -> + decrementAsync(amount, object : ObjectsCallback { + override fun onSuccess(result: Void?) { + continuation.resume(Unit) + } + + override fun onError(exception: AblyException?) { + continuation.cancel(exception) + } + }) +} + +suspend fun Channel.updateOptions(options: ChannelOptions): Unit = suspendCancellableCoroutine { continuation -> + setOptions(options, object : io.ably.lib.realtime.CompletionListener { + override fun onSuccess() { + continuation.resume(Unit) + } + + override fun onError(reason: ErrorInfo?) { + continuation.cancel(AblyException.fromErrorInfo(reason)) + } + }) +} + +suspend fun getOrCreateCounter(channel: Channel, root: LiveMap?, path: String): LiveCounter { + val mapValue = root?.get(path) + if (mapValue == null) { + val counter = channel.objects.createCounterCoroutine() + root?.setCoroutine(path, LiveMapValue.of(counter)) + return counter + } else { + return mapValue.asLiveCounter + } +} + +suspend fun getOrCreateMap(channel: Channel, root: LiveMap?, path: String): LiveMap { + val mapValue = root?.get(path) + if (mapValue == null) { + val map = channel.objects.createMapCoroutine() + root?.setCoroutine(path, LiveMapValue.of(map)) + return map + } else { + return mapValue.asLiveMap + } +} + +suspend fun LiveMap.setCoroutine(key: String, value: LiveMapValue) = suspendCancellableCoroutine { continuation -> + setAsync(key, value, object : ObjectsCallback { + override fun onSuccess(result: Void?) { + continuation.resume(Unit) + } + + override fun onError(exception: AblyException?) { + continuation.cancel(exception) + } + }) +} + +suspend fun LiveMap.removeCoroutine(key: String) = suspendCancellableCoroutine { continuation -> + removeAsync(key, object : ObjectsCallback { + override fun onSuccess(result: Void?) { + continuation.resume(Unit) + } + + override fun onError(exception: AblyException?) { + continuation.cancel(exception) + } + }) +} + +@Composable +fun observeCounter(channel: Channel, root: LiveMap?, path: String): CounterState { + var counter by remember { mutableStateOf(null) } + var counterValue by remember { mutableStateOf(null) } + + LaunchedEffect(root) { + counter = getOrCreateCounter(channel, root, path) + } + + DisposableEffect(counter) { + counterValue = counter?.value()?.toInt() + + val listener: (LiveCounterUpdate) -> Unit = { + counter?.value()?.let { + counterValue = it.toInt() + } + } + + counter?.subscribe(listener) + + onDispose { + counter?.unsubscribe(listener) + } + } + + DisposableEffect(root) { + val listener: (LiveMapUpdate) -> Unit = { rootUpdate -> + val counterHasBeenRemoved = rootUpdate.update + .filter { (_, change) -> change == UPDATED } + .any { (keyName) -> keyName == path } + + if (counterHasBeenRemoved) root?.get(path)?.asLiveCounter?.let { counter = it } + } + + root?.subscribe(listener) + + onDispose { + root?.unsubscribe(listener) + } + } + + return CounterState(counterValue, counter) { + coroutineScope { + launch { + counter = channel.objects.createCounterCoroutine().also { + root?.setCoroutine(path, LiveMapValue.of(it)) + } + } + } + } +} + +data class CounterState(val value: Int?, val counter: LiveCounter?, val reset: suspend () -> Unit) + +@Composable +fun observeChannelState(channel: Channel): ChannelState { + var channelState by remember { mutableStateOf(channel.state) } + + DisposableEffect(channel) { + val listener: (ChannelStateListener.ChannelStateChange) -> Unit = { + channelState = it.current + } + + channel.on(listener) + + onDispose { + channel.off(listener) + } + } + + return channelState +} + +@Composable +fun observeMap(channel: Channel, root: LiveMap?, path: String): Pair, LiveMap?> { + var map by remember { mutableStateOf(null) } + var mapValue by remember { mutableStateOf>(mapOf()) } + + LaunchedEffect(root) { + map = getOrCreateMap(channel, root, path) + } + + DisposableEffect(map) { + map?.entries()?.associate { (key, value) -> key to value.asString }?.let { + mapValue = it + } + + val listener: (LiveMapUpdate) -> Unit = { + map?.entries()?.associate { (key, value) -> key to value.asString }?.let { + mapValue = it + } + } + + map?.subscribe(listener) + + onDispose { + map?.unsubscribe(listener) + } + } + + return mapValue to map +} + +@Composable +fun observeRootObject(channel: Channel): LiveMap? { + val channelState = observeChannelState(channel) + var root: LiveMap? by remember { mutableStateOf(null) } + + LaunchedEffect(channelState) { + if (channelState == ChannelState.attached) { + root = channel.objects.getRootCoroutines() + } + } + + return root +} + +@Composable +fun getRealtimeChannel(realtimeClient: AblyRealtime, channelName: String): Channel { + val channel = realtimeClient.channels.get(channelName) + + DisposableEffect(channel) { + channel.setOptions(ChannelOptions().apply { + attachOnSubscribe = false + modes = arrayOf(ChannelMode.object_publish, ChannelMode.object_subscribe) + }) + + channel.attach() + + onDispose { + channel.detach() + } + } + + return channel +} diff --git a/example/src/main/java/com/ably/example/screen/ColorVotingScreen.kt b/example/src/main/java/com/ably/example/screen/ColorVotingScreen.kt new file mode 100644 index 000000000..71f63f435 --- /dev/null +++ b/example/src/main/java/com/ably/example/screen/ColorVotingScreen.kt @@ -0,0 +1,157 @@ +package com.ably.example.screen + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.ably.example.getRealtimeChannel +import com.ably.example.incrementCoroutine +import com.ably.example.observeCounter +import com.ably.example.observeRootObject +import io.ably.lib.realtime.AblyRealtime +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ColorVotingScreen(realtimeClient: AblyRealtime) { + val scope = rememberCoroutineScope() + val channel = getRealtimeChannel(realtimeClient, "objects-live-counter") + + val root = observeRootObject(channel) + + val (redCount, redCounter, resetRed) = observeCounter(channel, root,"red") + val (greenCount, greenCounter, resetGreen) = observeCounter(channel, root,"green") + val (blueCount, blueCounter, resetBlue) = observeCounter(channel, root,"blue") + + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(24.dp) + ) { + Text( + text = "Vote for your favorite color", + fontSize = 24.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + modifier = Modifier.padding(vertical = 16.dp) + ) + + ColorVoteCard( + color = Color.Red, + colorName = "Red", + count = redCount ?: 0, + onVote = { + scope.launch { + redCounter?.incrementCoroutine(1) + } + } + ) + + ColorVoteCard( + color = Color.Green, + colorName = "Green", + count = greenCount ?: 0, + onVote = { + scope.launch { + greenCounter?.incrementCoroutine(1) + } + } + ) + + ColorVoteCard( + color = Color.Blue, + colorName = "Blue", + count = blueCount ?: 0, + onVote = { + scope.launch { + blueCounter?.incrementCoroutine(1) + } + } + ) + + Button( + onClick = { + scope.launch { + resetRed() + resetBlue() + resetGreen() + } + }, + ) { + Text( + text = "Reset all", + color = Color.White, + fontWeight = FontWeight.Medium + ) + } + } +} + +@Composable +fun ColorVoteCard( + color: Color, + colorName: String, + count: Int, + onVote: () -> Unit +) { + Card( + modifier = Modifier + .fillMaxWidth() + .height(120.dp), + elevation = CardDefaults.cardElevation(defaultElevation = 4.dp), + shape = RoundedCornerShape(12.dp) + ) { + Row( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Box( + modifier = Modifier + .size(40.dp) + .background(color, RoundedCornerShape(8.dp)) + ) + Text( + text = colorName, + fontSize = 18.sp, + fontWeight = FontWeight.Medium + ) + } + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = count.toString(), + fontSize = 24.sp, + fontWeight = FontWeight.Bold + ) + OutlinedButton( + onClick = onVote, + ) { + Text( + text = "Vote", + fontWeight = FontWeight.Medium + ) + } + } + } + } +} diff --git a/example/src/main/java/com/ably/example/screen/MainScreen.kt b/example/src/main/java/com/ably/example/screen/MainScreen.kt new file mode 100644 index 000000000..371e22b4c --- /dev/null +++ b/example/src/main/java/com/ably/example/screen/MainScreen.kt @@ -0,0 +1,58 @@ +package com.ably.example.screen + +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.List +import androidx.compose.material.icons.filled.Favorite +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import io.ably.lib.realtime.AblyRealtime + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MainScreen(realtimeClient: AblyRealtime) { + var selectedTab by remember { mutableIntStateOf(0) } + + val tabs = listOf( + TabItem("Color Voting", Icons.Default.Favorite), + TabItem("Task Management", Icons.AutoMirrored.Filled.List), + ) + + Column(modifier = Modifier.fillMaxSize()) { + TopAppBar( + title = { + Text("Ably Live Objects Demo") + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer, + ) + ) + + TabRow( + selectedTabIndex = selectedTab, + modifier = Modifier.fillMaxWidth() + ) { + tabs.forEachIndexed { index, tab -> + Tab( + selected = selectedTab == index, + onClick = { selectedTab = index }, + text = { Text(tab.title) }, + icon = { Icon(tab.icon, contentDescription = tab.title) } + ) + } + } + + when (selectedTab) { + 0 -> ColorVotingScreen(realtimeClient) + 1 -> TaskManagementScreen(realtimeClient) + } + } +} + +data class TabItem( + val title: String, + val icon: ImageVector, +) diff --git a/example/src/main/java/com/ably/example/screen/TaskManagementScreen.kt b/example/src/main/java/com/ably/example/screen/TaskManagementScreen.kt new file mode 100644 index 000000000..1c384b797 --- /dev/null +++ b/example/src/main/java/com/ably/example/screen/TaskManagementScreen.kt @@ -0,0 +1,257 @@ +package com.ably.example.screen + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.ably.example.getRealtimeChannel +import com.ably.example.observeMap +import com.ably.example.observeRootObject +import com.ably.example.removeCoroutine +import com.ably.example.setCoroutine +import io.ably.lib.objects.type.map.LiveMapValue +import io.ably.lib.realtime.AblyRealtime +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TaskManagementScreen(realtimeClient: AblyRealtime) { + var taskText by remember { mutableStateOf("") } + var editingTaskId by remember { mutableStateOf(null) } + var editingText by remember { mutableStateOf("") } + + val scope = rememberCoroutineScope() + + val channel = getRealtimeChannel(realtimeClient, "objects-live-map") + val root = observeRootObject(channel) + + val (taskIdToTask, liveTasks) = observeMap(channel, root, "tasks") + + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = "Task Management", + fontSize = 24.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Card( + modifier = Modifier.fillMaxWidth(), + elevation = CardDefaults.cardElevation(defaultElevation = 4.dp), + shape = RoundedCornerShape(12.dp) + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + OutlinedTextField( + value = taskText, + onValueChange = { taskText = it }, + label = { Text("Enter new task") }, + modifier = Modifier.fillMaxWidth(), + singleLine = true + ) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Button( + onClick = { + if (taskText.isNotBlank()) { + scope.launch { + val taskId = "task_${System.currentTimeMillis()}" + liveTasks?.setCoroutine(taskId, LiveMapValue.of(taskText.trim())) + taskText = "" + } + } + }, + modifier = Modifier.weight(1f) + ) { + Icon(Icons.Default.Add, contentDescription = "Add") + Spacer(modifier = Modifier.width(8.dp)) + Text("Add Task") + } + + OutlinedButton( + onClick = { + scope.launch { + taskIdToTask.forEach { task -> + liveTasks?.removeCoroutine(task.key) + } + } + }, + modifier = Modifier.weight(1f) + ) { + Text("Remove All") + } + } + } + } + + Card( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + elevation = CardDefaults.cardElevation(defaultElevation = 4.dp), + shape = RoundedCornerShape(12.dp) + ) { + Column( + modifier = Modifier.padding(16.dp) + ) { + Text( + text = "Tasks (${taskIdToTask.size})", + fontSize = 18.sp, + fontWeight = FontWeight.Medium, + modifier = Modifier.padding(bottom = 12.dp) + ) + + if (taskIdToTask.isEmpty()) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(32.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = "No tasks yet. Add one above!", + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } else { + LazyColumn( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + items(taskIdToTask.entries.size) { index -> + val task = taskIdToTask.entries.elementAt(index) + TaskItemCard( + task = task, + isEditing = editingTaskId == task.key, + editingText = editingText, + onEditingTextChange = { editingText = it }, + onEdit = { + editingTaskId = task.key + editingText = task.value + }, + onSave = { + scope.launch { + liveTasks?.setCoroutine(task.key, LiveMapValue.of(editingText.trim())) + editingTaskId = null + editingText = "" + } + }, + onCancel = { + editingTaskId = null + editingText = "" + }, + onDelete = { + scope.launch { + liveTasks?.removeCoroutine(task.key) + } + } + ) + } + } + } + } + } + } +} + +@Composable +fun TaskItemCard( + task: Map.Entry, + isEditing: Boolean, + editingText: String, + onEditingTextChange: (String) -> Unit, + onEdit: () -> Unit, + onSave: () -> Unit, + onCancel: () -> Unit, + onDelete: () -> Unit +) { + Card( + modifier = Modifier.fillMaxWidth().padding(all = 2.dp), + elevation = CardDefaults.cardElevation(defaultElevation = 2.dp), + shape = RoundedCornerShape(8.dp) + ) { + if (isEditing) { + Column( + modifier = Modifier.padding(12.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + OutlinedTextField( + value = editingText, + onValueChange = onEditingTextChange, + modifier = Modifier.fillMaxWidth(), + singleLine = true + ) + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Button( + onClick = onSave, + modifier = Modifier.weight(1f) + ) { + Text("Save") + } + OutlinedButton( + onClick = onCancel, + modifier = Modifier.weight(1f) + ) { + Text("Cancel") + } + } + } + } else { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(12.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = task.value, + modifier = Modifier.weight(1f), + fontSize = 16.sp + ) + + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + IconButton(onClick = onEdit) { + Icon( + Icons.Default.Edit, + contentDescription = "Edit", + tint = MaterialTheme.colorScheme.primary + ) + } + IconButton(onClick = onDelete) { + Icon( + Icons.Default.Delete, + contentDescription = "Delete", + tint = MaterialTheme.colorScheme.error + ) + } + } + } + } + } +} diff --git a/example/src/main/java/com/ably/example/ui/theme/Color.kt b/example/src/main/java/com/ably/example/ui/theme/Color.kt new file mode 100644 index 000000000..621834cb1 --- /dev/null +++ b/example/src/main/java/com/ably/example/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.ably.example.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) diff --git a/example/src/main/java/com/ably/example/ui/theme/Theme.kt b/example/src/main/java/com/ably/example/ui/theme/Theme.kt new file mode 100644 index 000000000..6e9726b94 --- /dev/null +++ b/example/src/main/java/com/ably/example/ui/theme/Theme.kt @@ -0,0 +1,46 @@ +package com.ably.example.ui.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 +) + +@Composable +fun AblyTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} diff --git a/example/src/main/java/com/ably/example/ui/theme/Type.kt b/example/src/main/java/com/ably/example/ui/theme/Type.kt new file mode 100644 index 000000000..091be1dd4 --- /dev/null +++ b/example/src/main/java/com/ably/example/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.ably.example.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) diff --git a/example/src/main/res/drawable/launch_background.xml b/example/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000..304732f88 --- /dev/null +++ b/example/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/src/main/res/mipmap-anydpi/ic_launcher.xml b/example/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 000000000..036d09bc5 --- /dev/null +++ b/example/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/example/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 000000000..036d09bc5 --- /dev/null +++ b/example/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/example/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..928637e9c00058a5fd15dbe97e080e925446b5b6 GIT binary patch literal 11753 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 OT{5*gUuNun@BaXYiE*I- literal 0 HcmV?d00001 diff --git a/example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..9b97812d6f38f5bfef920c043e0dff2cb54bfd14 GIT binary patch literal 18953 zcmXtgWmFqo*LDag1b25RR@^BXoZ=3}-6a%vf1yYrPGb$XF`mRCfKc>l)80XpSH4MA-s?m=TSM&g}yi>jG#-nS~BTTc$C=C7wgb0>en}QpvQcX)JsH z$;!@qT+pGSfmbYV;`n)QY>Cg#R_%V=V|)x48fqnduSS&H!l9jWbXGT2{`W1pXZi1) zzRDf;lcpI3>g*K;cL;CTHdNufN8N|C(T$ z9HR>w6WuY0hl#AD@vl$?S%1X?p}B%T z^Q<^{q?0hAASkUDPu#v}vZQd6wCmhfsNMKX(`fMCQ1#bq@y%PIcWJ7COh7y;H#USS z)De&h=)kXJbN<%(1=i&d<7S{m4*N1+m`djuYgiZY0?8|g{jLY# z00<)cqQY(I`ai`?tq9CYNY+^wMO<4pod5hWEGP8U^vEV{C&LdZbO(tN)torgFw_fR zjth==7TC`a0+Im&0ZmB0Sa3GkE0m;z7}75D++mulQawOpWc>mL3{q4F^j-5>kHLEn z-sA%iBMDPN?#-(_f3$�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 P00000NkvXXu0mjfh3@1T literal 0 HcmV?d00001 diff --git a/example/src/main/res/values/colors.xml b/example/src/main/res/values/colors.xml new file mode 100644 index 000000000..ca1931bca --- /dev/null +++ b/example/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + diff --git a/example/src/main/res/values/ic_launcher_background.xml b/example/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 000000000..9f24f9be4 --- /dev/null +++ b/example/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #03020d + \ No newline at end of file diff --git a/example/src/main/res/values/strings.xml b/example/src/main/res/values/strings.xml new file mode 100644 index 000000000..7649b0314 --- /dev/null +++ b/example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Ably Example + diff --git a/example/src/main/res/values/themes.xml b/example/src/main/res/values/themes.xml new file mode 100644 index 000000000..6405a75fb --- /dev/null +++ b/example/src/main/res/values/themes.xml @@ -0,0 +1,4 @@ + + +