-
Notifications
You must be signed in to change notification settings - Fork 41
feat[live-objects]: add example module showcasing LiveObjects features
#1150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
97bb33a
feat[live-objects]: add `example` module showcasing LiveObjects features
ttypic ebb7915
chore: rename example module to examples
ttypic 4935c94
chore: sort tasks by IDs
ttypic 0a4c51f
chore(docs): update LO example readme
ttypic 4e8d773
refactor: move from `java/` to `kotlin/`
ttypic a7c6b55
feat: make example app work with the sandbox
ttypic 8a347f5
feat: add UI tests and CI workflow for example app
ttypic f4fed1c
chore(docs): add Live Objects overview, installation, and examples to…
ttypic File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| name: Example App | ||
|
|
||
| on: | ||
| pull_request: | ||
| push: | ||
| branches: | ||
| - main | ||
|
|
||
| jobs: | ||
| check: | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| android-api-level: [ 29 ] | ||
|
|
||
| steps: | ||
| - name: checkout | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up the JDK | ||
| uses: actions/setup-java@v3 | ||
| with: | ||
| java-version: '17' | ||
| distribution: 'temurin' | ||
|
|
||
| - name: Set up Gradle | ||
| uses: gradle/actions/setup-gradle@v3 | ||
|
|
||
| - name: Enable KVM | ||
| run: | | ||
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | ||
| sudo udevadm control --reload-rules | ||
| sudo udevadm trigger --name-match=kvm | ||
| - uses: reactivecircus/android-emulator-runner@v2 | ||
| with: | ||
| api-level: ${{ matrix.android-api-level }} | ||
| emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | ||
| disable-animations: true | ||
| # Print emulator logs if tests fail | ||
| script: ./gradlew :examples:connectedAndroidTest || (adb logcat -d System.out:I && exit 1) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /build |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| # 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 | ||
ttypic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| Add your Ably key to the `local.properties` file: | ||
|
|
||
| ```properties | ||
| sdk.dir=/path/to/android/sdk | ||
|
|
||
| EXAMPLES_ABLY_KEY=xxxx:yyyyyy | ||
| ``` | ||
ttypic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ## 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. | ||
|
|
||
| ## Building release APK | ||
|
|
||
| This is useful to check ProGuard rules, app size, etc. | ||
|
|
||
| 1. Create signing keys for the Android app | ||
|
|
||
| ```shell | ||
| keytool -genkey -v -keystore release.keystore \ | ||
| -storepass <store-password> \ | ||
| -alias <key-alias> \ | ||
| -keypass <key-password> \ | ||
| -keyalg RSA -keysize 2048 -validity 25000 -dname "CN=Ably Example App,OU=Examples,O=Ably,L=London,ST=England,C=GB" | ||
| ``` | ||
|
|
||
| 2. Update `local.properties` file: | ||
|
|
||
| ```properties | ||
| EXAMPLES_STORE_FILE=/absolute/path/to/release.keystore | ||
| EXAMPLES_STORE_PASSWORD=<store-password> | ||
| EXAMPLES_KEY_ALIAS=<key-alias> | ||
| EXAMPLES_KEY_PASSWORD=<key-password> | ||
| ``` | ||
|
|
||
| 3. Build release APK | ||
|
|
||
| ```shell | ||
| ./gradlew :examples:assembleRelease | ||
| ``` | ||
|
|
||
| 4. Install to the device | ||
|
|
||
| ```shell | ||
| adb install -r examples/build/outputs/apk/release/examples-release.apk | ||
| ``` | ||
|
|
||
| ## 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. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| 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 = 29 | ||
| targetSdk = 35 | ||
| versionCode = 1 | ||
| versionName = "1.0" | ||
|
|
||
| testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" | ||
|
|
||
| buildConfigField("String", "ABLY_KEY", "\"${getLocalProperty("EXAMPLES_ABLY_KEY") ?: ""}\"") | ||
| } | ||
|
|
||
| buildTypes { | ||
| release { | ||
| isMinifyEnabled = true | ||
| isShrinkResources = true | ||
|
|
||
| proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") | ||
|
|
||
| val keystorePath = getLocalProperty("EXAMPLES_STORE_FILE") | ||
| keystorePath?.let { | ||
| signingConfig = signingConfigs.create("release") { | ||
| keyAlias = getLocalProperty("EXAMPLES_KEY_ALIAS") | ||
| keyPassword = getLocalProperty("EXAMPLES_KEY_PASSWORD") | ||
| storeFile = file(it) | ||
| storePassword = getLocalProperty("EXAMPLES_STORE_PASSWORD") | ||
| } | ||
| } | ||
| } | ||
| } | ||
| 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(libs.ktor.client.core) | ||
| implementation(libs.ktor.client.cio) | ||
|
|
||
| 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) | ||
| } | ||
ttypic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 *; | ||
| #} | ||
|
|
||
sacOO7 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # 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 | ||
51 changes: 51 additions & 0 deletions
51
examples/src/androidTest/kotlin/com/ably/example/ColorVotingScreenTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| package com.ably.example | ||
|
|
||
| import androidx.compose.ui.semantics.SemanticsProperties | ||
| import androidx.compose.ui.test.assertIsDisplayed | ||
| import androidx.compose.ui.test.junit4.createAndroidComposeRule | ||
| import androidx.compose.ui.test.onAllNodesWithText | ||
| import androidx.compose.ui.test.onNodeWithTag | ||
| import androidx.compose.ui.test.onNodeWithText | ||
| import androidx.compose.ui.test.performClick | ||
| import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
| import org.junit.Rule | ||
| import org.junit.Test | ||
| import org.junit.runner.RunWith | ||
|
|
||
| @RunWith(AndroidJUnit4::class) | ||
| class ColorVotingScreenTest { | ||
|
|
||
| @get:Rule | ||
| val composeTestRule = createAndroidComposeRule<MainActivity>() | ||
|
|
||
| @Test | ||
| fun incrementRedColor() { | ||
| // Navigate to Color Voting tab | ||
| composeTestRule.onNodeWithText("Color Voting").performClick() | ||
|
|
||
| // Wait for the screen to load | ||
| composeTestRule.waitForIdle() | ||
|
|
||
| // Find and click the Vote button for Red color | ||
| val redVoteButton = composeTestRule.onNodeWithTag("vote_button_red") | ||
|
|
||
| // Capture initial count | ||
| val initial = composeTestRule.onNodeWithTag("counter_red") | ||
| .fetchSemanticsNode() | ||
| .config[SemanticsProperties.Text].first().text.toInt() | ||
|
|
||
| composeTestRule.waitUntil(timeoutMillis = 10_000) { | ||
| SemanticsProperties.Disabled !in redVoteButton.fetchSemanticsNode().config | ||
| } | ||
|
|
||
| redVoteButton.performClick() | ||
|
|
||
| // Wait for the counter to update with 5-seconds timeout | ||
| composeTestRule.waitUntil(timeoutMillis = 5_000) { | ||
| val updated = composeTestRule.onNodeWithTag("counter_red") | ||
| .fetchSemanticsNode() | ||
| .config[SemanticsProperties.Text].first().text.toInt() | ||
| updated == initial + 1 | ||
| } | ||
| } | ||
| } |
23 changes: 23 additions & 0 deletions
23
examples/src/androidTest/kotlin/com/ably/example/MainScreenTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.ably.example | ||
|
|
||
| import androidx.compose.ui.test.assertIsDisplayed | ||
| import androidx.compose.ui.test.junit4.createAndroidComposeRule | ||
| import androidx.compose.ui.test.onNodeWithText | ||
| import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
| import org.junit.Rule | ||
| import org.junit.Test | ||
| import org.junit.runner.RunWith | ||
|
|
||
| @RunWith(AndroidJUnit4::class) | ||
| class MainScreenTest { | ||
|
|
||
| @get:Rule | ||
| val composeTestRule = createAndroidComposeRule<MainActivity>() | ||
|
|
||
| @Test | ||
| fun tabsAreDisplayed() { | ||
| // Verify both tabs are displayed | ||
| composeTestRule.onNodeWithText("Color Voting").assertIsDisplayed() | ||
| composeTestRule.onNodeWithText("Task Management").assertIsDisplayed() | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.