From 3b0656a7744169f104ce0781ba54a3cab946dc97 Mon Sep 17 00:00:00 2001 From: Navratan Soni Date: Wed, 28 Aug 2024 21:03:05 +0530 Subject: [PATCH 1/6] Initial library setup --- code/aepcomposeui/.gitignore | 1 + code/aepcomposeui/build.gradle.kts | 34 +++++++++++++++++++ .../aepcomposeui/ExampleInstrumentedTest.kt | 24 +++++++++++++ .../aepcomposeui/src/main/AndroidManifest.xml | 4 +++ .../mobile/aepcomposeui/ExampleUnitTest.kt | 17 ++++++++++ code/gradle.properties | 5 +++ code/settings.gradle.kts | 9 ++--- 7 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 code/aepcomposeui/.gitignore create mode 100644 code/aepcomposeui/build.gradle.kts create mode 100644 code/aepcomposeui/src/androidTest/java/com/adobe/marketing/mobile/aepcomposeui/ExampleInstrumentedTest.kt create mode 100644 code/aepcomposeui/src/main/AndroidManifest.xml create mode 100644 code/aepcomposeui/src/test/java/com/adobe/marketing/mobile/aepcomposeui/ExampleUnitTest.kt diff --git a/code/aepcomposeui/.gitignore b/code/aepcomposeui/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/code/aepcomposeui/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/code/aepcomposeui/build.gradle.kts b/code/aepcomposeui/build.gradle.kts new file mode 100644 index 00000000..1c743e90 --- /dev/null +++ b/code/aepcomposeui/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id("aep-library") +} + +val mavenCoreVersion: String by project +val aepComposeUiModuleName: String by project +val aepComposeUiVersion: String by project +val aepComposeUiMavenRepoName: String by project +val aepComposeUiMavenRepoDescription: String by project + +aepLibrary { + namespace = "com.adobe.marketing.mobile.aepcomposeui" + + moduleName = aepComposeUiModuleName + moduleVersion = aepComposeUiVersion + enableSpotless = true + enableCheckStyle = true + enableDokkaDoc = true + + publishing { + mavenRepoName = aepComposeUiMavenRepoName + mavenRepoDescription = aepComposeUiMavenRepoDescription + gitRepoName = "aepsdk-ui-android" + addCoreDependency(mavenCoreVersion) + } +} + +dependencies { + implementation("com.adobe.marketing.mobile:core:$mavenCoreVersion") + testImplementation("org.robolectric:robolectric:4.7") + testImplementation("io.mockk:mockk:1.13.11") +} + + diff --git a/code/aepcomposeui/src/androidTest/java/com/adobe/marketing/mobile/aepcomposeui/ExampleInstrumentedTest.kt b/code/aepcomposeui/src/androidTest/java/com/adobe/marketing/mobile/aepcomposeui/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..dfe5e3a5 --- /dev/null +++ b/code/aepcomposeui/src/androidTest/java/com/adobe/marketing/mobile/aepcomposeui/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.adobe.marketing.mobile.aepcomposeui + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.adobe.marketing.mobile.aepcomposeui.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/code/aepcomposeui/src/main/AndroidManifest.xml b/code/aepcomposeui/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/code/aepcomposeui/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/code/aepcomposeui/src/test/java/com/adobe/marketing/mobile/aepcomposeui/ExampleUnitTest.kt b/code/aepcomposeui/src/test/java/com/adobe/marketing/mobile/aepcomposeui/ExampleUnitTest.kt new file mode 100644 index 00000000..6c65e748 --- /dev/null +++ b/code/aepcomposeui/src/test/java/com/adobe/marketing/mobile/aepcomposeui/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.adobe.marketing.mobile.aepcomposeui + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/code/gradle.properties b/code/gradle.properties index f87e7936..2660ee63 100644 --- a/code/gradle.properties +++ b/code/gradle.properties @@ -22,6 +22,11 @@ notificationbuilderVersion=3.0.0 notificationbuilderMavenRepoName=AdobeMobileNotificationBuilderSdk notificationbuilderMavenRepoDescription=Android Notification Builder library for Adobe Mobile Marketing +aepComposeUiModuleName = aepcomposeui +aepComposeUiVersion=1.0.1 +aepComposeUiMavenRepoName = AdobeMobileAEPComposeUiSdk +aepComposeUiMavenRepoDescription = Android AEP Compose UI library for Adobe Mobile Marketing + mavenCoreVersion=3.0.0 diff --git a/code/settings.gradle.kts b/code/settings.gradle.kts index 5c9ab072..4d6da57f 100755 --- a/code/settings.gradle.kts +++ b/code/settings.gradle.kts @@ -30,7 +30,8 @@ dependencyResolutionManagement { } rootProject.name = "aepsdk-ui-android" -include ( - ":notificationbuilder", - ":testapp" -) \ No newline at end of file +include( + ":notificationbuilder", + ":testapp", + ":aepcomposeui" +) From e5b41f969414b0f380b35860eb8efd08f884da86 Mon Sep 17 00:00:00 2001 From: Navratan Soni Date: Wed, 25 Sep 2024 14:40:12 +0530 Subject: [PATCH 2/6] aep-ui-templates setup --- code/aepuitemplates/.gitignore | 1 + code/aepuitemplates/build.gradle.kts | 32 +++++++++++++++++++ .../aepuitemplates/ExampleInstrumentedTest.kt | 24 ++++++++++++++ .../src/main/AndroidManifest.xml | 4 +++ .../mobile/aepuitemplates/ExampleUnitTest.kt | 17 ++++++++++ code/gradle.properties | 5 +++ code/settings.gradle.kts | 5 +-- 7 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 code/aepuitemplates/.gitignore create mode 100644 code/aepuitemplates/build.gradle.kts create mode 100644 code/aepuitemplates/src/androidTest/java/com/adobe/marketing/mobile/aepuitemplates/ExampleInstrumentedTest.kt create mode 100644 code/aepuitemplates/src/main/AndroidManifest.xml create mode 100644 code/aepuitemplates/src/test/java/com/adobe/marketing/mobile/aepuitemplates/ExampleUnitTest.kt diff --git a/code/aepuitemplates/.gitignore b/code/aepuitemplates/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/code/aepuitemplates/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/code/aepuitemplates/build.gradle.kts b/code/aepuitemplates/build.gradle.kts new file mode 100644 index 00000000..d429da20 --- /dev/null +++ b/code/aepuitemplates/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + id("aep-library") +} + +val mavenCoreVersion: String by project +val aepUiTemplatesModuleName: String by project +val aepUiTemplatesVersion: String by project +val aepUiTemplatesMavenRepoName: String by project +val aepUiTemplatesMavenRepoDescription: String by project + +aepLibrary { + namespace = "com.adobe.marketing.mobile.aepcomposeui" + + moduleName = aepUiTemplatesModuleName + moduleVersion = aepUiTemplatesVersion + enableSpotless = true + enableCheckStyle = true + enableDokkaDoc = true + + publishing { + mavenRepoName = aepUiTemplatesMavenRepoName + mavenRepoDescription = aepUiTemplatesMavenRepoDescription + gitRepoName = "aepsdk-ui-android" + addCoreDependency(mavenCoreVersion) + } +} + +dependencies { + implementation("com.adobe.marketing.mobile:core:$mavenCoreVersion") + testImplementation("org.robolectric:robolectric:4.7") + testImplementation("io.mockk:mockk:1.13.11") +} \ No newline at end of file diff --git a/code/aepuitemplates/src/androidTest/java/com/adobe/marketing/mobile/aepuitemplates/ExampleInstrumentedTest.kt b/code/aepuitemplates/src/androidTest/java/com/adobe/marketing/mobile/aepuitemplates/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..5ade8ab0 --- /dev/null +++ b/code/aepuitemplates/src/androidTest/java/com/adobe/marketing/mobile/aepuitemplates/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.adobe.marketing.mobile.aepuitemplates + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.adobe.marketing.mobile.aepuitemplates.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/code/aepuitemplates/src/main/AndroidManifest.xml b/code/aepuitemplates/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/code/aepuitemplates/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/code/aepuitemplates/src/test/java/com/adobe/marketing/mobile/aepuitemplates/ExampleUnitTest.kt b/code/aepuitemplates/src/test/java/com/adobe/marketing/mobile/aepuitemplates/ExampleUnitTest.kt new file mode 100644 index 00000000..d0069d2a --- /dev/null +++ b/code/aepuitemplates/src/test/java/com/adobe/marketing/mobile/aepuitemplates/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.adobe.marketing.mobile.aepuitemplates + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/code/gradle.properties b/code/gradle.properties index 2660ee63..770464ee 100644 --- a/code/gradle.properties +++ b/code/gradle.properties @@ -27,6 +27,11 @@ aepComposeUiVersion=1.0.1 aepComposeUiMavenRepoName = AdobeMobileAEPComposeUiSdk aepComposeUiMavenRepoDescription = Android AEP Compose UI library for Adobe Mobile Marketing +aepUiTemplatesModuleName = aepuitemplates +aepUiTemplatesVersion=1.0.1 +aepUiTemplatesMavenRepoName = AdobeMobileAEPUiTemplates +aepUiTemplatesMavenRepoDescription = Android AEP UI templates library for Adobe Mobile Marketing + mavenCoreVersion=3.0.0 diff --git a/code/settings.gradle.kts b/code/settings.gradle.kts index 4d6da57f..f4e67753 100755 --- a/code/settings.gradle.kts +++ b/code/settings.gradle.kts @@ -33,5 +33,6 @@ rootProject.name = "aepsdk-ui-android" include( ":notificationbuilder", ":testapp", - ":aepcomposeui" -) + ":aepcomposeui", + ":aepuitemplates" +) \ No newline at end of file From c5370ea11cacbe3cec8262324545a142ddc25cc5 Mon Sep 17 00:00:00 2001 From: Navratan Soni Date: Thu, 26 Sep 2024 22:09:33 +0530 Subject: [PATCH 3/6] Added classes AepUiTemplate --- .../marketing/mobile/aepuitemplates/AepUiTemplate.kt | 5 +++++ .../mobile/aepuitemplates/SmallImageTemplate.kt | 10 ++++++++++ .../mobile/aepuitemplates/utils/AepUiTemplateType.kt | 8 ++++++++ 3 files changed, 23 insertions(+) create mode 100644 code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/AepUiTemplate.kt create mode 100644 code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/SmallImageTemplate.kt create mode 100644 code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/utils/AepUiTemplateType.kt diff --git a/code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/AepUiTemplate.kt b/code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/AepUiTemplate.kt new file mode 100644 index 00000000..1a84e2fd --- /dev/null +++ b/code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/AepUiTemplate.kt @@ -0,0 +1,5 @@ +package com.adobe.marketing.mobile.aepuitemplates + +interface AepUiTemplate { + fun getType(): String +} \ No newline at end of file diff --git a/code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/SmallImageTemplate.kt b/code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/SmallImageTemplate.kt new file mode 100644 index 00000000..81bfceb9 --- /dev/null +++ b/code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/SmallImageTemplate.kt @@ -0,0 +1,10 @@ +package com.adobe.marketing.mobile.aepuitemplates + +import com.adobe.marketing.mobile.aepuitemplates.utils.AepUiTemplateType + +class SmallImageTemplate() : AepUiTemplate { + val imageUrl: String = "" + val title: String = "" + val description: String = "" + override fun getType() = AepUiTemplateType.SMALL_IMAGE.typeName +} diff --git a/code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/utils/AepUiTemplateType.kt b/code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/utils/AepUiTemplateType.kt new file mode 100644 index 00000000..4633a826 --- /dev/null +++ b/code/aepuitemplates/src/main/java/com/adobe/marketing/mobile/aepuitemplates/utils/AepUiTemplateType.kt @@ -0,0 +1,8 @@ +package com.adobe.marketing.mobile.aepuitemplates.utils + +enum class AepUiTemplateType(val typeName: String) { + + SMALL_IMAGE("SmallImage"), + LARGE_IMAGE("LargeImage") + +} \ No newline at end of file From 6282a8e884f6131187649615e34b79865c745a65 Mon Sep 17 00:00:00 2001 From: Navratan Soni Date: Thu, 26 Sep 2024 22:55:15 +0530 Subject: [PATCH 4/6] temp commit --- code/aepcomposeui/build.gradle.kts | 6 + .../mobile/marketing/uiengine/aepui/AepUi.kt | 42 +++++ .../uiengine/aepui/components/AepList.kt | 164 ++++++++++++++++++ .../aepui/components/LargeImageCard.kt | 116 +++++++++++++ .../aepui/components/SmallImageCard.kt | 93 ++++++++++ .../uiengine/aepui/state/AepUiState.kt | 6 + .../uiengine/aepui/style/AEPTextExt.kt | 24 +++ .../uiengine/aepui/style/AepUiStyle.kt | 27 +++ .../contentprovider/AepUiContentProvider.kt | 21 +++ .../uiengine/interactions/UiEvent.kt | 12 ++ .../uiengine/observers/AepUiEventObserver.kt | 7 + code/gradle/libs.versions.toml | 36 ++++ 12 files changed, 554 insertions(+) create mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/AepUi.kt create mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/AepList.kt create mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/LargeImageCard.kt create mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/SmallImageCard.kt create mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/state/AepUiState.kt create mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AEPTextExt.kt create mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AepUiStyle.kt create mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/contentprovider/AepUiContentProvider.kt create mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/interactions/UiEvent.kt create mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/observers/AepUiEventObserver.kt create mode 100644 code/gradle/libs.versions.toml diff --git a/code/aepcomposeui/build.gradle.kts b/code/aepcomposeui/build.gradle.kts index 1c743e90..10fbe7bc 100644 --- a/code/aepcomposeui/build.gradle.kts +++ b/code/aepcomposeui/build.gradle.kts @@ -29,6 +29,12 @@ dependencies { implementation("com.adobe.marketing.mobile:core:$mavenCoreVersion") testImplementation("org.robolectric:robolectric:4.7") testImplementation("io.mockk:mockk:1.13.11") + api(project(":aepuitemplates")) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) } diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/AepUi.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/AepUi.kt new file mode 100644 index 00000000..be1c872b --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/AepUi.kt @@ -0,0 +1,42 @@ +package com.adobe.mobile.marketing.uiengine.aepui + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import com.adobe.marketing.mobile.aepuitemplates.AepUiTemplate +import com.adobe.marketing.mobile.aepuitemplates.SmallImageTemplate +import com.adobe.mobile.marketing.uiengine.aepui.state.AepUiState +import com.adobe.mobile.marketing.uiengine.aepui.state.SmallImageUIState + + +/** + * Represents a UI component that can be rendered in the AepUI Engine. Binds a template data with a tracking observer. + * This is a sealed interface that can be implemented by different UI components like [SmallImageAepUi], [LargeImageAepUi], etc. + * This allows restricting the type of template and observer that can be associated with a UI component, which can be later + * used in the app to render the UI component via UIComposeExtensions. + * @param T The type of the template associated with the UI component. + * @param S The type of the state associated with the UI component. + */ +sealed interface AepUI { + fun getTemplate(): T + fun getState(): S + fun updateState(newState: S) +} + + +class SmallImageAepUi( + private val template: SmallImageTemplate, + state: SmallImageUIState +) : AepUI { + private val _state = mutableStateOf(state) + override fun updateState(newState: SmallImageUIState) { + _state.value = newState + } + + override fun getTemplate(): SmallImageTemplate { + return template + } + + override fun getState(): SmallImageUIState { + return _state.value + } +} \ No newline at end of file diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/AepList.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/AepList.kt new file mode 100644 index 00000000..aace638b --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/AepList.kt @@ -0,0 +1,164 @@ +package com.adobe.mobile.marketing.uiengine.aepui.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.compose.viewModel +import com.adobe.mobile.marketing.uiengine.aepui.AepUI +import com.adobe.mobile.marketing.uiengine.aepui.LargeImageAepUi +import com.adobe.mobile.marketing.uiengine.aepui.SmallImageAepUi +import com.adobe.mobile.marketing.uiengine.contentprovider.AepUiContentProvider +import com.adobe.mobile.marketing.uiengine.observers.AepUiEventObserver +import com.adobe.mobile.marketing.uiengine.aepui.state.LargeImageUIState +import com.adobe.mobile.marketing.uiengine.aepui.state.SmallImageUIState +import com.adobe.mobile.marketing.uiengine.aepui.style.AepUiStyle +import com.adobe.mobile.marketing.uitemplates.LargeImageTemplate +import com.adobe.mobile.marketing.uitemplates.SmallImageTemplate +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch + +/** + * Composable for rendering a list of AEP UI components. + * Maintains a list of AEP UI components and renders them using the provided container. + */ +@Composable +fun AepList( + contentProvider: AepUiContentProvider, + aepUiEventObserver: AepUiEventObserver, + aepUiStyle: AepUiStyle, + viewModelKey: String, + container: @Composable (@Composable () -> Unit) -> Unit = { content -> + // Default to a Column if no container is provided + LazyColumn( + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + item { + content() + } + } + } +) { + val viewModel: AepListViewModel = viewModel( + factory = AepComposableViewModelFactory( + contentProvider + ), + key = viewModelKey + ) + val uiList = viewModel.uiList.collectAsStateWithLifecycle() + + container { + uiList.value.forEach { ui -> + val uiAsComposable = asComposable(ui, aepUiEventObserver, aepUiStyle) + uiAsComposable.invoke() + } + + Button(onClick = { viewModel.loadMore() }, Modifier.fillMaxWidth()) { + Text(text = "Load More") + } + } +} + + +private fun asComposable( + aepUI: AepUI<*, *>, + observer: AepUiEventObserver, + aepUiStyle: AepUiStyle +): @Composable () -> Unit { + return when (aepUI) { + is LargeImageAepUi -> { + { + val state = aepUI.getState() + if (!state.dismissed) { + LargeImageCard( + ui = aepUI, + style = aepUiStyle.largeImageAepUiStyle, + observer = observer + ) + } + } + } + + is SmallImageAepUi -> { + { + val state = aepUI.getState() + if (!state.dismissed) { + SmallImageCard( + ui = aepUI, + style = aepUiStyle.smallImageAepUiStyle, + observer = observer + ) + } + } + } + + else -> throw IllegalArgumentException("Unknown template type") + } +} + + +private class AepListViewModel( + private val contentProvider: AepUiContentProvider, +) : ViewModel() { + + private val _uiList = MutableStateFlow(listOf>()) + val uiList: StateFlow>> = _uiList + + init { + viewModelScope.launch { + contentProvider.getContent().collect { templates -> + + val uiList = templates.map { template -> + + val aepUiState: AepUI<*, *> = when (template) { + is LargeImageTemplate -> LargeImageAepUi( + template, + LargeImageUIState() + ) + + is SmallImageTemplate -> SmallImageAepUi( + template, + SmallImageUIState(title = template.title?.content) + ) + + else -> throw IllegalArgumentException("Unknown template type") + } + + aepUiState + } + _uiList.value = uiList + } + } + } + + fun loadMore() { + viewModelScope.launch { + contentProvider.refreshContent() + } + } +} + +private class AepComposableViewModelFactory( + private val contentProvider: AepUiContentProvider +) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(AepListViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return AepListViewModel(contentProvider) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/LargeImageCard.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/LargeImageCard.kt new file mode 100644 index 00000000..25bbcce9 --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/LargeImageCard.kt @@ -0,0 +1,116 @@ +package com.adobe.mobile.marketing.uiengine.aepui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.adobe.mobile.marketing.uiengine.aepui.LargeImageAepUi +import com.adobe.mobile.marketing.uiengine.aepui.style.AepUiStyle +import com.adobe.mobile.marketing.uiengine.aepui.style.LargeImageAepUiStyle +import com.adobe.mobile.marketing.uiengine.interactions.UIEvent +import com.adobe.mobile.marketing.uiengine.observers.AepUiEventObserver + +/** + * Composable for rendering a Large Image Card + */ +@Composable +internal fun LargeImageCard( + ui: LargeImageAepUi, + style: LargeImageAepUiStyle, + observer: AepUiEventObserver?, +) { + + LaunchedEffect(key1 = Unit) { + observer?.onEvent(UIEvent.Display(ui)) + } + + DisposableEffect(Unit) { + onDispose { + observer?.onEvent(UIEvent.Dismiss(ui)) + } + } + + Card( + shape = RoundedCornerShape(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) + ) { + Column(modifier = Modifier.fillMaxWidth()) { + Box( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(4f / 3f) // Aspect ratio to maintain 3/4th image height + ) { + AsyncImage( + model = ui.getTemplate().imageUrl, + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp)) + ) + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Black.copy(alpha = 0.3f)) // Dark overlay for better text visibility + ) + + ui.getTemplate().title?.let { + Text( + text = it.content, + style = style.getTitleTextStyle(ui.getTemplate()), + modifier = Modifier.fillMaxWidth() + ) + } + } + + Spacer(modifier = Modifier.height(8.dp)) + + Column( + modifier = Modifier.padding(16.dp) + ) { + Text( + text = ui.getTemplate().description, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Button( + onClick = { + observer?.onEvent(UIEvent.Click(ui)) + }, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) { + Text(text = ui.getTemplate().cta) + } + } + } + } +} \ No newline at end of file diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/SmallImageCard.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/SmallImageCard.kt new file mode 100644 index 00000000..0ca35577 --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/SmallImageCard.kt @@ -0,0 +1,93 @@ +package com.adobe.mobile.marketing.uiengine.aepui.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.adobe.mobile.marketing.uiengine.aepui.SmallImageAepUi +import com.adobe.mobile.marketing.uiengine.aepui.style.SmallImageAepUiStyle +import com.adobe.mobile.marketing.uiengine.interactions.UIEvent +import com.adobe.mobile.marketing.uiengine.observers.AepUiEventObserver + +/** + * Composable for rendering a Small Image Card + */ +@Composable +internal fun SmallImageCard( + ui: SmallImageAepUi, + style: SmallImageAepUiStyle, + observer: AepUiEventObserver?, +) { + + LaunchedEffect(key1 = Unit) { + observer?.onEvent(UIEvent.Display(ui)) + } + + DisposableEffect(key1 = Unit) { + onDispose { + observer?.onEvent(UIEvent.Dismiss(ui)) + } + } + + Card( + shape = RoundedCornerShape(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + .clickable { + observer?.onEvent(UIEvent.Click(ui)) + }, + elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) + ) { + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + AsyncImage( + model = ui.getTemplate().imageUrl, + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .size(64.dp) + .clip(RoundedCornerShape(8.dp)) + ) + + Spacer(modifier = Modifier.width(16.dp)) + + Column( + verticalArrangement = Arrangement.Center + ) { + ui.getState().title?.let { + Text( + text = it, + style = style.getTitleTextStyle(ui.getTemplate()), + ) + } + Text( + text = ui.getTemplate().description, + style = MaterialTheme.typography.bodyMedium + ) + } + } + } +} \ No newline at end of file diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/state/AepUiState.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/state/AepUiState.kt new file mode 100644 index 00000000..27470802 --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/state/AepUiState.kt @@ -0,0 +1,6 @@ +package com.adobe.mobile.marketing.uiengine.aepui.state + +sealed interface AepUiState + +data class SmallImageUIState(val title: String?, val dismissed: Boolean = false) : AepUiState +data class LargeImageUIState(val dismissed: Boolean = false) : AepUiState diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AEPTextExt.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AEPTextExt.kt new file mode 100644 index 00000000..6b3bd71c --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AEPTextExt.kt @@ -0,0 +1,24 @@ +package com.adobe.mobile.marketing.uiengine.aepui.style + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign + +// only needed when we support server side styling +fun AEPText.getComposeTextStyle(): TextStyle { + var textStyle = TextStyle() + if (color != null) { + textStyle = textStyle.merge(Color(android.graphics.Color.parseColor(color))) + } + if (align != null) { + textStyle = textStyle.merge(textAlign = when (align) { + "center" -> TextAlign.Center + "left" -> TextAlign.Left + "right" -> TextAlign.Right + "start" -> TextAlign.Start + "end" -> TextAlign.End + else -> TextAlign.Unspecified + }) + } + return textStyle +} \ No newline at end of file diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AepUiStyle.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AepUiStyle.kt new file mode 100644 index 00000000..df32bc3c --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AepUiStyle.kt @@ -0,0 +1,27 @@ +package com.adobe.mobile.marketing.uiengine.aepui.style + +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import com.adobe.marketing.mobile.aepuitemplates.SmallImageTemplate + +// class containing style for all UI types supported by the UI Engine +class AepUiStyle( + val smallImageAepUiStyle: SmallImageAepUiStyle = SmallImageAepUiStyle() +) + +class SmallImageAepUiStyle( + private val titleTextStyle: TextStyle? = null +) { + private var defaultTitleColor = Color.Red + + // order of merging is important here + // App style > Server style > Default style + @Composable + fun getTitleTextStyle(template: SmallImageTemplate): TextStyle { + return MaterialTheme.typography.h5.copy(defaultTitleColor) + .merge(template.title?.getComposeTextStyle()) + .merge(titleTextStyle) + } +} \ No newline at end of file diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/contentprovider/AepUiContentProvider.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/contentprovider/AepUiContentProvider.kt new file mode 100644 index 00000000..8223d3e1 --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/contentprovider/AepUiContentProvider.kt @@ -0,0 +1,21 @@ +package com.adobe.mobile.marketing.uiengine.contentprovider + +import com.adobe.marketing.mobile.aepuitemplates.AepUiTemplate +import kotlinx.coroutines.flow.Flow + +/** + * Interface to provide content for the UI and a backing tracker for analytics. + * This can be treated as a strategy to provide content and tracking for the UI. + * Allows components like extensions which provide content to expose an implementation + * for the app to use. + */ +interface AepUiContentProvider { + /** + * Retrieves the content for the UI. + * @return The content for the UI as a flow of [AepTemplate]s. + */ + suspend fun getContent(): Flow> + + suspend fun refreshContent() +} + diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/interactions/UiEvent.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/interactions/UiEvent.kt new file mode 100644 index 00000000..2452763e --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/interactions/UiEvent.kt @@ -0,0 +1,12 @@ +package com.adobe.mobile.marketing.uiengine.interactions + +import com.adobe.marketing.mobile.aepuitemplates.AepUiTemplate +import com.adobe.mobile.marketing.uiengine.aepui.AepUI +import com.adobe.mobile.marketing.uiengine.aepui.state.AepUiState + +sealed interface UIEvent { + data class Display(val _aepui: AepUI) : UIEvent + data class Click(val _aepui: AepUI) : UIEvent + data class Dismiss(val _aepui: AepUI) : UIEvent + data class Expand(val _aepui: AepUI) : UIEvent +} diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/observers/AepUiEventObserver.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/observers/AepUiEventObserver.kt new file mode 100644 index 00000000..97a74cfb --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/observers/AepUiEventObserver.kt @@ -0,0 +1,7 @@ +package com.adobe.mobile.marketing.uiengine.observers + +import com.adobe.mobile.marketing.uiengine.interactions.UIEvent + +interface AepUiEventObserver { + fun onEvent(event: UIEvent<*, *>) +} \ No newline at end of file diff --git a/code/gradle/libs.versions.toml b/code/gradle/libs.versions.toml new file mode 100644 index 00000000..40ece485 --- /dev/null +++ b/code/gradle/libs.versions.toml @@ -0,0 +1,36 @@ +[versions] +agp = "8.5.1" +kotlin = "1.9.0" +coreKtx = "1.13.1" +junit = "4.13.2" +junitVersion = "1.2.1" +espressoCore = "3.6.1" +lifecycleRuntimeKtx = "2.8.4" +activityCompose = "1.9.1" +composeBom = "2024.04.01" +appcompat = "1.7.0" +material = "1.12.0" + +[libraries] +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } +androidx-ui = { group = "androidx.compose.ui", name = "ui" } +androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } +androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } +androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } +androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } +androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +android-library = { id = "com.android.library", version.ref = "agp" } + From 027e071411719b9a0a36d51747e2b8e04fc7c9da Mon Sep 17 00:00:00 2001 From: Navratan Soni Date: Fri, 27 Sep 2024 18:15:16 +0530 Subject: [PATCH 5/6] Cleanup --- code/aepcomposeui/build.gradle.kts | 2 + .../uiengine/aepui/components/AepList.kt | 29 +---- .../aepui/components/LargeImageCard.kt | 116 ------------------ .../aepui/components/SmallImageCard.kt | 15 +-- code/gradle/libs.versions.toml | 4 + 5 files changed, 9 insertions(+), 157 deletions(-) delete mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/LargeImageCard.kt diff --git a/code/aepcomposeui/build.gradle.kts b/code/aepcomposeui/build.gradle.kts index 10fbe7bc..4778aa0d 100644 --- a/code/aepcomposeui/build.gradle.kts +++ b/code/aepcomposeui/build.gradle.kts @@ -35,6 +35,8 @@ dependencies { implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) + implementation(libs.androidx.lifecycle.viewmodel.compose) + implementation(libs.androidx.lifecycle.runtime.compose) } diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/AepList.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/AepList.kt index aace638b..66f2e097 100644 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/AepList.kt +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/AepList.kt @@ -1,8 +1,6 @@ package com.adobe.mobile.marketing.uiengine.aepui.components import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Button @@ -16,16 +14,13 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.compose.viewModel +import com.adobe.marketing.mobile.aepuitemplates.SmallImageTemplate import com.adobe.mobile.marketing.uiengine.aepui.AepUI -import com.adobe.mobile.marketing.uiengine.aepui.LargeImageAepUi import com.adobe.mobile.marketing.uiengine.aepui.SmallImageAepUi import com.adobe.mobile.marketing.uiengine.contentprovider.AepUiContentProvider import com.adobe.mobile.marketing.uiengine.observers.AepUiEventObserver -import com.adobe.mobile.marketing.uiengine.aepui.state.LargeImageUIState import com.adobe.mobile.marketing.uiengine.aepui.state.SmallImageUIState import com.adobe.mobile.marketing.uiengine.aepui.style.AepUiStyle -import com.adobe.mobile.marketing.uitemplates.LargeImageTemplate -import com.adobe.mobile.marketing.uitemplates.SmallImageTemplate import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch @@ -79,19 +74,6 @@ private fun asComposable( aepUiStyle: AepUiStyle ): @Composable () -> Unit { return when (aepUI) { - is LargeImageAepUi -> { - { - val state = aepUI.getState() - if (!state.dismissed) { - LargeImageCard( - ui = aepUI, - style = aepUiStyle.largeImageAepUiStyle, - observer = observer - ) - } - } - } - is SmallImageAepUi -> { { val state = aepUI.getState() @@ -120,20 +102,13 @@ private class AepListViewModel( init { viewModelScope.launch { contentProvider.getContent().collect { templates -> - val uiList = templates.map { template -> val aepUiState: AepUI<*, *> = when (template) { - is LargeImageTemplate -> LargeImageAepUi( - template, - LargeImageUIState() - ) - is SmallImageTemplate -> SmallImageAepUi( template, - SmallImageUIState(title = template.title?.content) + SmallImageUIState(title = template.title) ) - else -> throw IllegalArgumentException("Unknown template type") } diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/LargeImageCard.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/LargeImageCard.kt deleted file mode 100644 index 25bbcce9..00000000 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/LargeImageCard.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.adobe.mobile.marketing.uiengine.aepui.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage -import com.adobe.mobile.marketing.uiengine.aepui.LargeImageAepUi -import com.adobe.mobile.marketing.uiengine.aepui.style.AepUiStyle -import com.adobe.mobile.marketing.uiengine.aepui.style.LargeImageAepUiStyle -import com.adobe.mobile.marketing.uiengine.interactions.UIEvent -import com.adobe.mobile.marketing.uiengine.observers.AepUiEventObserver - -/** - * Composable for rendering a Large Image Card - */ -@Composable -internal fun LargeImageCard( - ui: LargeImageAepUi, - style: LargeImageAepUiStyle, - observer: AepUiEventObserver?, -) { - - LaunchedEffect(key1 = Unit) { - observer?.onEvent(UIEvent.Display(ui)) - } - - DisposableEffect(Unit) { - onDispose { - observer?.onEvent(UIEvent.Dismiss(ui)) - } - } - - Card( - shape = RoundedCornerShape(8.dp), - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) - ) { - Column(modifier = Modifier.fillMaxWidth()) { - Box( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(4f / 3f) // Aspect ratio to maintain 3/4th image height - ) { - AsyncImage( - model = ui.getTemplate().imageUrl, - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = Modifier - .fillMaxSize() - .clip(RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp)) - ) - - Box( - modifier = Modifier - .fillMaxSize() - .background(Color.Black.copy(alpha = 0.3f)) // Dark overlay for better text visibility - ) - - ui.getTemplate().title?.let { - Text( - text = it.content, - style = style.getTitleTextStyle(ui.getTemplate()), - modifier = Modifier.fillMaxWidth() - ) - } - } - - Spacer(modifier = Modifier.height(8.dp)) - - Column( - modifier = Modifier.padding(16.dp) - ) { - Text( - text = ui.getTemplate().description, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.fillMaxWidth() - ) - - Spacer(modifier = Modifier.height(8.dp)) - - Button( - onClick = { - observer?.onEvent(UIEvent.Click(ui)) - }, - modifier = Modifier.align(Alignment.CenterHorizontally) - ) { - Text(text = ui.getTemplate().cta) - } - } - } - } -} \ No newline at end of file diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/SmallImageCard.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/SmallImageCard.kt index 0ca35577..8720d162 100644 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/SmallImageCard.kt +++ b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/SmallImageCard.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card @@ -19,11 +18,7 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage import com.adobe.mobile.marketing.uiengine.aepui.SmallImageAepUi import com.adobe.mobile.marketing.uiengine.aepui.style.SmallImageAepUiStyle import com.adobe.mobile.marketing.uiengine.interactions.UIEvent @@ -63,15 +58,7 @@ internal fun SmallImageCard( modifier = Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { - AsyncImage( - model = ui.getTemplate().imageUrl, - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = Modifier - .size(64.dp) - .clip(RoundedCornerShape(8.dp)) - ) - + // TODO - Add image support Spacer(modifier = Modifier.width(16.dp)) Column( diff --git a/code/gradle/libs.versions.toml b/code/gradle/libs.versions.toml index 40ece485..84d5d098 100644 --- a/code/gradle/libs.versions.toml +++ b/code/gradle/libs.versions.toml @@ -9,6 +9,8 @@ lifecycleRuntimeKtx = "2.8.4" activityCompose = "1.9.1" composeBom = "2024.04.01" appcompat = "1.7.0" +lifecycleViewModelCompose = "2.8.1" +lifecycleRuntimeCompose = "2.8.1" material = "1.12.0" [libraries] @@ -17,6 +19,8 @@ junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" } +androidx-lifecycle-viewmodel-compose = {group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewModelCompose"} androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } androidx-ui = { group = "androidx.compose.ui", name = "ui" } From 32feb970b48f4d2eb34b7723505290bcb0894285 Mon Sep 17 00:00:00 2001 From: Navratan Soni Date: Fri, 27 Sep 2024 18:39:42 +0530 Subject: [PATCH 6/6] Changes package --- .../mobile/aepcomposeui}/aepui/AepUi.kt | 6 +++--- .../aepcomposeui}/aepui/components/AepList.kt | 14 +++++++------- .../aepui/components/SmallImageCard.kt | 10 +++++----- .../mobile/aepcomposeui}/aepui/state/AepUiState.kt | 2 +- .../mobile/aepcomposeui}/aepui/style/AEPTextExt.kt | 2 +- .../mobile/aepcomposeui}/aepui/style/AepUiStyle.kt | 2 +- .../contentprovider/AepUiContentProvider.kt | 2 +- .../mobile/aepcomposeui}/interactions/UiEvent.kt | 6 +++--- .../aepcomposeui/observers/AepUiEventObserver.kt | 7 +++++++ .../uiengine/observers/AepUiEventObserver.kt | 7 ------- 10 files changed, 29 insertions(+), 29 deletions(-) rename code/aepcomposeui/src/main/java/com/adobe/{mobile/marketing/uiengine => marketing/mobile/aepcomposeui}/aepui/AepUi.kt (87%) rename code/aepcomposeui/src/main/java/com/adobe/{mobile/marketing/uiengine => marketing/mobile/aepcomposeui}/aepui/components/AepList.kt (89%) rename code/aepcomposeui/src/main/java/com/adobe/{mobile/marketing/uiengine => marketing/mobile/aepcomposeui}/aepui/components/SmallImageCard.kt (86%) rename code/aepcomposeui/src/main/java/com/adobe/{mobile/marketing/uiengine => marketing/mobile/aepcomposeui}/aepui/state/AepUiState.kt (76%) rename code/aepcomposeui/src/main/java/com/adobe/{mobile/marketing/uiengine => marketing/mobile/aepcomposeui}/aepui/style/AEPTextExt.kt (92%) rename code/aepcomposeui/src/main/java/com/adobe/{mobile/marketing/uiengine => marketing/mobile/aepcomposeui}/aepui/style/AepUiStyle.kt (93%) rename code/aepcomposeui/src/main/java/com/adobe/{mobile/marketing/uiengine => marketing/mobile/aepcomposeui}/contentprovider/AepUiContentProvider.kt (90%) rename code/aepcomposeui/src/main/java/com/adobe/{mobile/marketing/uiengine => marketing/mobile/aepcomposeui}/interactions/UiEvent.kt (73%) create mode 100644 code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/observers/AepUiEventObserver.kt delete mode 100644 code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/observers/AepUiEventObserver.kt diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/AepUi.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/AepUi.kt similarity index 87% rename from code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/AepUi.kt rename to code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/AepUi.kt index be1c872b..4c5c6cc9 100644 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/AepUi.kt +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/AepUi.kt @@ -1,11 +1,11 @@ -package com.adobe.mobile.marketing.uiengine.aepui +package com.adobe.marketing.mobile.aepcomposeui.aepui import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import com.adobe.marketing.mobile.aepuitemplates.AepUiTemplate import com.adobe.marketing.mobile.aepuitemplates.SmallImageTemplate -import com.adobe.mobile.marketing.uiengine.aepui.state.AepUiState -import com.adobe.mobile.marketing.uiengine.aepui.state.SmallImageUIState +import com.adobe.marketing.mobile.aepcomposeui.aepui.state.AepUiState +import com.adobe.marketing.mobile.aepcomposeui.aepui.state.SmallImageUIState /** diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/AepList.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/components/AepList.kt similarity index 89% rename from code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/AepList.kt rename to code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/components/AepList.kt index 66f2e097..8eb29e7c 100644 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/AepList.kt +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/components/AepList.kt @@ -1,4 +1,4 @@ -package com.adobe.mobile.marketing.uiengine.aepui.components +package com.adobe.marketing.mobile.aepcomposeui.aepui.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxWidth @@ -15,12 +15,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.compose.viewModel import com.adobe.marketing.mobile.aepuitemplates.SmallImageTemplate -import com.adobe.mobile.marketing.uiengine.aepui.AepUI -import com.adobe.mobile.marketing.uiengine.aepui.SmallImageAepUi -import com.adobe.mobile.marketing.uiengine.contentprovider.AepUiContentProvider -import com.adobe.mobile.marketing.uiengine.observers.AepUiEventObserver -import com.adobe.mobile.marketing.uiengine.aepui.state.SmallImageUIState -import com.adobe.mobile.marketing.uiengine.aepui.style.AepUiStyle +import com.adobe.marketing.mobile.aepcomposeui.aepui.AepUI +import com.adobe.marketing.mobile.aepcomposeui.aepui.SmallImageAepUi +import com.adobe.marketing.mobile.aepcomposeui.contentprovider.AepUiContentProvider +import com.adobe.marketing.mobile.aepcomposeui.observers.AepUiEventObserver +import com.adobe.marketing.mobile.aepcomposeui.aepui.state.SmallImageUIState +import com.adobe.marketing.mobile.aepcomposeui.aepui.style.AepUiStyle import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/SmallImageCard.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/components/SmallImageCard.kt similarity index 86% rename from code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/SmallImageCard.kt rename to code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/components/SmallImageCard.kt index 8720d162..fa571043 100644 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/components/SmallImageCard.kt +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/components/SmallImageCard.kt @@ -1,4 +1,4 @@ -package com.adobe.mobile.marketing.uiengine.aepui.components +package com.adobe.marketing.mobile.aepcomposeui.aepui.components import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -19,10 +19,10 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.adobe.mobile.marketing.uiengine.aepui.SmallImageAepUi -import com.adobe.mobile.marketing.uiengine.aepui.style.SmallImageAepUiStyle -import com.adobe.mobile.marketing.uiengine.interactions.UIEvent -import com.adobe.mobile.marketing.uiengine.observers.AepUiEventObserver +import com.adobe.marketing.mobile.aepcomposeui.aepui.SmallImageAepUi +import com.adobe.marketing.mobile.aepcomposeui.aepui.style.SmallImageAepUiStyle +import com.adobe.marketing.mobile.aepcomposeui.interactions.UIEvent +import com.adobe.marketing.mobile.aepcomposeui.observers.AepUiEventObserver /** * Composable for rendering a Small Image Card diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/state/AepUiState.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/state/AepUiState.kt similarity index 76% rename from code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/state/AepUiState.kt rename to code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/state/AepUiState.kt index 27470802..b6dc6b4a 100644 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/state/AepUiState.kt +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/state/AepUiState.kt @@ -1,4 +1,4 @@ -package com.adobe.mobile.marketing.uiengine.aepui.state +package com.adobe.marketing.mobile.aepcomposeui.aepui.state sealed interface AepUiState diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AEPTextExt.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/style/AEPTextExt.kt similarity index 92% rename from code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AEPTextExt.kt rename to code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/style/AEPTextExt.kt index 6b3bd71c..c503f7ec 100644 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AEPTextExt.kt +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/style/AEPTextExt.kt @@ -1,4 +1,4 @@ -package com.adobe.mobile.marketing.uiengine.aepui.style +package com.adobe.marketing.mobile.aepcomposeui.aepui.style import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AepUiStyle.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/style/AepUiStyle.kt similarity index 93% rename from code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AepUiStyle.kt rename to code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/style/AepUiStyle.kt index df32bc3c..fdc088a3 100644 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/aepui/style/AepUiStyle.kt +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/style/AepUiStyle.kt @@ -1,4 +1,4 @@ -package com.adobe.mobile.marketing.uiengine.aepui.style +package com.adobe.marketing.mobile.aepcomposeui.aepui.style import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/contentprovider/AepUiContentProvider.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/contentprovider/AepUiContentProvider.kt similarity index 90% rename from code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/contentprovider/AepUiContentProvider.kt rename to code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/contentprovider/AepUiContentProvider.kt index 8223d3e1..6f8f8b0b 100644 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/contentprovider/AepUiContentProvider.kt +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/contentprovider/AepUiContentProvider.kt @@ -1,4 +1,4 @@ -package com.adobe.mobile.marketing.uiengine.contentprovider +package com.adobe.marketing.mobile.aepcomposeui.contentprovider import com.adobe.marketing.mobile.aepuitemplates.AepUiTemplate import kotlinx.coroutines.flow.Flow diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/interactions/UiEvent.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/interactions/UiEvent.kt similarity index 73% rename from code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/interactions/UiEvent.kt rename to code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/interactions/UiEvent.kt index 2452763e..d03e85d7 100644 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/interactions/UiEvent.kt +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/interactions/UiEvent.kt @@ -1,8 +1,8 @@ -package com.adobe.mobile.marketing.uiengine.interactions +package com.adobe.marketing.mobile.aepcomposeui.interactions import com.adobe.marketing.mobile.aepuitemplates.AepUiTemplate -import com.adobe.mobile.marketing.uiengine.aepui.AepUI -import com.adobe.mobile.marketing.uiengine.aepui.state.AepUiState +import com.adobe.marketing.mobile.aepcomposeui.aepui.AepUI +import com.adobe.marketing.mobile.aepcomposeui.aepui.state.AepUiState sealed interface UIEvent { data class Display(val _aepui: AepUI) : UIEvent diff --git a/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/observers/AepUiEventObserver.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/observers/AepUiEventObserver.kt new file mode 100644 index 00000000..6e31e29f --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/observers/AepUiEventObserver.kt @@ -0,0 +1,7 @@ +package com.adobe.marketing.mobile.aepcomposeui.observers + +import com.adobe.marketing.mobile.aepcomposeui.interactions.UIEvent + +interface AepUiEventObserver { + fun onEvent(event: UIEvent<*, *>) +} \ No newline at end of file diff --git a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/observers/AepUiEventObserver.kt b/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/observers/AepUiEventObserver.kt deleted file mode 100644 index 97a74cfb..00000000 --- a/code/aepcomposeui/src/main/java/com/adobe/mobile/marketing/uiengine/observers/AepUiEventObserver.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.adobe.mobile.marketing.uiengine.observers - -import com.adobe.mobile.marketing.uiengine.interactions.UIEvent - -interface AepUiEventObserver { - fun onEvent(event: UIEvent<*, *>) -} \ No newline at end of file