diff --git a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ConsentsUISDK.kt b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ConsentsUISDK.kt index dc9f476..f465d49 100644 --- a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ConsentsUISDK.kt +++ b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ConsentsUISDK.kt @@ -43,7 +43,7 @@ object ConsentsUISDK { CustomUI.setCustomUi( customLightColorScheme?.toLightColorScheme(), customDarkColorScheme?.toDarkColorScheme(), - customTypography?.toTypography() + customTypography ) } diff --git a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/CustomColorScheme.kt b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/CustomColorScheme.kt index 600bfd0..ba5c505 100644 --- a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/CustomColorScheme.kt +++ b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/CustomColorScheme.kt @@ -4,6 +4,8 @@ import androidx.compose.material3.ColorScheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import com.cookieinformation.mobileconsents.sdk.ui.ui.MaterialColorSchemeWithCustom /** * Customizable colors (based as much as possible on component colors from https://m3.material.io/components) @@ -17,6 +19,14 @@ import androidx.compose.ui.graphics.Color * outline * outlineVariant * primaryContainer + * +//// Additional //// + * primaryButton + * secondaryButton + * topBar + * divider + * checkboxes + * readMore */ data class CustomColorScheme( @@ -28,59 +38,82 @@ data class CustomColorScheme( val onSurfaceVariantColorCode: Int? = null, val surfaceVariantColorCode: Int? = null, val outlineColorCode: Int? = null, - val outlineVariantColorCode: Int? = null -) - -internal fun CustomColorScheme.toLightColorScheme(): ColorScheme { - val lcm = lightColorScheme() - - val pc = if (primaryColorCode != null) Color(primaryColorCode) else lcm.primary - val opc = if (onPrimaryColorCode != null) Color(onPrimaryColorCode) else lcm.onPrimary - val pcc = if (primaryContainerColorCode != null) Color(primaryContainerColorCode) else lcm.primaryContainer - val sc = if (surfaceColorCode != null) Color(surfaceColorCode) else lcm.surface - val osc = if (onSurfaceColorCode != null) Color(onSurfaceColorCode) else lcm.onSurface - val osvc = if (onSurfaceVariantColorCode != null) Color(onSurfaceVariantColorCode) else lcm.onSurfaceVariant - val svc = if (surfaceVariantColorCode != null) Color(surfaceVariantColorCode) else lcm.surfaceVariant - val oc = if (outlineColorCode != null) Color(outlineColorCode) else lcm.outline - val ovc = if (outlineVariantColorCode != null) Color(outlineVariantColorCode) else lcm.outlineVariant - - return lightColorScheme( - primary = pc, - onPrimary = opc, - primaryContainer = pcc, - surface = sc, - onSurface = osc, - onSurfaceVariant = osvc, - surfaceVariant = svc, - outline = oc, - outlineVariant = ovc + val outlineVariantColorCode: Int? = null, + val primaryButton: Int? = null, + val secondaryButton: Int? = null, + val topBar: Int? = null, + val divider: Int? = null, + val checkboxes: Int? = null, + val readMore: Int? = null, +) { + constructor( + primaryColor: Color? = null, + onPrimaryColor: Color? = null, + primaryContainerColor: Color? = null, + surfaceColor: Color? = null, + onSurfaceColor: Color? = null, + onSurfaceVariantColor: Color? = null, + surfaceVariantColor: Color? = null, + outlineColor: Color? = null, + outlineVariantColor: Color? = null, + primaryButton: Color? = null, + secondaryButton: Color? = null, + topBar: Color? = null, + divider: Color? = null, + checkbox: Color? = null, + readMore: Color? = null + ) : this( + primaryColorCode = primaryColor?.toArgb(), + onPrimaryColorCode = onPrimaryColor?.toArgb(), + primaryContainerColorCode = primaryContainerColor?.toArgb(), + surfaceColorCode = surfaceColor?.toArgb(), + onSurfaceColorCode = onSurfaceColor?.toArgb(), + onSurfaceVariantColorCode = onSurfaceVariantColor?.toArgb(), + surfaceVariantColorCode = surfaceVariantColor?.toArgb(), + outlineColorCode = outlineColor?.toArgb(), + outlineVariantColorCode = outlineVariantColor?.toArgb(), + primaryButton = primaryButton?.toArgb(), + secondaryButton = secondaryButton?.toArgb(), + topBar = topBar?.toArgb(), + divider = divider?.toArgb(), + checkboxes = checkbox?.toArgb(), + readMore = readMore?.toArgb() ) - } -internal fun CustomColorScheme.toDarkColorScheme(): ColorScheme { - val dcm = darkColorScheme() - - val pc = if (primaryColorCode != null) Color(primaryColorCode) else dcm.primary - val opc = if (onPrimaryColorCode != null) Color(onPrimaryColorCode) else dcm.onPrimary - val pcc = if (primaryContainerColorCode != null) Color(primaryContainerColorCode) else dcm.primaryContainer - val sc = if (surfaceColorCode != null) Color(surfaceColorCode) else dcm.surface - val osc = if (onSurfaceColorCode != null) Color(onSurfaceColorCode) else dcm.onSurface - val osvc = if (onSurfaceVariantColorCode != null) Color(onSurfaceVariantColorCode) else dcm.onSurfaceVariant - val svc = if (surfaceVariantColorCode != null) Color(surfaceVariantColorCode) else dcm.surfaceVariant - val oc = if (outlineColorCode != null) Color(outlineColorCode) else dcm.outline - val ovc = if (outlineVariantColorCode != null) Color(outlineVariantColorCode) else dcm.outlineVariant +private fun CustomColorScheme.toMaterialColorScheme(baseScheme: ColorScheme): MaterialColorSchemeWithCustom { + val primary = primaryColorCode.toColorOrDefault(baseScheme.primary) + val onPrimary = onPrimaryColorCode.toColorOrDefault(baseScheme.onPrimary) + val primaryContainer = primaryContainerColorCode.toColorOrDefault(baseScheme.primaryContainer) + val surface = surfaceColorCode.toColorOrDefault(baseScheme.surface) + val onSurface = onSurfaceColorCode.toColorOrDefault(baseScheme.onSurface) + val onSurfaceVariant = onSurfaceVariantColorCode.toColorOrDefault(baseScheme.onSurfaceVariant) + val surfaceVariant = surfaceVariantColorCode.toColorOrDefault(baseScheme.surfaceVariant) + val outline = outlineColorCode.toColorOrDefault(baseScheme.outline) + val outlineVariant = outlineVariantColorCode.toColorOrDefault(baseScheme.outlineVariant) - return darkColorScheme( - primary = pc, - onPrimary = opc, - primaryContainer = pcc, - surface = sc, - onSurface = osc, - onSurfaceVariant = osvc, - surfaceVariant = svc, - outline = oc, - outlineVariant = ovc + return MaterialColorSchemeWithCustom( + materialColorScheme = baseScheme.copy( + primary = primary, + onPrimary = onPrimary, + primaryContainer = primaryContainer, + surface = surface, + onSurface = onSurface, + onSurfaceVariant = onSurfaceVariant, + surfaceVariant = surfaceVariant, + outline = outline, + outlineVariant = outlineVariant + ), + primaryButton = primaryButton.toColorOrDefault(primary), + secondaryButton = secondaryButton.toColorOrDefault(primary), + topBar = topBar.toColorOrDefault(primary), + divider = divider.toColorOrDefault(outline), + checkbox = checkboxes.toColorOrDefault(primary), + readMore = readMore.toColorOrDefault(primary) ) +} + +internal fun CustomColorScheme.toLightColorScheme() = toMaterialColorScheme(lightColorScheme()) +internal fun CustomColorScheme.toDarkColorScheme() = toMaterialColorScheme(darkColorScheme()) +fun Int?.toColorOrDefault(default: Color) = this?.let { Color(it) } ?: default -} \ No newline at end of file diff --git a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/CustomTypography.kt b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/CustomTypography.kt index 14c935c..26c57d7 100644 --- a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/CustomTypography.kt +++ b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/CustomTypography.kt @@ -1,19 +1,48 @@ package com.cookieinformation.mobileconsents.sdk.ui -import android.content.Context -import androidx.annotation.FontRes import androidx.compose.material3.Typography -import androidx.compose.ui.text.font.Font -import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.TextStyle + /** * Customizable typography * - * headlineSmall (Privacy policy headline) - * titleMedium (Privacy policy short, header section text, consent item tile) - * bodyMedium (consent item body (when no html), privacy policy body (when no html)) - * labelLarge - Read more, Bottom bar button texts - * labelSmall - Powered by Cookie Information, Device identifier + * Core Material3 mappings: + * headlineSmall - Privacy Policy headline + * titleMedium - Privacy Policy short, header section text, consent item title + * bodyMedium - Consent item body (when no HTML), Privacy Policy body (when no HTML) + * labelLarge - "Read more", Bottom bar button texts + * labelSmall - "Powered by Cookie Information", Device identifier + * + * Additional custom fields: + * + * readMore + * - Optional override for "Read more" button/label text style. + * - If provided, it replaces `bodyMedium` for "Read more" text. + * + * requiredTitle + * - Optional override for the "Required" . + * - If provided, it **replaces `titleMedium`**. + * + * requiredSectionTitle + * - Optional override for the header of the required consents section. + * - If provided, it **replaces `titleMedium`** in SectionHeader for required section. + * + * requiredSectionBody + * - Optional override for the body text of the required consents section. + * - If provided, it **replaces `bodyMedium`** for the section body. + * + * optionalTitle + * - Optional override for the "Optional". + * - If provided, it **replaces `titleMedium`** . + * + * optionalSectionTitle + * - Optional override for the header of the optional consents section. + * - If provided, it **replaces `titleMedium`** in SectionHeader for optional section. + * + * optionalSectionBody + * - Optional override for the body text of the optional consents section. + * - If provided, it **replaces `bodyMedium`** for the section body. */ data class CustomTypography( @@ -21,8 +50,31 @@ data class CustomTypography( val titleMedium: CustomTextStyle? = null, val bodyMedium: CustomTextStyle? = null, val labelLarge: CustomTextStyle? = null, - val labelSmall: CustomTextStyle? = null -) + val labelSmall: CustomTextStyle? = null, + //additional + val readMore: CustomTextStyle? = null, + val requiredTitle: CustomTextStyle? = null, + val requiredSectionTitle: CustomTextStyle? = null, + val requiredSectionBody: CustomTextStyle? = null, + val optionalTitle: CustomTextStyle? = null, + val optionalSectionTitle: CustomTextStyle? = null, + val optionalSectionBody: CustomTextStyle? = null +) { + + fun itemTitleStyle(isRequired: Boolean): TextStyle? = + if (isRequired) requiredTitle?.toTextStyle() + else optionalTitle?.toTextStyle() + + fun sectionHeaderStyle(isRequired: Boolean): TextStyle? = + if (isRequired) requiredSectionTitle?.toTextStyle() + else optionalSectionTitle?.toTextStyle() + + fun sectionBodyStyle(isRequired: Boolean): TextStyle? = + if (isRequired) requiredSectionBody?.toTextStyle() + else optionalSectionBody?.toTextStyle() + + fun readMoreStyle(): TextStyle? = readMore?.toTextStyle() +} internal fun CustomTypography.toTypography(): Typography { val defaultTypography = Typography() @@ -35,7 +87,7 @@ internal fun CustomTypography.toTypography(): Typography { return Typography( headlineSmall = hs, - titleMedium = tm, + titleMedium = tm, bodyMedium = bm, labelLarge = ll, labelSmall = ls diff --git a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsActivity.kt b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsActivity.kt index ab9eb2c..9b91268 100644 --- a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsActivity.kt +++ b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsActivity.kt @@ -21,12 +21,15 @@ class ConsentsActivity : ComponentActivity() { setContent { AndroidUiSDKTheme( - customerLightColorScheme = CustomUI.lightColorScheme, - customerDarkColorScheme = CustomUI.darkColorScheme, + additionalLightColors = CustomUI.lightColorSchemeWithCustom, + additionalDarkColors = CustomUI.darkColorSchemeWithCustom, typography = CustomUI.typography ) { ConsentsWrappingScreen( viewModel = ConsentsViewModel(application), + additionalLightColors = CustomUI.lightColorSchemeWithCustom, + additionalDarkColors = CustomUI.darkColorSchemeWithCustom, + additionalTypography = CustomUI.additionalTypography, userId = userId ) } diff --git a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsScreen.kt b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsScreen.kt index e0d6947..203606f 100644 --- a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsScreen.kt +++ b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -18,7 +19,9 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Divider +import androidx.compose.material3.DividerDefaults import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Switch @@ -47,6 +50,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import androidx.core.text.HtmlCompat +import com.cookieinformation.mobileconsents.sdk.ui.CustomTypography import com.cookieinformation.mobileconsents.sdk.ui.R import com.cookieinformation.mobileconsents.sdk.ui.UIConsentItem import com.cookieinformation.mobileconsents.sdk.ui.ui.components.TextViewComposable @@ -62,7 +66,9 @@ fun ConsentsScreen( consents: List, showPolicy: () -> Unit, acceptConsents: (MutableMap) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + additionalColors: MaterialColorSchemeWithCustom? = null, + additionalTypography: CustomTypography? ) { val selectedValues by rememberSaveable { @@ -73,7 +79,6 @@ fun ConsentsScreen( val requiredSectionHeader = stringResource(R.string.required_consents_header) val optionalSectionHeader = stringResource(R.string.optional_consents_header) - Column( modifier = modifier, verticalArrangement = Arrangement.SpaceBetween @@ -107,39 +112,46 @@ fun ConsentsScreen( .verticalScroll(rememberScrollState()) .weight(weight = 1f, fill = true) ) { - PrivacyPolicySection(privacyPolicy, showPolicy) + PrivacyPolicySection(privacyPolicy, showPolicy, additionalColors, additionalTypography) val requiredConsents = consents.filter { it.required } val optionalConsents = consents.filter { !it.required } if (requiredConsents.isNotEmpty()) { - SectionHeader(requiredSectionHeader) + SectionHeader(requiredSectionHeader, additionalColors, additionalTypography, true) requiredConsents.forEach { item -> ConsentRow( - item.title, - item.description, - item.required, - item.accepted + title = item.title, + description = item.description, + required = item.required, + accepted = item.accepted, + additionalColors = additionalColors, + additionalTypography = additionalTypography, + isRequiredSection = true ) { checked -> selectedValues[item.id] = checked } - Divider() + Divider(color = additionalColors?.divider ?: DividerDefaults.color) } } if (optionalConsents.isNotEmpty()) { - SectionHeader(optionalSectionHeader) + SectionHeader(optionalSectionHeader, additionalColors, additionalTypography, false) optionalConsents.forEach { item -> Column { ConsentRow( - item.title, - item.description, - item.required, - item.accepted - ) { checked -> - selectedValues[item.id] = checked - } - Divider() + title = item.title, + description = item.description, + required = item.required, + accepted = item.accepted, + additionalColors = additionalColors, + additionalTypography = additionalTypography, + isRequiredSection = false, + onChange = { checked -> + selectedValues[item.id] = checked + }, + ) + Divider(color = additionalColors?.divider ?: DividerDefaults.color) } } } @@ -159,6 +171,11 @@ fun ConsentsScreen( .weight(1f, true), onClick = { acceptConsents(selectedValues) }, shape = RoundedCornerShape(20), + colors = IconButtonDefaults.filledIconButtonColors( + containerColor = additionalColors?.primaryButton + ?: MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary + ) ) { Text( stringResource(R.string.accept_selected), @@ -175,6 +192,11 @@ fun ConsentsScreen( .toMutableMap()) }, shape = RoundedCornerShape(20), + colors = IconButtonDefaults.filledIconButtonColors( + containerColor = additionalColors?.secondaryButton + ?: MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary + ) ) { val acceptALlText = stringResource(id = R.string.accept_all) @@ -213,17 +235,25 @@ fun ConsentsScreen( } @Composable -fun SectionHeader(title: String) { +fun SectionHeader( + title: String, + additionalColors: MaterialColorSchemeWithCustom?, + additionalTypography: CustomTypography?, + isRequiredSection: Boolean = false +) { + + val customTextStyle = additionalTypography?.itemTitleStyle(isRequiredSection) + Text( text = title, color = MaterialTheme.colorScheme.onSurface, - style = MaterialTheme.typography.titleMedium, + style = customTextStyle ?: MaterialTheme.typography.titleMedium, modifier = Modifier .fillMaxWidth() .padding(16.dp) .wrapContentHeight() ) - Divider() + Divider(color = additionalColors?.divider ?: DividerDefaults.color) } @Composable @@ -232,6 +262,9 @@ fun ConsentRow( description: String, required: Boolean, accepted: Boolean, + additionalColors: MaterialColorSchemeWithCustom?, + additionalTypography: CustomTypography?, + isRequiredSection: Boolean = false, onChange: (checked: Boolean) -> Unit ) { var checked by remember { mutableStateOf(required || accepted) } @@ -241,14 +274,15 @@ fun ConsentRow( val itemAccepted = stringResource(id = R.string.item_accepted) val itemRejected = stringResource(id = R.string.item_rejected) - Column(modifier = Modifier - .background(MaterialTheme.colorScheme.surface) - .fillMaxWidth() - .wrapContentHeight() - .padding(horizontal = 16.dp, vertical = 8.dp) - .semantics(mergeDescendants = true) { - contentDescription = "$title \n\n$stringBuilder" - }) + Column( + modifier = Modifier + .background(MaterialTheme.colorScheme.surface) + .fillMaxWidth() + .wrapContentHeight() + .padding(horizontal = 16.dp, vertical = 8.dp) + .semantics(mergeDescendants = true) { + contentDescription = "$title \n\n$stringBuilder" + }) { Row( modifier = Modifier.fillMaxWidth(), @@ -257,11 +291,16 @@ fun ConsentRow( ) { Text( text = title, - modifier = Modifier.clearAndSetSemantics {}, + modifier = Modifier + .weight(1f) + .clearAndSetSemantics {}, color = MaterialTheme.colorScheme.onSurface, - style = MaterialTheme.typography.titleMedium + style = additionalTypography?.sectionHeaderStyle(isRequiredSection) ?: MaterialTheme.typography.titleMedium ) onChange(checked) + + Spacer(modifier = Modifier.width(8.dp)) + Switch( checked = checked, onCheckedChange = { @@ -276,21 +315,40 @@ fun ConsentRow( this.stateDescription = "$title $desc" }, enabled = !required, - colors = SwitchDefaults.colors( - disabledCheckedTrackColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.5f), - disabledCheckedThumbColor = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.5f), - ), + colors = when { + additionalColors?.checkbox != null -> { + val checkboxColor = additionalColors.checkbox + SwitchDefaults.colors( + checkedTrackColor = checkboxColor, + checkedThumbColor = MaterialTheme.colorScheme.onPrimary, + uncheckedTrackColor = checkboxColor.copy(alpha = 0.4f), + uncheckedThumbColor = checkboxColor, + disabledCheckedTrackColor = checkboxColor.copy(alpha = 0.5f), + disabledCheckedThumbColor = MaterialTheme.colorScheme.onPrimary.copy( + alpha = 0.5f + ), + ) + } + + else -> { + SwitchDefaults.colors( + disabledCheckedTrackColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.5f), + disabledCheckedThumbColor = MaterialTheme.colorScheme.onPrimary.copy( + alpha = 0.5f + ), + ) + } + } ) } if (containsHtmlTags(description)) { TextViewComposable(description = description) - } - else { + } else { Text( text = description, modifier = Modifier.clearAndSetSemantics {}, color = MaterialTheme.colorScheme.onSurfaceVariant, - style = MaterialTheme.typography.bodyMedium + style = additionalTypography?.sectionBodyStyle(isRequiredSection) ?: MaterialTheme.typography.bodyMedium ) } @@ -298,11 +356,18 @@ fun ConsentRow( } @Composable -fun PrivacyPolicySection(policy: UIConsentItem?, showPolicy: () -> Unit) { +fun PrivacyPolicySection( + policy: UIConsentItem?, + showPolicy: () -> Unit, + additionalColors: MaterialColorSchemeWithCustom?, + additionalTypography: CustomTypography? +) { policy?.let { - Column(modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.surface)) { + Column( + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.surface) + ) { Text( text = stringResource(id = R.string.privacy_policy_header), modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp), @@ -310,10 +375,12 @@ fun PrivacyPolicySection(policy: UIConsentItem?, showPolicy: () -> Unit) { style = MaterialTheme.typography.headlineSmall ) - Text(text = policy.title, + Text( + text = policy.title, style = MaterialTheme.typography.titleMedium, modifier = Modifier.padding(horizontal = 16.dp), - color = MaterialTheme.colorScheme.onSurface) + color = MaterialTheme.colorScheme.onSurface + ) OutlinedButton( contentPadding = PaddingValues(0.dp), modifier = Modifier.padding(horizontal = 16.dp), @@ -323,26 +390,24 @@ fun PrivacyPolicySection(policy: UIConsentItem?, showPolicy: () -> Unit) { content = { Text( text = stringResource(id = R.string.read_more), - - style = MaterialTheme.typography.labelLarge.copy( - platformStyle = PlatformTextStyle( - includeFontPadding = false - ) - ), + style = (additionalTypography?.readMoreStyle() ?: MaterialTheme.typography.bodyMedium) + .copy(platformStyle = PlatformTextStyle(includeFontPadding = false)), textAlign = TextAlign.Center, + color = additionalColors?.readMore ?: MaterialTheme.colorScheme.onSurface ) // Specify the icon using the icon parameter Image( - colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary), + colorFilter = ColorFilter.tint(additionalColors?.readMore ?: MaterialTheme.colorScheme.primary), painter = painterResource(R.drawable.navigate_next), contentDescription = null ) } ) - Divider() + Divider(color = additionalColors?.divider ?: DividerDefaults.color) } } } + fun containsHtmlTags(input: String): Boolean { // This regex pattern looks for strings that contain "<" followed by any characters, // and then ">", indicating the presence of HTML tags. diff --git a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsWrappingScreen.kt b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsWrappingScreen.kt index 4fc3256..2a20fc6 100644 --- a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsWrappingScreen.kt +++ b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/ConsentsWrappingScreen.kt @@ -4,6 +4,7 @@ import android.app.Activity import androidx.activity.ComponentActivity import androidx.activity.compose.BackHandler import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme @@ -21,6 +22,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.cookieinformation.mobileconsents.core.domain.entities.ConsentType +import com.cookieinformation.mobileconsents.sdk.ui.CustomTypography import com.cookieinformation.mobileconsents.sdk.ui.toUIConsentItem import kotlinx.coroutines.launch @@ -35,11 +37,19 @@ enum class AppScreen { fun ConsentsWrappingScreen( viewModel: ConsentsViewModel, navController: NavHostController = rememberNavController(), - userId: String? + userId: String?, + additionalLightColors: MaterialColorSchemeWithCustom?, + additionalDarkColors: MaterialColorSchemeWithCustom?, + additionalTypography: CustomTypography? ) { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() + val additionalColors = when { + isSystemInDarkTheme() -> additionalDarkColors + else -> additionalLightColors + } + LaunchedEffect(Unit) { viewModel.getConsents(userId).onFailure { (context as? ComponentActivity)?.finish() @@ -48,11 +58,12 @@ fun ConsentsWrappingScreen( BackHandler(enabled = true) { coroutineScope.launch { - viewModel.canNavigateBack(userId).fold(onFailure = { - if (context is Activity) { - context.finish() - } - }, + viewModel.canNavigateBack(userId).fold( + onFailure = { + if (context is Activity) { + context.finish() + } + }, onSuccess = { if (it) { if (context is Activity) { @@ -82,14 +93,16 @@ fun ConsentsWrappingScreen( consents = consentsUiState.consents.filter { consent -> consent.type != ConsentType.PRIVACY_POLICY }.map { it.toUIConsentItem() }, acceptConsents = { coroutineScope.launch { - viewModel.saveConsents(userId, it) + viewModel.saveConsents(userId, it) }.invokeOnCompletion { (context as? ComponentActivity)?.finish() } }, showPolicy = { navController.navigate(AppScreen.PrivacyPolicy.name) - } + }, + additionalColors = additionalColors, + additionalTypography = additionalTypography ) } composable(route = AppScreen.PrivacyPolicy.name) { diff --git a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/CustomUI.kt b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/CustomUI.kt index 6b4e329..ad1c040 100644 --- a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/CustomUI.kt +++ b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/CustomUI.kt @@ -1,30 +1,66 @@ package com.cookieinformation.mobileconsents.sdk.ui.ui import androidx.compose.material3.ColorScheme +import androidx.compose.material3.Typography +import androidx.compose.ui.graphics.Color +import com.cookieinformation.mobileconsents.sdk.ui.CustomTypography +import com.cookieinformation.mobileconsents.sdk.ui.toTypography + + +data class MaterialColorSchemeWithCustom( + val materialColorScheme: ColorScheme?, + val primaryButton: Color? = null, + val secondaryButton: Color? = null, + val topBar: Color? = null, + val divider: Color? = null, + val checkbox: Color? = null, + val readMore: Color? = null +) object CustomUI { - private var customLightColorScheme: ColorScheme? = null - private var customDarkColorScheme: ColorScheme? = null - private var customTypography: androidx.compose.material3.Typography? = null + private var customLightColorScheme: MaterialColorSchemeWithCustom? = null + private var customDarkColorScheme: MaterialColorSchemeWithCustom? = null + private var baseTypography: Typography? = null + private var customTypography: CustomTypography? = null + fun setCustomUi( lightScheme: ColorScheme? = null, darkScheme: ColorScheme? = null, - typography: androidx.compose.material3.Typography? = null + typography: Typography? = null + ) { + customLightColorScheme = MaterialColorSchemeWithCustom(materialColorScheme = lightScheme) + customDarkColorScheme = MaterialColorSchemeWithCustom(materialColorScheme = darkScheme) + baseTypography = typography + } + + fun setCustomUi( + lightScheme: MaterialColorSchemeWithCustom? = null, + darkScheme: MaterialColorSchemeWithCustom? = null, + typography: CustomTypography? = null ) { customLightColorScheme = lightScheme customDarkColorScheme = darkScheme + baseTypography = typography?.toTypography() customTypography = typography } val lightColorScheme: ColorScheme? - get() = customLightColorScheme - + get() = customLightColorScheme?.materialColorScheme val darkColorScheme: ColorScheme? - get() = customDarkColorScheme + get() = customDarkColorScheme?.materialColorScheme - val typography: androidx.compose.material3.Typography? + val typography: Typography? + get() = baseTypography + + val additionalTypography: CustomTypography? get() = customTypography + + val lightColorSchemeWithCustom: MaterialColorSchemeWithCustom? + get() = customLightColorScheme + + val darkColorSchemeWithCustom: MaterialColorSchemeWithCustom? + get() = customDarkColorScheme } \ No newline at end of file diff --git a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/theme/Theme.kt b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/theme/Theme.kt index 65cbdcf..3cda1f3 100644 --- a/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/theme/Theme.kt +++ b/UI-SDK/src/main/java/com/cookieinformation/mobileconsents/sdk/ui/ui/theme/Theme.kt @@ -10,10 +10,11 @@ import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect -import androidx.compose.ui.graphics.Color +import androidx.compose.runtime.remember import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowCompat +import com.cookieinformation.mobileconsents.sdk.ui.ui.MaterialColorSchemeWithCustom /** * Customizable colors (based as much as possible on component colors from https://m3.material.io/components) @@ -39,6 +40,8 @@ import androidx.core.view.WindowCompat * labelLarge - Read more, Bottom bar button texts * labelSmall - Powered by Cookie Information, Device identifier * + * additionalLightColors - wrapper for ColorScheme with additional colors + * additionalDarkColors- wrapper for ColorScheme with additional colors */ @Composable @@ -47,18 +50,28 @@ fun AndroidUiSDKTheme( // Dynamic color is available on Android 12+ customerLightColorScheme: ColorScheme? = null, customerDarkColorScheme: ColorScheme? = null, + additionalLightColors: MaterialColorSchemeWithCustom? = null, + additionalDarkColors: MaterialColorSchemeWithCustom? = null, typography: Typography? = null, content: @Composable () -> Unit ) { - val colorScheme = when { - darkTheme -> customerDarkColorScheme ?: darkColorScheme() - else -> customerLightColorScheme ?: lightColorScheme() + + val colorScheme = remember(darkTheme, customerLightColorScheme, customerDarkColorScheme, additionalLightColors, additionalDarkColors) { + when { + darkTheme -> customerDarkColorScheme ?: additionalDarkColors?.materialColorScheme ?: darkColorScheme() + else -> customerLightColorScheme ?: additionalLightColors?.materialColorScheme ?: lightColorScheme() + } } + + val additionalColors = remember(darkTheme, additionalLightColors, additionalDarkColors) { + if (darkTheme) additionalDarkColors else additionalLightColors + } + val view = LocalView.current if (!view.isInEditMode) { SideEffect { val window = (view.context as Activity).window - window.statusBarColor = colorScheme.primary.toArgb() + window.statusBarColor = additionalColors?.topBar?.toArgb() ?: colorScheme.primary.toArgb() WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme } } diff --git a/example/src/main/java/com/cookieinformation/mobileconsents/ui/MainActivity.kt b/example/src/main/java/com/cookieinformation/mobileconsents/ui/MainActivity.kt index 6c05dae..05e4651 100644 --- a/example/src/main/java/com/cookieinformation/mobileconsents/ui/MainActivity.kt +++ b/example/src/main/java/com/cookieinformation/mobileconsents/ui/MainActivity.kt @@ -19,6 +19,15 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.cookieinformation.mobileconsents.sdk.ui.ConsentsUISDK +import com.cookieinformation.mobileconsents.sdk.ui.CustomColorScheme +import com.cookieinformation.mobileconsents.sdk.ui.CustomTextStyle +import com.cookieinformation.mobileconsents.sdk.ui.CustomTypography +import com.cookieinformation.mobileconsents.ui.ui.theme.BrightOrange +import com.cookieinformation.mobileconsents.ui.ui.theme.Cyan +import com.cookieinformation.mobileconsents.ui.ui.theme.ForestGreen +import com.cookieinformation.mobileconsents.ui.ui.theme.Gold +import com.cookieinformation.mobileconsents.ui.ui.theme.LimeGreen +import com.cookieinformation.mobileconsents.ui.ui.theme.NavyBlue import com.cookieinformation.mobileconsents.ui.ui.theme.SampleAppTheme import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -28,13 +37,35 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - + ConsentsUISDK.init( clientID = "91cb67d4-9ebc-4c7e-974f-858d08b7ab7d", clientSecret = "a4fe5c7763ef5d8e395c9ef8c358c1f67c0876b6e7465c59ce9421d6ca67d02ba3d3a37f0351f320a2216878e4ddc570bdf6092a351b24067d536ded36a75946", solutionId = "a7a95d5e-e99b-44ff-8383-b7f6d1b720a8", context = this@MainActivity, - languageCode = "en" + customLightColorScheme = CustomColorScheme( + primaryButton = NavyBlue, + secondaryButton = LimeGreen, + topBar = ForestGreen, + divider = Cyan, + checkbox = BrightOrange, + readMore = Gold + ), + customTypography = CustomTypography( + requiredSectionTitle = CustomTextStyle( + fontResId = R.font.bbh_bogle, + fontSize = 15, + ), + optionalSectionTitle = CustomTextStyle( + fontResId = R.font.dancing_script, + fontSize = 22 + ), + optionalSectionBody = CustomTextStyle( + fontResId = R.font.dancing_script, + fontSize = 30 + ) + ), + languageCode = "en", ) setContent { diff --git a/example/src/main/java/com/cookieinformation/mobileconsents/ui/ui/theme/Color.kt b/example/src/main/java/com/cookieinformation/mobileconsents/ui/ui/theme/Color.kt new file mode 100644 index 0000000..2ded78c --- /dev/null +++ b/example/src/main/java/com/cookieinformation/mobileconsents/ui/ui/theme/Color.kt @@ -0,0 +1,14 @@ +package com.cookieinformation.mobileconsents.ui.ui.theme + +import androidx.compose.ui.graphics.Color + +val DeepPurple = Color(0xFF673AB7) +val BrightOrange = Color(0xFFFF5722) +val LimeGreen = Color(0xFFCDDC39) +val DeepPink = Color(0xFFE91E63) +val Cyan = Color(0xFF00BCD4) +val Amber = Color(0xFFFFC107) +val NavyBlue = Color(0xFF0D47A1) +val CrimsonRed = Color(0xFFB71C1C) +val ForestGreen = Color(0xFF1B5E20) +val Gold = Color(0xFFFFD700) \ No newline at end of file diff --git a/example/src/main/res/font/bbh_bogle.ttf b/example/src/main/res/font/bbh_bogle.ttf new file mode 100644 index 0000000..d9f2a27 Binary files /dev/null and b/example/src/main/res/font/bbh_bogle.ttf differ diff --git a/example/src/main/res/font/dancing_script.ttf b/example/src/main/res/font/dancing_script.ttf new file mode 100644 index 0000000..5c0f61a Binary files /dev/null and b/example/src/main/res/font/dancing_script.ttf differ