Skip to content

Commit

Permalink
Merge pull request #10 from javierpe/task/RemoveVMFromComponents
Browse files Browse the repository at this point in the history
Task | Remove ViewModel from components and Navigation
  • Loading branch information
javierpe authored May 4, 2023
2 parents 1ddfb94 + 08fc5a1 commit ae747ce
Show file tree
Hide file tree
Showing 442 changed files with 9,266 additions and 4,663 deletions.
69 changes: 0 additions & 69 deletions .github/workflows/android-ci-v2i.yml

This file was deleted.

29 changes: 4 additions & 25 deletions .github/workflows/android-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:

steps:
- uses: actions/checkout@v3
- name: set up JDK 11
- name: set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'
cache: gradle

Expand All @@ -32,10 +32,10 @@ jobs:
needs: build
steps:
- uses: actions/checkout@v3
- name: set up JDK 11
- name: set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'
cache: gradle
- name: Grant execute permission for gradlew
Expand All @@ -46,24 +46,3 @@ jobs:

- name: Unit Test
run: ./gradlew testDebugUnitTest

- name: Upload Test Reports Folder
uses: actions/upload-artifact@v2
if: ${{ always() }} # IMPORTANT: Upload reports regardless of status
with:
name: reports
path: app/build/test-results # path to where the xml test results are stored

report:

runs-on: ubuntu-latest

needs: test
if: ${{ always() }}
steps:
- name: Download Test Reports Folder
uses: actions/download-artifact@v2
with:
name: reports
- name: Android Test Report
uses: asadmansr/android-test-report-action@v1.2.0
16 changes: 8 additions & 8 deletions .github/workflows/release-version.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build & Publish Debug APK
name: Build & Publish Release APK

on:
push:
Expand All @@ -14,24 +14,24 @@ jobs:

steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'
cache: gradle

- name: Gradle permissions
run: chmod +x ./gradlew

- name: Create destinations
run: ./gradlew kspDebugKotlin
run: ./gradlew kspReleaseKotlin

- name: Build with Gradle
run: ./gradlew build

- name: Build Debug APK
run: ./gradlew assembleDebug
- name: Build Release APK
run: ./gradlew assembleRelease

- name: Expose version labels
uses: michpohl/android-expose-version-name-action@v1.0.0
Expand All @@ -45,6 +45,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: "v${{ env.ANDROID_VERSION_NAME }}"
asset_path: app/build/outputs/apk/debug/app-debug.apk
asset_name: "SearchApp_Build$_{{ env.ANDROID_VERSION_Code }}.apk"
asset_path: app/build/outputs/apk/release/app-release.apk
asset_name: "ArtGallery_Build$_{{ env.ANDROID_VERSION_CODE }}.apk"
asset_content_type: application/zip
70 changes: 31 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,58 @@

## Features

- [x] Support multiple screens
- [x] Tooltips
- [x] Compose navigation
- [x] Render process mechanism
- [x] Package refactor and naming
- [x] Unit test
- [x] Compose navigation by
- [x] Macrobenchmark test
- [x] Android CI
- [x] KSP processor to avoid boilerplate code
- [x] Unit test, Macrobenchmark and Android CI
- [x] KSP processor

Art Gallery is based on Server Driven UI, only provide a JSON and UI definition.

> The SDUI allows you send new features and create
> reusable componentes as possible.
### JSON Definition
### Definition

Main structure:
```json
{
"header": [...]
"body": [...]
}
```
1. Each JSON component must follow the following definition:
1. `render` is an enum of RenderType and is the record of each component in the list.
2. `index` is the component's position in the list and must not be duplicated.
3. `resource` is a JSON object and therefore can be converted to some data class.
![Definition](https://user-images.githubusercontent.com/7097754/229964052-a2069a24-b386-42e9-a666-5a0e3a3eb7aa.png)

To add new component only follow this definition:
```json
{
"body": [
{
"render": "component_name",
"index": 0,
"resource": {
... A model definition for this component.
}
}
]
}
```
2. You must create a model capable of containing all these components, for example `DataContentModel`, here the header and body of our JSON are specified.
![Definition](https://user-images.githubusercontent.com/7097754/229964357-668ff6fd-afae-4983-8ac7-7d7db1760454.png)
![DataContentModel](https://user-images.githubusercontent.com/7097754/229964225-e305f830-2530-42ed-8768-133a5af2a5bb.png)

### Magic annotations
### Do it!

With KSP we extend the functionality to annotations to avoid boilerplate code. Only follow this:
To create a new UI component you must follow the following steps:

1. Your data class should have _@RenderClass_ annotation and provide it the render type like _RenderType.BANNER_ or whatever you need.
1. In the RenderType class you must register the name of the new component
2. Create a model specific to the `resource` you need to convert
3. Add the `@RenderModel` annotation to the model and pass the new render type you created as a parameter
![RenderClass](https://user-images.githubusercontent.com/7097754/236341023-619813d2-4b09-43de-9ed2-3c469de2c66d.png)

2. If you want transform that model when the resource is available you can add a render factory class that extends of _DynamicListRender<BannerCarouselModel>_ and provide _@RenderFactory_ annotation with a model class as parameter like _BannerCarouselModel::class_. This process transform your model before UI use it.
4. Create a render factory class that extends `DynamicListFactory` and that will take care of managing the component
1. The class asks for a list of compatible renders, add the one you created.
2. If that component has an onboarding tooltip then add `hasShowCaseConfigured = true`
3. In the `CreateComponent` function add your composable element
4. In the `CreateSkeleton` function create a plain copy of your composable element, this is a skeleton!
5. Do not forget annotate it with `@ComponentFactory`

3. Finaly, you should have a factory that will be used to create the UI for that single component, should extends of _DynamicListFactory_ and provide _@AdapterFactory_ annotation.

Note: _ComponentModel_ is a data class that contains your element definition, _render_, _index_ and _resource_
5. On the main screen add the `ContextView` component as follows
1. `title` is the title of the screen
2. `viewModel` you must create a ViewModel that extends from `ContextViewModel` that contains the screen information
3. `destinationsNavigator` is a navigation instance
1. `dynamicListRequestModel` here goes a `DynamicListRequestModel` object or via `rememberDynamicListRequestState`
![ContextView](https://user-images.githubusercontent.com/7097754/229964545-78096ce6-b286-4bbe-98e7-56a7c8e0aab0.png)

## Used libraries

- [Compose navigation](https://developer.android.com/jetpack/compose/navigation?hl=es-419) - Recommended for Google to navigate between compose screens
- [Compose Destinations](https://github.com/raamcosta/compose-destinations) - Its a great navigation compose library
- [Hilt](https://developer.android.com/training/dependency-injection/hilt-android?hl=es-419) - Dependency injection
- [Coil](https://coil-kt.github.io/coil/compose/) - Image loader for compose
- [Lottie](https://github.com/airbnb/lottie/blob/master/android-compose.md) - Lottie animations for compose
- [Gson](https://github.com/google/gson) - Serialization/Deserializaton JSON to convert Java Objects into JSON and back
- [Moshi](https://github.com/square/moshi) - JSON Serialization/Deserializaton
- [DataStore](https://developer.android.com/topic/libraries/architecture/datastore?hl=es-419) - Modern Android preferences
- [Room](https://developer.android.com/jetpack/androidx/releases/room?gclid=CjwKCAjw6fyXBhBgEiwAhhiZsjAF2biSAQEU8zfC58pfv7u2Z-B6Hbysd4PlQtYZH_KZSvWyMRhd3BoCIV8QAvD_BwE&gclsrc=aw.ds) - Data persistence
- [Jetpack Compose](https://developer.android.com/jetpack/compose?gclid=CjwKCAjw6fyXBhBgEiwAhhiZshbizxlJ4fvLaIjjt3SZerY3SnmCgygwltc7iBUlIApiwcC7IHmEexoC7PsQAvD_BwE&gclsrc=aw.ds) - Modern Android UI development
11 changes: 6 additions & 5 deletions app/benchmark/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ androidComponents {
}

dependencies {
implementation "androidx.profileinstaller:profileinstaller:1.2.2"
implementation 'androidx.test.ext:junit:1.1.5'
implementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation 'androidx.test.uiautomator:uiautomator:2.2.0'
implementation 'androidx.benchmark:benchmark-macro-junit4:1.1.1'
implementation(libs.androidx.profileinstaller)
implementation(libs.test.junit)
implementation(libs.test.android.espresso)
implementation(libs.test.android.automator)
implementation(libs.test.android.benchmark)
implementation(libs.test.android.junit)
}

androidComponents {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ class BaselineProfileGenerator {
// through your most important UI.
startActivityAndWait()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package com.nucu.benchmark

const val PACKAGE_NAME = "com.nucu.dynamiclistcompose"
const val PACKAGE_NAME = "com.nucu.dynamiclistcompose"
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ private const val SHOW_CASE_TAG = "show-case"

private const val GESTURE_MARGIN = 5
private const val TIMEOUT: Long = 30_000
private const val REPEAT_TIMES = 3
private const val REPEAT_TIMES = 5

@RunWith(AndroidJUnit4ClassRunner::class)
class ScrollDynamicListBenchmark {
Expand Down Expand Up @@ -52,4 +52,4 @@ class ScrollDynamicListBenchmark {
dynamicListContainer.fling(Direction.DOWN)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class ScrollingCardListBenchmark {
startActivityAndWait() // Time To Initial Display (TTID)
}
) {

// Wait to until show case is hidden
device.wait(Until.gone(By.res(SHOW_CASE_TAG)), TIMEOUT)

Expand Down Expand Up @@ -87,4 +86,4 @@ class ScrollingCardListBenchmark {
// Scroll down
container.fling(Direction.RIGHT)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.StartupTimingMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
Expand Down Expand Up @@ -34,9 +32,9 @@ class StartupBenchmark {
packageName = PACKAGE_NAME,
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD
startupMode = StartupMode.WARM
) {
pressHome()
startActivityAndWait()
}
}
}
Loading

0 comments on commit ae747ce

Please sign in to comment.