Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ kotlin {
commonMain {
dependencies {
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.ui.tooling.preview)
implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.runtime)
Expand Down
7 changes: 6 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ android-compileSdk = "36"
android-minSdk = "26"
android-targetSdk = "36"
androidx-activity = "1.12.4"
androidx-datastore = "1.1.7"
androidx-core-runtime = "2.2.0"
androidx-customview-poolingcontainer = "1.1.0"
androidx-emoji2 = "1.6.0"
Expand All @@ -12,7 +13,7 @@ androidx-test = "1.7.0"
androidx-test-junit = "1.3.0"
androidx-test-orchestrator = "1.6.1"
compose-hot-reload = "1.0.0"
compose-multiplatform = "1.10.1"
compose-multiplatform = "1.10.3"
compose-navigation = "2.9.2"
htmlconverter = "1.1.0"
kotlin = "2.3.10"
Expand All @@ -25,11 +26,14 @@ oidc = "0.16.5"
okhttp = "5.3.2"
table = "0.3.0"
uiautomator = "2.3.0"
uiToolingPreview = "1.10.3"
webkit = "1.15.0"
dnd = "0.4.0"

[libraries]
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "androidx-datastore" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" }
androidx-core-runtime = { module = "androidx.arch.core:core-runtime", version.ref = "androidx-core-runtime" }
androidx-customview-poolingcontainer = { module = "androidx.customview:customview-poolingcontainer", version.ref = "androidx-customview-poolingcontainer" }
androidx-emoji2 = { module = "androidx.emoji2:emoji2", version.ref = "androidx-emoji2" }
Expand Down Expand Up @@ -62,6 +66,7 @@ oidc-tokenstore = { module = "io.github.kalinjul.kotlin.multiplatform:oidc-token
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
table-m3 = { module = "io.github.windedge.table:table-m3", version.ref = "table" }
compose-dnd = { module = "com.mohamedrejeb.dnd:compose-dnd", version.ref = "dnd" }
ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "uiToolingPreview" }

[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
Expand Down
5 changes: 4 additions & 1 deletion samples/base-cmp-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@ kotlin {
api(project(":ui-components-cmp"))
api(project(":ui-renderer-cmp"))
api(project(":engine-webview"))
implementation(libs.androidx.datastore)
implementation(libs.androidx.datastore.preferences)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.ui.tooling.preview)
implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.materialIconsExtended)
implementation(compose.runtime)
implementation(compose.ui)
implementation(libs.androidx.lifecycle.viewmodelCompose)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.pega.constellation.sdk.kmp.samples.basecmpapp.data

import androidx.datastore.preferences.core.Preferences
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.preferencesDataStoreFile
import com.pega.constellation.sdk.kmp.ui.components.cmp.controls.form.internal.AppContext

actual fun createDataStore(): DataStore<Preferences> {
return PreferenceDataStoreFactory.create(
produceFile = { AppContext.get().preferencesDataStoreFile(DATASTORE_FILE_NAME) }
)
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.pega.constellation.sdk.kmp.samples.basecmpapp

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
Expand All @@ -8,6 +9,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.pega.constellation.sdk.kmp.samples.basecmpapp.auth.AuthState.AuthError
import com.pega.constellation.sdk.kmp.samples.basecmpapp.auth.AuthState.Authenticated
import com.pega.constellation.sdk.kmp.samples.basecmpapp.auth.AuthState.TokenExpired
import com.pega.constellation.sdk.kmp.samples.basecmpapp.data.PreferencesStore
import com.pega.constellation.sdk.kmp.samples.basecmpapp.ui.screens.login.LoginScreen
import com.pega.constellation.sdk.kmp.samples.basecmpapp.ui.screens.main.MainScreen
import com.pega.constellation.sdk.kmp.samples.basecmpapp.ui.screens.pega.PegaViewModel
Expand All @@ -30,12 +32,13 @@ fun MediaCoApp(
else -> {}
}
}

MediaCoTheme {
val darkTheme by PreferencesStore.isDarkThemeFlow.collectAsState(null)
val isDarkTheme = darkTheme ?: isSystemInDarkTheme()
MediaCoTheme(isDarkTheme = isDarkTheme) {
if (authenticated) {
MainScreen(appViewModel, pegaViewModel, servicesViewModel)
MainScreen(appViewModel, pegaViewModel, servicesViewModel, isDarkTheme)
} else {
LoginScreen(appViewModel)
LoginScreen(appViewModel, isDarkTheme = isDarkTheme)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.pega.constellation.sdk.kmp.samples.basecmpapp.data

import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map


expect fun createDataStore(): DataStore<Preferences>

const val DATASTORE_FILE_NAME = "dataStore"

object PreferencesStore {
private val dataStore = createDataStore()
private val IS_DARK_MODE = booleanPreferencesKey("is_dark_mode")

val isDarkThemeFlow: Flow<Boolean?> =
dataStore.data.map { preferences -> preferences[IS_DARK_MODE] }

suspend fun updateTheme(isDark: Boolean?) {
dataStore.edit { preferences ->
if (isDark != null) {
preferences[IS_DARK_MODE] = isDark
} else {
preferences.remove(IS_DARK_MODE)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
package com.pega.constellation.sdk.kmp.samples.basecmpapp.ui.screens.common

import androidx.compose.foundation.layout.RowScope
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.pega.constellation.sdk.kmp.base_cmp_app.generated.resources.Res
import com.pega.constellation.sdk.kmp.base_cmp_app.generated.resources.icon_contact
import com.pega.constellation.sdk.kmp.base_cmp_app.generated.resources.icon_home
Expand All @@ -27,41 +46,100 @@ fun MediaCoBottomAppBar(
selected: MainTab = MainTab.Home,
onNavItemSelected: (MainTab) -> Unit = {}
) {
NavigationBar(
containerColor = MaterialTheme.colorScheme.background,
tonalElevation = 0.dp
Surface(
modifier = Modifier.fillMaxWidth(),
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.45f),
shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp)
) {
NavItem(
"Home",
Res.drawable.icon_home,
selected = selected == MainTab.Home,
onSelected = { onNavItemSelected(MainTab.Home) }
)
NavItem(
"Services",
Res.drawable.icon_services,
selected = selected == MainTab.Services,
onSelected = { onNavItemSelected(MainTab.Services) }
)
NavItem("Offers", Res.drawable.icon_offers)
NavItem("Contact", Res.drawable.icon_contact)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 10.dp),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically
) {
NavItem(
label = "Home",
icon = Res.drawable.icon_home,
selected = selected == MainTab.Home,
onSelected = { onNavItemSelected(MainTab.Home) }
)
NavItem(
label = "Services",
icon = Res.drawable.icon_services,
selected = selected == MainTab.Services,
onSelected = { onNavItemSelected(MainTab.Services) }
)
NavItem(
label = "Offers",
icon = Res.drawable.icon_offers,
selected = false,
onSelected = { }
)
NavItem(
label = "Contact",
icon = Res.drawable.icon_contact,
selected = false,
onSelected = { }
)
}
}
}

@Composable
fun RowScope.NavItem(
private fun NavItem(
label: String,
icon: DrawableResource,
modifier: Modifier = Modifier,
selected: Boolean = false,
onSelected: () -> Unit = { }
selected: Boolean,
onSelected: () -> Unit
) {
NavigationBarItem(
selected = selected,
onClick = onSelected,
icon = { Icon(painterResource(icon), "home", modifier = modifier.height(24.dp)) },
label = { Text(label, fontSize = 12.sp) }
val scale by animateFloatAsState(
targetValue = if (selected) 1.1f else 1f,
animationSpec = spring(stiffness = Spring.StiffnessMedium)
)
val iconColor by animateColorAsState(
targetValue = if (selected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant
)
val labelColor by animateColorAsState(
targetValue = if (selected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant
)

Column(
modifier = Modifier
.clip(RoundedCornerShape(12.dp))
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = onSelected
)
.padding(horizontal = 16.dp, vertical = 6.dp)
.scale(scale),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(3.dp)
) {
Icon(
painter = painterResource(icon),
contentDescription = label,
tint = iconColor,
modifier = Modifier.size(22.dp)
)
Text(
text = label,
style = MaterialTheme.typography.labelSmall,
color = labelColor
)
if (selected) {
Box(
modifier = Modifier
.width(18.dp)
.height(3.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.primary)
)
} else {
Box(modifier = Modifier.height(3.dp))
}
}
}

@Preview(widthDp = 500)
Expand Down
Loading
Loading