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..4778aa0d --- /dev/null +++ b/code/aepcomposeui/build.gradle.kts @@ -0,0 +1,42 @@ +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") + 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) + implementation(libs.androidx.lifecycle.viewmodel.compose) + implementation(libs.androidx.lifecycle.runtime.compose) +} + + 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/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/AepUi.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/AepUi.kt new file mode 100644 index 00000000..4c5c6cc9 --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/AepUi.kt @@ -0,0 +1,42 @@ +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.marketing.mobile.aepcomposeui.aepui.state.AepUiState +import com.adobe.marketing.mobile.aepcomposeui.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/marketing/mobile/aepcomposeui/aepui/components/AepList.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/components/AepList.kt new file mode 100644 index 00000000..8eb29e7c --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/components/AepList.kt @@ -0,0 +1,139 @@ +package com.adobe.marketing.mobile.aepcomposeui.aepui.components + +import androidx.compose.foundation.layout.Arrangement +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.marketing.mobile.aepuitemplates.SmallImageTemplate +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 + +/** + * 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 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 SmallImageTemplate -> SmallImageAepUi( + template, + SmallImageUIState(title = template.title) + ) + 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/marketing/mobile/aepcomposeui/aepui/components/SmallImageCard.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/components/SmallImageCard.kt new file mode 100644 index 00000000..fa571043 --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/components/SmallImageCard.kt @@ -0,0 +1,80 @@ +package com.adobe.marketing.mobile.aepcomposeui.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.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.unit.dp +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 + */ +@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 + ) { + // TODO - Add image support + 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/marketing/mobile/aepcomposeui/aepui/state/AepUiState.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/state/AepUiState.kt new file mode 100644 index 00000000..b6dc6b4a --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/state/AepUiState.kt @@ -0,0 +1,6 @@ +package com.adobe.marketing.mobile.aepcomposeui.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/marketing/mobile/aepcomposeui/aepui/style/AEPTextExt.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/style/AEPTextExt.kt new file mode 100644 index 00000000..c503f7ec --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/style/AEPTextExt.kt @@ -0,0 +1,24 @@ +package com.adobe.marketing.mobile.aepcomposeui.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/marketing/mobile/aepcomposeui/aepui/style/AepUiStyle.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/style/AepUiStyle.kt new file mode 100644 index 00000000..fdc088a3 --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/aepui/style/AepUiStyle.kt @@ -0,0 +1,27 @@ +package com.adobe.marketing.mobile.aepcomposeui.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/marketing/mobile/aepcomposeui/contentprovider/AepUiContentProvider.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/contentprovider/AepUiContentProvider.kt new file mode 100644 index 00000000..6f8f8b0b --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/contentprovider/AepUiContentProvider.kt @@ -0,0 +1,21 @@ +package com.adobe.marketing.mobile.aepcomposeui.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/marketing/mobile/aepcomposeui/interactions/UiEvent.kt b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/interactions/UiEvent.kt new file mode 100644 index 00000000..d03e85d7 --- /dev/null +++ b/code/aepcomposeui/src/main/java/com/adobe/marketing/mobile/aepcomposeui/interactions/UiEvent.kt @@ -0,0 +1,12 @@ +package com.adobe.marketing.mobile.aepcomposeui.interactions + +import com.adobe.marketing.mobile.aepuitemplates.AepUiTemplate +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 + 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/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/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/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/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 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 f87e7936..770464ee 100644 --- a/code/gradle.properties +++ b/code/gradle.properties @@ -22,6 +22,16 @@ 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 + +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/gradle/libs.versions.toml b/code/gradle/libs.versions.toml new file mode 100644 index 00000000..84d5d098 --- /dev/null +++ b/code/gradle/libs.versions.toml @@ -0,0 +1,40 @@ +[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" +lifecycleViewModelCompose = "2.8.1" +lifecycleRuntimeCompose = "2.8.1" +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-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" } +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" } + diff --git a/code/settings.gradle.kts b/code/settings.gradle.kts index 5c9ab072..f4e67753 100755 --- a/code/settings.gradle.kts +++ b/code/settings.gradle.kts @@ -30,7 +30,9 @@ dependencyResolutionManagement { } rootProject.name = "aepsdk-ui-android" -include ( - ":notificationbuilder", - ":testapp" +include( + ":notificationbuilder", + ":testapp", + ":aepcomposeui", + ":aepuitemplates" ) \ No newline at end of file