Compare commits
1 Commits
5db3391b7d
...
86a7020c2b
Author | SHA1 | Date | |
---|---|---|---|
86a7020c2b |
@ -6,10 +6,9 @@ plugins {
|
|||||||
alias(libs.plugins.ksp)
|
alias(libs.plugins.ksp)
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.kotlin.compose)
|
|
||||||
alias(libs.plugins.hilt)
|
alias(libs.plugins.hilt)
|
||||||
alias(libs.plugins.serialization)
|
alias(libs.plugins.serialization)
|
||||||
|
alias(libs.plugins.kotlin.compose)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keystore
|
// Keystore
|
||||||
@ -106,6 +105,8 @@ dependencies {
|
|||||||
implementation(libs.hilt.android)
|
implementation(libs.hilt.android)
|
||||||
ksp(libs.hilt.compiler)
|
ksp(libs.hilt.compiler)
|
||||||
implementation(libs.hilt.navigation.compose)
|
implementation(libs.hilt.navigation.compose)
|
||||||
|
|
||||||
|
// Libs analytics
|
||||||
implementation(libs.matomo)
|
implementation(libs.matomo)
|
||||||
implementation(libs.clarity)
|
implementation(libs.clarity)
|
||||||
implementation(libs.ga4)
|
implementation(libs.ga4)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package fr.openium.consentium
|
package fr.openium.consentium
|
||||||
|
|
||||||
import fr.openium.consentium.ui.navigation.DemoNavGraph
|
import DemoNavGraph
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
@ -24,7 +24,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val navHostController = rememberNavController()
|
val navHostController = rememberNavController()
|
||||||
|
|
||||||
ConsentiumTheme {
|
ConsentiumTheme {
|
||||||
Scaffold( modifier = Modifier.fillMaxSize() ) { paddingValues ->
|
Scaffold(modifier = Modifier.fillMaxSize()) { paddingValues ->
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
@ -1,17 +1,32 @@
|
|||||||
package fr.openium.consentium.ui.navigation
|
|
||||||
|
|
||||||
import Destination
|
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.slideIn
|
import androidx.compose.animation.slideIn
|
||||||
import androidx.compose.animation.slideOut
|
import androidx.compose.animation.slideOut
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.toRoute
|
||||||
|
import fr.openium.consentium.api.Consentium
|
||||||
import fr.openium.consentium.ui.screens.main.MainScreen
|
import fr.openium.consentium.ui.screens.main.MainScreen
|
||||||
import fr.openium.consentium.ui.screens.splash.SplashScreen
|
import fr.openium.consentium.ui.screens.splash.SplashScreen
|
||||||
|
import fr.openium.consentium.ui.theme.Error
|
||||||
|
import fr.openium.consentium.ui.theme.OnPrimary
|
||||||
|
import fr.openium.consentium.ui.theme.OnSecondary
|
||||||
|
import fr.openium.consentium.ui.theme.OnSurface
|
||||||
|
import fr.openium.consentium.ui.theme.OnSurfaceVariant
|
||||||
|
import fr.openium.consentium.ui.theme.Primary
|
||||||
|
import fr.openium.consentium.ui.theme.Secondary
|
||||||
|
import fr.openium.consentium.ui.theme.Success
|
||||||
|
import fr.openium.consentium.ui.theme.SurfaceHigh
|
||||||
|
import fr.openium.consentium.ui.theme.SurfaceHighest
|
||||||
|
import fr.openium.consentium.ui.theme.SurfaceMiddle
|
||||||
|
import fr.openium.consentium.ui.theme.Tertiary
|
||||||
|
import fr.openium.consentium_ui.ui.components.ConsentiumComponent
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumDefaults
|
||||||
|
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
|
||||||
|
|
||||||
private const val NAV_ANIMATION_TIME = 500
|
private const val NAV_ANIMATION_TIME = 500
|
||||||
|
|
||||||
@ -43,23 +58,56 @@ fun DemoNavGraph(navHostController: NavHostController) {
|
|||||||
saveState = true
|
saveState = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
navigateToConsent = {
|
||||||
|
navHostController.navigate(Destination.Consent(ConsentiumPageUI.GENERAL_CONSENT))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable<Destination.Main> {
|
composable<Destination.Main> {
|
||||||
MainScreen(
|
MainScreen(
|
||||||
onGoToConsentDetail = {
|
|
||||||
navHostController.navigate(Destination.Consent)
|
|
||||||
},
|
|
||||||
onGoToConsentMaster = {
|
onGoToConsentMaster = {
|
||||||
navHostController.navigate(Destination.Consent)
|
navHostController.navigate(Destination.Consent(ConsentiumPageUI.GENERAL_CONSENT))
|
||||||
}
|
},
|
||||||
|
onGoToConsentDetail = {
|
||||||
|
navHostController.navigate(Destination.Consent(ConsentiumPageUI.DETAILS_CONSENT))
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable<Destination.Consent> {
|
composable<Destination.Consent> { backStackEntry ->
|
||||||
// TODO
|
val consent = backStackEntry.toRoute<Destination.Consent>()
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
ConsentiumComponent(
|
||||||
|
defaultLandingPage = consent.landingPage,
|
||||||
|
onQuitConsent = {
|
||||||
|
navHostController.navigate(Destination.Main) {
|
||||||
|
popUpTo(navHostController.graph.findStartDestination().id) {
|
||||||
|
saveState = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
consentium = Consentium(
|
||||||
|
context = context,
|
||||||
|
applicationId = "ApplicationId",
|
||||||
|
),
|
||||||
|
colors = ConsentiumDefaults.colors(
|
||||||
|
primary = Primary,
|
||||||
|
onPrimary = OnPrimary,
|
||||||
|
secondary = Secondary,
|
||||||
|
onSecondary = OnSecondary,
|
||||||
|
tertiary = Tertiary,
|
||||||
|
onSurfaceVariant = OnSurfaceVariant,
|
||||||
|
onSurface = OnSurface,
|
||||||
|
error = Error,
|
||||||
|
surfaceHighest = SurfaceHighest,
|
||||||
|
surfaceHigh = SurfaceHigh,
|
||||||
|
surfaceMiddle = SurfaceMiddle,
|
||||||
|
success = Success,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
||||||
@ -10,6 +11,8 @@ sealed interface Destination {
|
|||||||
data object Main : Destination
|
data object Main : Destination
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Consent : Destination
|
data class Consent(
|
||||||
|
val landingPage: ConsentiumPageUI
|
||||||
|
): Destination
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package fr.openium.consentium.ui.screens.consent
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ConsentScreen() {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -43,7 +43,5 @@ fun MainScreen(
|
|||||||
text = "Go to Consent Detail"
|
text = "Go to Consent Detail"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -10,36 +10,51 @@ import androidx.compose.material3.CircularProgressIndicator
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import fr.openium.consentium.api.Consentium
|
||||||
|
import fr.openium.consentium.api.state.FetchConsentiumState
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SplashScreen(
|
fun SplashScreen(
|
||||||
navigateToMain: () -> Unit,
|
navigateToMain: () -> Unit,
|
||||||
viewModel: SplashScreenViewModel = hiltViewModel(),
|
navigateToConsent: () -> Unit,
|
||||||
) {
|
) {
|
||||||
// State
|
// Property
|
||||||
val currentState by viewModel.state.collectAsState()
|
val context = LocalContext.current
|
||||||
|
val consentium = remember { Consentium(context = context, applicationId = "DemoApplicationId") }
|
||||||
|
|
||||||
// Effect
|
// Effect
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.initMain()
|
consentium.fetchConsentState.collect { consentState ->
|
||||||
|
when (consentState) {
|
||||||
|
FetchConsentiumState.Idle,
|
||||||
|
FetchConsentiumState.Loading -> {}
|
||||||
|
|
||||||
|
FetchConsentiumState.Error -> {
|
||||||
|
// Handle error
|
||||||
|
consentium.fetchConsents()
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(currentState) {
|
is FetchConsentiumState.Invalid -> {
|
||||||
when (val state = currentState) {
|
navigateToConsent()
|
||||||
is SplashScreenViewModel.State.Loaded -> {
|
}
|
||||||
if (state.isSplashEnded) {
|
|
||||||
|
is FetchConsentiumState.Valid -> {
|
||||||
|
// The tracking services should be initialized here
|
||||||
navigateToMain()
|
navigateToMain()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
consentium.fetchConsents()
|
||||||
|
}
|
||||||
|
|
||||||
// View
|
// View
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
@ -47,7 +62,7 @@ fun SplashScreen(
|
|||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Text("Splash Screen")
|
Text("Splash Screen") // TODO
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(14.dp))
|
Spacer(modifier = Modifier.height(14.dp))
|
||||||
|
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
package fr.openium.consentium.ui.screens.splash
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
class SplashScreenViewModel @Inject constructor(
|
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
private val _state = MutableStateFlow<State>(State.Loaded(false))
|
|
||||||
val state: StateFlow<State> = _state
|
|
||||||
|
|
||||||
fun initMain() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
delay(1500L)
|
|
||||||
_state.value = State.Loaded(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed interface State {
|
|
||||||
data class Loaded(val isSplashEnded: Boolean) : State
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -2,10 +2,21 @@ package fr.openium.consentium.ui.theme
|
|||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
val Purple80 = Color(0xFFD0BCFF)
|
val Primary = Color(0xFF70ACDC)
|
||||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
val OnPrimary = Color(0XFFFFFFFF)
|
||||||
val Pink80 = Color(0xFFEFB8C8)
|
|
||||||
|
|
||||||
val Purple40 = Color(0xFF6650a4)
|
val Secondary = Color(0xFFF29413)
|
||||||
val PurpleGrey40 = Color(0xFF625b71)
|
val OnSecondary = Color(0xFFFFFFFF)
|
||||||
val Pink40 = Color(0xFF7D5260)
|
|
||||||
|
val Tertiary = Color(0xFF3470A0)
|
||||||
|
|
||||||
|
val OnSurfaceVariant = Color(0xFF3470A0)
|
||||||
|
val OnSurface = Color(0xFF163752)
|
||||||
|
|
||||||
|
val Error = Color(0xFF3470A0)
|
||||||
|
|
||||||
|
val SurfaceHighest = Color(0xFFFFFFFF)
|
||||||
|
val SurfaceHigh = Color(0xFFF2F8FC)
|
||||||
|
val SurfaceMiddle = Color(0xFFD6E6F5)
|
||||||
|
|
||||||
|
val Success = Color(0xFF479B3F)
|
@ -1,39 +1,29 @@
|
|||||||
package fr.openium.consentium.ui.theme
|
package fr.openium.consentium.ui.theme
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.darkColorScheme
|
import androidx.compose.material3.darkColorScheme
|
||||||
import androidx.compose.material3.dynamicDarkColorScheme
|
|
||||||
import androidx.compose.material3.dynamicLightColorScheme
|
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
|
|
||||||
private val DarkColorScheme = darkColorScheme(
|
private val DarkColorScheme = darkColorScheme(
|
||||||
primary = Purple80,
|
primary = Primary,
|
||||||
secondary = PurpleGrey80,
|
secondary = Secondary,
|
||||||
tertiary = Pink80
|
tertiary = Tertiary
|
||||||
)
|
)
|
||||||
|
|
||||||
private val LightColorScheme = lightColorScheme(
|
private val LightColorScheme = lightColorScheme(
|
||||||
primary = Purple40,
|
primary = Primary,
|
||||||
secondary = PurpleGrey40,
|
secondary = Secondary,
|
||||||
tertiary = Pink40
|
tertiary = Tertiary
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ConsentiumTheme(
|
fun ConsentiumTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
dynamicColor: Boolean = true,
|
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
val colorScheme = when {
|
val colorScheme = when {
|
||||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
|
||||||
val context = LocalContext.current
|
|
||||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
darkTheme -> DarkColorScheme
|
darkTheme -> DarkColorScheme
|
||||||
else -> LightColorScheme
|
else -> LightColorScheme
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.serialization)
|
alias(libs.plugins.serialization)
|
||||||
alias(libs.plugins.ksp)
|
alias(libs.plugins.ksp)
|
||||||
|
alias(libs.plugins.kotlin.compose)
|
||||||
|
alias(libs.plugins.hilt)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -19,7 +21,10 @@ android {
|
|||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +50,10 @@ android {
|
|||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "11"
|
||||||
}
|
}
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -54,6 +63,22 @@ dependencies {
|
|||||||
// AndroidX
|
// AndroidX
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
|
implementation(libs.androidx.runtime.android)
|
||||||
|
implementation(libs.androidx.foundation.android)
|
||||||
|
implementation(libs.androidx.ui.android)
|
||||||
|
implementation(libs.androidx.foundation.layout.android)
|
||||||
|
implementation(libs.androidx.ui.tooling.preview.android)
|
||||||
|
implementation(libs.androidx.material3.android)
|
||||||
|
implementation(libs.androidx.ui.tooling.preview)
|
||||||
|
implementation(libs.androidx.activity.compose)
|
||||||
|
|
||||||
|
|
||||||
|
//Compose
|
||||||
|
implementation(platform(libs.compose.bom))
|
||||||
|
implementation(libs.compose.ui.graphics)
|
||||||
|
implementation(libs.compose.ui)
|
||||||
|
implementation(libs.compose.ui.tooling)
|
||||||
|
implementation(libs.compose.material3)
|
||||||
|
|
||||||
// Serialization
|
// Serialization
|
||||||
implementation(libs.kotlin.serialization)
|
implementation(libs.kotlin.serialization)
|
||||||
@ -65,13 +90,20 @@ dependencies {
|
|||||||
|
|
||||||
// Hilt
|
// Hilt
|
||||||
implementation(libs.hilt.android)
|
implementation(libs.hilt.android)
|
||||||
|
implementation(libs.hilt.navigation.compose)
|
||||||
ksp(libs.hilt.compiler)
|
ksp(libs.hilt.compiler)
|
||||||
|
|
||||||
// Timber
|
// Timber
|
||||||
implementation(libs.timber)
|
implementation(libs.timber)
|
||||||
|
|
||||||
|
// Coil
|
||||||
|
implementation(libs.coil)
|
||||||
|
implementation(libs.coil.network)
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
testImplementation(libs.test.junit)
|
testImplementation(libs.test.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.test.espresso)
|
androidTestImplementation(libs.test.espresso)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
</manifest>
|
</manifest>
|
@ -9,8 +9,8 @@ import dagger.hilt.components.SingletonComponent
|
|||||||
import fr.openium.consentium.data.di.ConsentiumUrl
|
import fr.openium.consentium.data.di.ConsentiumUrl
|
||||||
import fr.openium.consentium.data.di.OkHttpClientDefault
|
import fr.openium.consentium.data.di.OkHttpClientDefault
|
||||||
import fr.openium.consentium_ui.BuildConfig
|
import fr.openium.consentium_ui.BuildConfig
|
||||||
import fr.openium.consentium_ui.api.mock.ConsentiumUIMockApi
|
|
||||||
import fr.openium.consentium_ui.data.remote.ConsentiumUIApi
|
import fr.openium.consentium_ui.data.remote.ConsentiumUIApi
|
||||||
|
import fr.openium.consentium_ui.data.remote.mock.ConsentiumUIMockApi
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
package fr.openium.consentium_ui.api.mock
|
|
||||||
|
|
||||||
import fr.openium.consentium_ui.data.remote.ConsentiumUIApi
|
|
||||||
import fr.openium.consentium_ui.data.remote.model.GetConsentConfigDTO
|
|
||||||
import retrofit2.Response
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
internal object ConsentiumUIMockApi : ConsentiumUIApi {
|
|
||||||
|
|
||||||
private val consents = GetConsentConfigDTO(
|
|
||||||
installationId = UUID.randomUUID().toString(),
|
|
||||||
appName = "Consentium",
|
|
||||||
icon = "https://www.example.com/icon.png",
|
|
||||||
primaryColor = "#FF0000",
|
|
||||||
secondaryColor = "#00FF00",
|
|
||||||
textColor = "#0000FF",
|
|
||||||
consentMainTextTranslation = emptyList(),
|
|
||||||
purposes = emptyList(),
|
|
||||||
)
|
|
||||||
|
|
||||||
override suspend fun getConsentConfig(applicationID: String, installationID: String): Response<GetConsentConfigDTO> {
|
|
||||||
return Response.success(consents)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,201 @@
|
|||||||
|
package fr.openium.consentium_ui.data.remote.mock
|
||||||
|
|
||||||
|
import fr.openium.consentium_ui.data.remote.ConsentiumUIApi
|
||||||
|
import fr.openium.consentium_ui.data.remote.model.GetConsentConfigDTO
|
||||||
|
import fr.openium.consentium_ui.data.remote.model.MainConsentTextTranslationDTO
|
||||||
|
import fr.openium.consentium_ui.data.remote.model.PurposeDTO
|
||||||
|
import fr.openium.consentium_ui.data.remote.model.PurposeStatusDTO
|
||||||
|
import fr.openium.consentium_ui.data.remote.model.PurposeTranslationDTO
|
||||||
|
import fr.openium.consentium_ui.data.remote.model.VendorDTO
|
||||||
|
import fr.openium.consentium_ui.data.remote.model.VendorTranslationDTO
|
||||||
|
import retrofit2.Response
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
internal object ConsentiumUIMockApi : ConsentiumUIApi {
|
||||||
|
|
||||||
|
private val consents = GetConsentConfigDTO(
|
||||||
|
installationId = UUID.randomUUID().toString(),
|
||||||
|
appName = "Consentium",
|
||||||
|
icon = "https://amp.openium.fr/openium.png",
|
||||||
|
primaryColor = "#FF0000",
|
||||||
|
secondaryColor = "#00FF00",
|
||||||
|
textColor = "#0000FF",
|
||||||
|
consentMainTextTranslation = listOf(
|
||||||
|
MainConsentTextTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
consentPageUrl = "https:consentium.fr",
|
||||||
|
mainConsentText = "<p>[Nom de l’application] utilise des cookies pour différents objectifs : faire fonctionner l’application, améliorer nos services en mesurant l’efficacité de nos contenus et afficher des publicités susceptibles de vous intéresser.<br></p>\n<p>En cliquant sur “Accepter et fermer”, vous acceptez cette utilisation sur l’application mobile. Vous pouvez également paramétrer vos choix en cliquant sur “Paramétrer mes choix” ou refuser ces cookies en cliquant sur “Continuer sans accepter”. Vous pouvez changer d’avis à tout moment depuis les paramètres de votre compte via l’onglet “Notifications et cookies”</p>\n",
|
||||||
|
durationText = "<p>Nous conservons votre choix pendant 12 mois. Vous pouvez changer d’avis à tout moment depuis les paramètres de votre compte via l’onglet “Notification et cookies” tetetettetettetetstts</p>\n"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
purposes = listOf(
|
||||||
|
PurposeDTO(
|
||||||
|
id = "purpose-required",
|
||||||
|
order = 0,
|
||||||
|
isRequired = true,
|
||||||
|
isAccepted = PurposeStatusDTO.ACCEPTED,
|
||||||
|
translations = listOf(
|
||||||
|
PurposeTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
text = "<p>Ces traceurs sont nécessaire au fonctionnement de l’application. Ils permettent de vérifier la stabilité technique de l’application et de mesurer notre audience. Ces données ne sont utilisées que pour notre compte exclusif (en ne produisant que des données statistiques anonymes).</p>",
|
||||||
|
name = "Nécessaire"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vendors = listOf(
|
||||||
|
VendorDTO(
|
||||||
|
id = "vendors-crashlytics",
|
||||||
|
order = 0,
|
||||||
|
isAccepted = true,
|
||||||
|
isRequired = true,
|
||||||
|
translations = listOf(
|
||||||
|
VendorTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
text = "<p>Ces traceurs sont nécessaire au fonctionnement de l’application. Ils permettent de vérifier la stabilité technique de l’application et de mesurer notre audience. Ces données ne sont utilisées que pour notre compte exclusif (en ne produisant que des données statistiques anonymes).</p>"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
VendorDTO(
|
||||||
|
id = "vendors-matomo",
|
||||||
|
order = 1,
|
||||||
|
isAccepted = true,
|
||||||
|
isRequired = false,
|
||||||
|
translations = listOf(
|
||||||
|
VendorTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
text = "<p>Ces traceurs sont nécessaire au fonctionnement de l’application. Ils permettent de vérifier la stabilité technique de l’application et de mesurer notre audience. Ces données ne sont utilisées que pour notre compte exclusif (en ne produisant que des données statistiques anonymes).</p>"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
PurposeDTO(
|
||||||
|
id = "purpose-advertising",
|
||||||
|
order = 1,
|
||||||
|
isRequired = false,
|
||||||
|
isAccepted = PurposeStatusDTO.REJECTED,
|
||||||
|
translations = listOf(
|
||||||
|
PurposeTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
text = "<p>Ces traceurs sont nécessaires pour afficher des publicités susceptibles de vous intéresser. Ils permettent de mesurer l’efficacité de nos campagnes publicitaires et de personnaliser les publicités affichées.</p>",
|
||||||
|
name = "Publicité"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vendors = listOf(
|
||||||
|
VendorDTO(
|
||||||
|
id = "vendors-admob",
|
||||||
|
order = 0,
|
||||||
|
isAccepted = true,
|
||||||
|
isRequired = false,
|
||||||
|
translations = listOf(
|
||||||
|
VendorTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
text = "<p>Ces traceurs sont nécessaires pour afficher des publicités susceptibles de vous intéresser. Ils permettent de mesurer l’efficacité de nos campagnes publicitaires et de personnaliser les publicités affichées.</p>"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
PurposeDTO(
|
||||||
|
id = "purpose-analytics",
|
||||||
|
order = 2,
|
||||||
|
isRequired = false,
|
||||||
|
isAccepted = PurposeStatusDTO.ACCEPTED,
|
||||||
|
translations = listOf(
|
||||||
|
PurposeTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
text = "<p>Ces traceurs sont nécessaires pour mesurer l’efficacité de nos contenus. Ils permettent de mesurer l’audience de l’application et de comprendre comment les utilisateurs interagissent avec l’application.</p>",
|
||||||
|
name = "Analyse"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vendors = listOf(
|
||||||
|
VendorDTO(
|
||||||
|
id = "vendors-firebase",
|
||||||
|
order = 0,
|
||||||
|
isAccepted = true,
|
||||||
|
isRequired = false,
|
||||||
|
translations = listOf(
|
||||||
|
VendorTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
text = "<p>Ces traceurs sont nécessaires pour mesurer l’efficacité de nos contenus. Ils permettent de mesurer l’audience de l’application et de comprendre comment les utilisateurs interagissent avec l’application.</p>"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
PurposeDTO(
|
||||||
|
id = "purpose-personalization",
|
||||||
|
order = 3,
|
||||||
|
isRequired = false,
|
||||||
|
isAccepted = PurposeStatusDTO.ACCEPTED,
|
||||||
|
translations = listOf(
|
||||||
|
PurposeTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
text = "<p>Ces traceurs sont nécessaires pour mesurer l’efficacité de nos contenus. Ils permettent de mesurer l’audience de l’application et de comprendre comment les utilisateurs interagissent avec l’application.</p>",
|
||||||
|
name = "Personnalisation"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vendors = listOf(
|
||||||
|
VendorDTO(
|
||||||
|
id = "vendors-firebase",
|
||||||
|
order = 0,
|
||||||
|
isAccepted = true,
|
||||||
|
isRequired = false,
|
||||||
|
translations = listOf(
|
||||||
|
VendorTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
text = "<p>Ces traceurs sont nécessaires pour mesurer l’efficacité de nos contenus. Ils permettent de mesurer l’audience de l’application et de comprendre comment les utilisateurs interagissent avec l’application.</p>"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
PurposeDTO(
|
||||||
|
id = "purpose-social",
|
||||||
|
order = 4,
|
||||||
|
isRequired = false,
|
||||||
|
isAccepted = PurposeStatusDTO.ACCEPTED,
|
||||||
|
translations = listOf(
|
||||||
|
PurposeTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
text = "<p>Ces traceurs sont nécessaires pour mesurer l’efficacité de nos contenus. Ils permettent de mesurer l’audience de l’application et de comprendre comment les utilisateurs interagissent avec l’application.</p>",
|
||||||
|
name = "Social"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vendors = listOf(
|
||||||
|
VendorDTO(
|
||||||
|
id = "vendors-firebase",
|
||||||
|
order = 0,
|
||||||
|
isAccepted = true,
|
||||||
|
isRequired = false,
|
||||||
|
translations = listOf(
|
||||||
|
VendorTranslationDTO(
|
||||||
|
id = "UUID",
|
||||||
|
language = "fr",
|
||||||
|
text = "<p>Ces traceurs sont nécessaires pour mesurer l’efficacité de nos contenus. Ils permettent de mesurer l’audience de l’application et de comprendre comment les utilisateurs interagissent avec l’application.</p>"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
override suspend fun getConsentConfig(
|
||||||
|
applicationID: String,
|
||||||
|
installationID: String,
|
||||||
|
): Response<GetConsentConfigDTO> {
|
||||||
|
return Response.success(consents)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,7 +8,7 @@ internal data class PurposeDTO(
|
|||||||
@SerialName("identifier") val id: String,
|
@SerialName("identifier") val id: String,
|
||||||
@SerialName("order") val order: Int,
|
@SerialName("order") val order: Int,
|
||||||
@SerialName("isRequired") val isRequired: Boolean,
|
@SerialName("isRequired") val isRequired: Boolean,
|
||||||
@SerialName("isAccepted") val isAccepted: Boolean,
|
@SerialName("isAccepted") val isAccepted: PurposeStatusDTO,
|
||||||
@SerialName("translations") val translations: List<PurposeTranslationDTO>,
|
@SerialName("translations") val translations: List<PurposeTranslationDTO>,
|
||||||
@SerialName("vendors") val vendors: List<VendorDTO>,
|
@SerialName("vendors") val vendors: List<VendorDTO>,
|
||||||
)
|
)
|
@ -0,0 +1,16 @@
|
|||||||
|
package fr.openium.consentium_ui.data.remote.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
internal enum class PurposeStatusDTO {
|
||||||
|
@SerialName("ACCEPTED")
|
||||||
|
ACCEPTED,
|
||||||
|
|
||||||
|
@SerialName("REJECTED")
|
||||||
|
REJECTED,
|
||||||
|
|
||||||
|
@SerialName("NOT_DEFINED")
|
||||||
|
NOT_DEFINED,
|
||||||
|
}
|
@ -8,4 +8,5 @@ internal data class PurposeTranslationDTO(
|
|||||||
@SerialName("id") val id: String,
|
@SerialName("id") val id: String,
|
||||||
@SerialName("lang") val language: String,
|
@SerialName("lang") val language: String,
|
||||||
@SerialName("text") val text: String,
|
@SerialName("text") val text: String,
|
||||||
|
@SerialName("name") val name: String,
|
||||||
)
|
)
|
@ -7,7 +7,7 @@ internal fun PurposeDTO.toPurposeData() =
|
|||||||
PurposeData(
|
PurposeData(
|
||||||
identifier = id,
|
identifier = id,
|
||||||
isRequired = isRequired,
|
isRequired = isRequired,
|
||||||
isAccepted = isAccepted,
|
isAccepted = isAccepted.toPurposeStatusData(),
|
||||||
order = order,
|
order = order,
|
||||||
vendors = vendors.toVendorDataList(),
|
vendors = vendors.toVendorDataList(),
|
||||||
translations = translations.toPurposeTranslationDataList(),
|
translations = translations.toPurposeTranslationDataList(),
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package fr.openium.consentium_ui.domain.adapter
|
||||||
|
|
||||||
|
import fr.openium.consentium_ui.data.remote.model.PurposeStatusDTO
|
||||||
|
import fr.openium.consentium_ui.domain.model.PurposeStatusData
|
||||||
|
|
||||||
|
internal fun PurposeStatusDTO.toPurposeStatusData(): PurposeStatusData {
|
||||||
|
return when (this) {
|
||||||
|
PurposeStatusDTO.ACCEPTED -> PurposeStatusData.ACCEPTED
|
||||||
|
PurposeStatusDTO.REJECTED -> PurposeStatusData.REJECTED
|
||||||
|
PurposeStatusDTO.NOT_DEFINED -> PurposeStatusData.NOT_DEFINED
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ internal fun PurposeTranslationDTO.toPurposeTranslationData() =
|
|||||||
id = id,
|
id = id,
|
||||||
language = language,
|
language = language,
|
||||||
text = text,
|
text = text,
|
||||||
|
name = name,
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun List<PurposeTranslationDTO>.toPurposeTranslationDataList() = map { it.toPurposeTranslationData() }
|
internal fun List<PurposeTranslationDTO>.toPurposeTranslationDataList() = map { it.toPurposeTranslationData() }
|
@ -4,6 +4,8 @@ import dagger.Binds
|
|||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import fr.openium.consentium_ui.domain.usecase.GetApplicationLanguageUseCase
|
||||||
|
import fr.openium.consentium_ui.domain.usecase.GetApplicationLanguageUseCaseImpl
|
||||||
import fr.openium.consentium_ui.domain.usecase.GetConfigTextForLanguageUseCase
|
import fr.openium.consentium_ui.domain.usecase.GetConfigTextForLanguageUseCase
|
||||||
import fr.openium.consentium_ui.domain.usecase.GetConfigTextForLanguageUseCaseImpl
|
import fr.openium.consentium_ui.domain.usecase.GetConfigTextForLanguageUseCaseImpl
|
||||||
|
|
||||||
@ -16,4 +18,9 @@ internal interface ConsentiumUseCaseModule {
|
|||||||
getConfigTextForLanguageUseCaseImpl: GetConfigTextForLanguageUseCaseImpl,
|
getConfigTextForLanguageUseCaseImpl: GetConfigTextForLanguageUseCaseImpl,
|
||||||
): GetConfigTextForLanguageUseCase
|
): GetConfigTextForLanguageUseCase
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
fun bindGetApplicationLangageUseCase(
|
||||||
|
getApplicationLangageUseCaseImpl: GetApplicationLanguageUseCaseImpl,
|
||||||
|
): GetApplicationLanguageUseCase
|
||||||
|
|
||||||
}
|
}
|
@ -4,7 +4,7 @@ internal data class PurposeData(
|
|||||||
val identifier: String,
|
val identifier: String,
|
||||||
val order: Int,
|
val order: Int,
|
||||||
val isRequired: Boolean,
|
val isRequired: Boolean,
|
||||||
val isAccepted: Boolean,
|
val isAccepted: PurposeStatusData,
|
||||||
val translations: List<PurposeTranslationData>,
|
val translations: List<PurposeTranslationData>,
|
||||||
val vendors: List<VendorData>,
|
val vendors: List<VendorData>,
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package fr.openium.consentium_ui.domain.model
|
||||||
|
|
||||||
|
internal enum class PurposeStatusData {
|
||||||
|
ACCEPTED,
|
||||||
|
REJECTED,
|
||||||
|
NOT_DEFINED,
|
||||||
|
}
|
@ -4,4 +4,5 @@ internal data class PurposeTranslationData(
|
|||||||
val id: String,
|
val id: String,
|
||||||
val language: String,
|
val language: String,
|
||||||
val text: String,
|
val text: String,
|
||||||
|
val name: String,
|
||||||
)
|
)
|
@ -10,7 +10,6 @@ internal class ConsentiumRepository @Inject constructor(
|
|||||||
private val getConsentiumUniqueInstallationIdUseCase: GetConsentiumUniqueInstallationIdUseCase,
|
private val getConsentiumUniqueInstallationIdUseCase: GetConsentiumUniqueInstallationIdUseCase,
|
||||||
private val consentiumUIApi: ConsentiumUIApi,
|
private val consentiumUIApi: ConsentiumUIApi,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun getConsentiumConfig(
|
suspend fun getConsentiumConfig(
|
||||||
applicationId: String,
|
applicationId: String,
|
||||||
): ConsentiumUIRepositoryResponse {
|
): ConsentiumUIRepositoryResponse {
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package fr.openium.consentium_ui.domain.usecase
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal interface GetApplicationLanguageUseCase {
|
||||||
|
suspend operator fun invoke(context: Context): String
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class GetApplicationLanguageUseCaseImpl @Inject constructor() : GetApplicationLanguageUseCase {
|
||||||
|
override suspend fun invoke(context: Context): String {
|
||||||
|
return context.resources.configuration.locales[0].language
|
||||||
|
}
|
||||||
|
}
|
@ -6,22 +6,24 @@ import javax.inject.Inject
|
|||||||
private const val FALLBACK_LANGUAGE = "en"
|
private const val FALLBACK_LANGUAGE = "en"
|
||||||
|
|
||||||
internal interface GetConfigTextForLanguageUseCase {
|
internal interface GetConfigTextForLanguageUseCase {
|
||||||
suspend fun invoke(
|
suspend operator fun invoke(
|
||||||
language: String = FALLBACK_LANGUAGE,
|
language: String = FALLBACK_LANGUAGE,
|
||||||
configData: ContentConfigData,
|
configData: ContentConfigData,
|
||||||
): GetConfigTextForLanguageUseCaseResponce
|
): GetConfigTextForLanguageUseCaseResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class GetConfigTextForLanguageUseCaseImpl @Inject constructor() : GetConfigTextForLanguageUseCase {
|
internal class GetConfigTextForLanguageUseCaseImpl @Inject constructor() :
|
||||||
|
GetConfigTextForLanguageUseCase {
|
||||||
|
|
||||||
override suspend fun invoke(
|
override suspend fun invoke(
|
||||||
language: String,
|
language: String,
|
||||||
configData: ContentConfigData,
|
configData: ContentConfigData,
|
||||||
): GetConfigTextForLanguageUseCaseResponce {
|
): GetConfigTextForLanguageUseCaseResponse {
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
|
|
||||||
val canIUseTheTranslation = configData.mainTextTranslation.any { it.language == language } &&
|
val canIUseTheTranslation =
|
||||||
|
configData.mainTextTranslation.any { it.language == language } &&
|
||||||
configData.purposes.all { purposeData ->
|
configData.purposes.all { purposeData ->
|
||||||
purposeData.translations.any { it.language == language } &&
|
purposeData.translations.any { it.language == language } &&
|
||||||
purposeData.vendors.all { vendorData ->
|
purposeData.vendors.all { vendorData ->
|
||||||
@ -32,7 +34,8 @@ internal class GetConfigTextForLanguageUseCaseImpl @Inject constructor() : GetCo
|
|||||||
val languageToUse = if (canIUseTheTranslation) {
|
val languageToUse = if (canIUseTheTranslation) {
|
||||||
language
|
language
|
||||||
} else {
|
} else {
|
||||||
val isThereAGoodFallbackLanguage = configData.mainTextTranslation.any { it.language == FALLBACK_LANGUAGE } &&
|
val isThereAGoodFallbackLanguage =
|
||||||
|
configData.mainTextTranslation.any { it.language == FALLBACK_LANGUAGE } &&
|
||||||
configData.purposes.all { purposeData ->
|
configData.purposes.all { purposeData ->
|
||||||
purposeData.translations.any { it.language == FALLBACK_LANGUAGE } &&
|
purposeData.translations.any { it.language == FALLBACK_LANGUAGE } &&
|
||||||
purposeData.vendors.all { vendorData ->
|
purposeData.vendors.all { vendorData ->
|
||||||
@ -61,23 +64,24 @@ internal class GetConfigTextForLanguageUseCaseImpl @Inject constructor() : GetCo
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (languageToUse == FALLBACK_LANGUAGE) {
|
if (languageToUse == FALLBACK_LANGUAGE) {
|
||||||
GetConfigTextForLanguageUseCaseResponce.DefaultLanguage(filteredConfigData)
|
GetConfigTextForLanguageUseCaseResponse.DefaultLanguage(filteredConfigData)
|
||||||
} else {
|
} else {
|
||||||
GetConfigTextForLanguageUseCaseResponce.Success(filteredConfigData)
|
GetConfigTextForLanguageUseCaseResponse.Success(filteredConfigData)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
GetConfigTextForLanguageUseCaseResponce.Error
|
GetConfigTextForLanguageUseCaseResponse.Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface GetConfigTextForLanguageUseCaseResponce {
|
internal interface GetConfigTextForLanguageUseCaseResponse {
|
||||||
|
|
||||||
data object Error : GetConfigTextForLanguageUseCaseResponce
|
data object Error : GetConfigTextForLanguageUseCaseResponse
|
||||||
|
|
||||||
data class Success(val configData: ContentConfigData) : GetConfigTextForLanguageUseCaseResponce
|
data class Success(val configData: ContentConfigData) : GetConfigTextForLanguageUseCaseResponse
|
||||||
|
|
||||||
data class DefaultLanguage(val configData: ContentConfigData) : GetConfigTextForLanguageUseCaseResponce
|
data class DefaultLanguage(val configData: ContentConfigData) :
|
||||||
|
GetConfigTextForLanguageUseCaseResponse
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package fr.openium.consentium_ui.ui
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.slideIn
|
||||||
|
import androidx.compose.animation.slideOut
|
||||||
|
import androidx.compose.animation.togetherWith
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
|
import fr.openium.consentium_ui.ui.components.ConsentiumUIDetailConsentComponent
|
||||||
|
import fr.openium.consentium_ui.ui.components.ConsentiumUIErrorComponent
|
||||||
|
import fr.openium.consentium_ui.ui.components.ConsentiumUIGeneralConsentComponent
|
||||||
|
import fr.openium.consentium_ui.ui.components.ConsentiumUILoadingComponent
|
||||||
|
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
|
||||||
|
import fr.openium.consentium_ui.ui.model.DetailConsentUI
|
||||||
|
import fr.openium.consentium_ui.ui.model.LoadingElement
|
||||||
|
import fr.openium.consentium_ui.ui.state.ConsentiumUIState
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ConsentiumScreen(
|
||||||
|
page: ConsentiumPageUI,
|
||||||
|
state: ConsentiumUIState,
|
||||||
|
loadingElement: LoadingElement,
|
||||||
|
onNavigateBack: () -> Unit,
|
||||||
|
onAcceptAndClose: (consents: DetailConsentUI) -> Unit,
|
||||||
|
onDenyAndClose: (consents: DetailConsentUI) -> Unit,
|
||||||
|
onSaveAndCloseDetails: (consents: DetailConsentUI) -> Unit,
|
||||||
|
onNavigateToDetails: () -> Unit,
|
||||||
|
onClickCookiesPolicies: () -> Unit,
|
||||||
|
) {
|
||||||
|
when (state) {
|
||||||
|
is ConsentiumUIState.Loading -> {
|
||||||
|
ConsentiumUILoadingComponent()
|
||||||
|
}
|
||||||
|
|
||||||
|
is ConsentiumUIState.Error -> {
|
||||||
|
ConsentiumUIErrorComponent(
|
||||||
|
errorMessage = state.message,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ConsentiumUIState.Loaded -> {
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = page,
|
||||||
|
transitionSpec = {
|
||||||
|
when (page) {
|
||||||
|
ConsentiumPageUI.GENERAL_CONSENT -> {
|
||||||
|
slideIn(
|
||||||
|
initialOffset = { fullSize -> IntOffset(-fullSize.width, 0) }
|
||||||
|
).togetherWith(
|
||||||
|
slideOut(
|
||||||
|
targetOffset = { fullSize -> IntOffset(fullSize.width, 0) }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsentiumPageUI.DETAILS_CONSENT -> {
|
||||||
|
slideIn(
|
||||||
|
initialOffset = { fullSize -> IntOffset(fullSize.width, 0) }
|
||||||
|
).togetherWith(
|
||||||
|
slideOut(
|
||||||
|
targetOffset = { fullSize -> IntOffset(-fullSize.width, 0) }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { currentPage ->
|
||||||
|
|
||||||
|
when (currentPage) {
|
||||||
|
ConsentiumPageUI.GENERAL_CONSENT -> {
|
||||||
|
ConsentiumUIGeneralConsentComponent(
|
||||||
|
generalConsentUI = state.generalConsentUI,
|
||||||
|
onNavigateToDetails = onNavigateToDetails,
|
||||||
|
onAcceptAndClose = {
|
||||||
|
onAcceptAndClose(state.detailConsentUI)
|
||||||
|
},
|
||||||
|
onDenyAndClose = {
|
||||||
|
onDenyAndClose(state.detailConsentUI)
|
||||||
|
},
|
||||||
|
onClickCookiesPolicies = onClickCookiesPolicies,
|
||||||
|
loadingElement = loadingElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsentiumPageUI.DETAILS_CONSENT -> {
|
||||||
|
ConsentiumUIDetailConsentComponent(
|
||||||
|
detailConsentUI = state.detailConsentUI,
|
||||||
|
onNavigateBack = onNavigateBack,
|
||||||
|
onSave = {
|
||||||
|
onSaveAndCloseDetails(state.detailConsentUI)
|
||||||
|
},
|
||||||
|
loadingElement = loadingElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
|||||||
|
package fr.openium.consentium_ui.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import fr.openium.consentium_ui.domain.repository.ConsentiumRepository
|
||||||
|
import fr.openium.consentium_ui.domain.repository.ConsentiumUIRepositoryResponse
|
||||||
|
import fr.openium.consentium_ui.domain.usecase.GetApplicationLanguageUseCase
|
||||||
|
import fr.openium.consentium_ui.domain.usecase.GetConfigTextForLanguageUseCase
|
||||||
|
import fr.openium.consentium_ui.domain.usecase.GetConfigTextForLanguageUseCaseResponse
|
||||||
|
import fr.openium.consentium_ui.ui.adapter.toDetailConsentUI
|
||||||
|
import fr.openium.consentium_ui.ui.adapter.toGeneralConsentUI
|
||||||
|
import fr.openium.consentium_ui.ui.state.ConsentiumUIState
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
internal class ConsentiumUIViewModel @Inject constructor(
|
||||||
|
@ApplicationContext private val context: Context,
|
||||||
|
private val consentiumUIRepository: ConsentiumRepository,
|
||||||
|
private val configTextLanguageUseCase: GetConfigTextForLanguageUseCase,
|
||||||
|
private val getApplicationLanguageUseCase: GetApplicationLanguageUseCase,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _state = MutableStateFlow<ConsentiumUIState>(ConsentiumUIState.Loading)
|
||||||
|
val state: StateFlow<ConsentiumUIState> by lazy { _state.asStateFlow() }
|
||||||
|
|
||||||
|
fun init(appId: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
|
||||||
|
_state.value = ConsentiumUIState.Loading
|
||||||
|
|
||||||
|
when (val consentiumUIFetchResponse = consentiumUIRepository.getConsentiumConfig(appId)) {
|
||||||
|
|
||||||
|
is ConsentiumUIRepositoryResponse.Success -> {
|
||||||
|
val consentUIResponse =
|
||||||
|
configTextLanguageUseCase(getApplicationLanguageUseCase(context), consentiumUIFetchResponse.contentConfigData)
|
||||||
|
|
||||||
|
when (val consentUI = consentUIResponse) {
|
||||||
|
|
||||||
|
is GetConfigTextForLanguageUseCaseResponse.Success -> {
|
||||||
|
_state.emit(ConsentiumUIState.Loaded(
|
||||||
|
generalConsentUI = consentUI.configData.toGeneralConsentUI(),
|
||||||
|
detailConsentUI = consentUI.configData.toDetailConsentUI(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
is GetConfigTextForLanguageUseCaseResponse.DefaultLanguage -> {
|
||||||
|
_state.emit(ConsentiumUIState.Loaded(
|
||||||
|
generalConsentUI = consentUI.configData.toGeneralConsentUI(),
|
||||||
|
detailConsentUI = consentUI.configData.toDetailConsentUI(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
is GetConfigTextForLanguageUseCaseResponse.Error -> {
|
||||||
|
_state.emit(ConsentiumUIState.Error("Failed to load data"))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is ConsentiumUIRepositoryResponse.Error -> {
|
||||||
|
_state.value = ConsentiumUIState.Error("Failed to load data")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.adapter
|
||||||
|
|
||||||
|
import fr.openium.consentium.api.model.PurposeChoice
|
||||||
|
import fr.openium.consentium_ui.domain.model.ContentConfigData
|
||||||
|
import fr.openium.consentium_ui.ui.model.DetailConsentUI
|
||||||
|
import fr.openium.consentium_ui.ui.model.PurposeStatusUI
|
||||||
|
import fr.openium.consentium_ui.ui.model.PurposeUI
|
||||||
|
|
||||||
|
internal fun ContentConfigData.toDetailConsentUI(): DetailConsentUI = DetailConsentUI(
|
||||||
|
conservationDurationText = mainTextTranslation.first().durationText,
|
||||||
|
purposes = purposes.sortedBy { it.order }.map { it.toPurposeUI() },
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun DetailConsentUI.toPurposeChoices(): List<PurposeChoice> = purposes.map {
|
||||||
|
it.toPurposeChoice()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun DetailConsentUI.toDeniedPurposeChoices(): List<PurposeChoice> = purposes.map {
|
||||||
|
it.toPurposeChoice().copy(isAccepted = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun DetailConsentUI.toAcceptedPurposeChoices(): List<PurposeChoice> = purposes.map {
|
||||||
|
it.toPurposeChoice().copy(isAccepted = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun PurposeUI.toPurposeChoice(): PurposeChoice =
|
||||||
|
PurposeChoice(
|
||||||
|
purposeIdentifier = id,
|
||||||
|
isAccepted = isAccepted == PurposeStatusUI.ACCEPTED,
|
||||||
|
vendors = emptyList(), // Not in v1
|
||||||
|
)
|
@ -0,0 +1,12 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.adapter
|
||||||
|
|
||||||
|
import fr.openium.consentium_ui.domain.model.ContentConfigData
|
||||||
|
import fr.openium.consentium_ui.ui.model.GeneralConsentUI
|
||||||
|
|
||||||
|
internal fun ContentConfigData.toGeneralConsentUI(): GeneralConsentUI =
|
||||||
|
GeneralConsentUI(
|
||||||
|
applicationName = applicationName,
|
||||||
|
iconUrl = iconUrl,
|
||||||
|
mainConsentText = mainTextTranslation.first().mainConsentText,
|
||||||
|
consentPageUrl = mainTextTranslation.first().consentPageUrl,
|
||||||
|
)
|
@ -0,0 +1,10 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.adapter
|
||||||
|
|
||||||
|
import fr.openium.consentium_ui.domain.model.PurposeStatusData
|
||||||
|
import fr.openium.consentium_ui.ui.model.PurposeStatusUI
|
||||||
|
|
||||||
|
internal fun PurposeStatusData.toPurposeStatusUI(): PurposeStatusUI = when(this) {
|
||||||
|
PurposeStatusData.ACCEPTED -> PurposeStatusUI.ACCEPTED
|
||||||
|
PurposeStatusData.REJECTED -> PurposeStatusUI.REJECTED
|
||||||
|
PurposeStatusData.NOT_DEFINED -> PurposeStatusUI.NOT_DEFINED
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.adapter
|
||||||
|
|
||||||
|
import fr.openium.consentium_ui.domain.model.PurposeData
|
||||||
|
import fr.openium.consentium_ui.ui.model.PurposeUI
|
||||||
|
|
||||||
|
internal fun PurposeData.toPurposeUI(): PurposeUI = PurposeUI(
|
||||||
|
id = identifier,
|
||||||
|
isRequired = isRequired,
|
||||||
|
isAccepted = isAccepted.toPurposeStatusUI(),
|
||||||
|
title = translations.first().name,
|
||||||
|
description = translations.first().text,
|
||||||
|
vendors = vendors.toVendorUIList(),
|
||||||
|
)
|
@ -0,0 +1,11 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.adapter
|
||||||
|
|
||||||
|
import fr.openium.consentium_ui.domain.model.VendorData
|
||||||
|
import fr.openium.consentium_ui.ui.model.VendorUI
|
||||||
|
|
||||||
|
internal fun VendorData.toVendorUI() = VendorUI(
|
||||||
|
id = identifier,
|
||||||
|
isAccepted = isAccepted,
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun List<VendorData>.toVendorUIList() = map { it.toVendorUI() }
|
@ -0,0 +1,119 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import fr.openium.consentium.api.Consentium
|
||||||
|
import fr.openium.consentium.api.state.SetConsentiumState
|
||||||
|
import fr.openium.consentium_ui.R
|
||||||
|
import fr.openium.consentium_ui.ui.ConsentiumScreen
|
||||||
|
import fr.openium.consentium_ui.ui.ConsentiumUIViewModel
|
||||||
|
import fr.openium.consentium_ui.ui.adapter.toAcceptedPurposeChoices
|
||||||
|
import fr.openium.consentium_ui.ui.adapter.toDeniedPurposeChoices
|
||||||
|
import fr.openium.consentium_ui.ui.adapter.toPurposeChoices
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumColors
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumDefaults
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumTypography
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.LocalColors
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.LocalTypography
|
||||||
|
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
|
||||||
|
import fr.openium.consentium_ui.ui.model.LoadingElement
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ConsentiumComponent(
|
||||||
|
consentium: Consentium,
|
||||||
|
onQuitConsent: () -> (Unit),
|
||||||
|
colors: ConsentiumColors = ConsentiumDefaults.colors(),
|
||||||
|
typography: ConsentiumTypography = ConsentiumDefaults.typography(),
|
||||||
|
defaultLandingPage: ConsentiumPageUI = ConsentiumPageUI.GENERAL_CONSENT,
|
||||||
|
) {
|
||||||
|
// Property
|
||||||
|
val viewModel: ConsentiumUIViewModel = hiltViewModel()
|
||||||
|
val loadingState by viewModel.state.collectAsStateWithLifecycle()
|
||||||
|
var currentPage by remember(defaultLandingPage) { mutableStateOf(defaultLandingPage) }
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val context = LocalContext.current
|
||||||
|
val errorMessage = stringResource(id = R.string.save_consents_error_message)
|
||||||
|
val consentiumState by consentium.saveConsentState.collectAsStateWithLifecycle()
|
||||||
|
var loadingElement by remember { mutableStateOf(LoadingElement.NONE) }
|
||||||
|
|
||||||
|
// Effect
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.init(consentium.applicationId)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(consentiumState) {
|
||||||
|
when (consentiumState) {
|
||||||
|
SetConsentiumState.Idle,
|
||||||
|
SetConsentiumState.Loading,
|
||||||
|
-> {
|
||||||
|
}
|
||||||
|
|
||||||
|
SetConsentiumState.Error -> {
|
||||||
|
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
SetConsentiumState.Success -> {
|
||||||
|
onQuitConsent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// View
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalColors provides colors,
|
||||||
|
LocalTypography provides typography,
|
||||||
|
) {
|
||||||
|
ConsentiumScreen(
|
||||||
|
state = loadingState,
|
||||||
|
page = currentPage,
|
||||||
|
loadingElement = if (consentiumState is SetConsentiumState.Loading) loadingElement else LoadingElement.NONE,
|
||||||
|
onNavigateBack = {
|
||||||
|
when {
|
||||||
|
defaultLandingPage == ConsentiumPageUI.GENERAL_CONSENT && currentPage == ConsentiumPageUI.DETAILS_CONSENT -> {
|
||||||
|
currentPage = ConsentiumPageUI.GENERAL_CONSENT
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
onQuitConsent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onNavigateToDetails = {
|
||||||
|
currentPage = ConsentiumPageUI.DETAILS_CONSENT
|
||||||
|
},
|
||||||
|
onAcceptAndClose = { consent ->
|
||||||
|
scope.launch {
|
||||||
|
loadingElement = LoadingElement.BUTTON
|
||||||
|
consentium.saveConsents(consent.toAcceptedPurposeChoices())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDenyAndClose = { consent ->
|
||||||
|
scope.launch {
|
||||||
|
loadingElement = LoadingElement.LINK
|
||||||
|
consentium.saveConsents(consent.toDeniedPurposeChoices())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSaveAndCloseDetails = { consent ->
|
||||||
|
scope.launch {
|
||||||
|
loadingElement = LoadingElement.BUTTON
|
||||||
|
consentium.saveConsents(consent.toPurposeChoices())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClickCookiesPolicies = {
|
||||||
|
// TODO Open cookies policies
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components
|
||||||
|
|
||||||
|
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.fillMaxSize
|
||||||
|
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.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import fr.openium.consentium_ui.R
|
||||||
|
import fr.openium.consentium_ui.ui.components.core.PurposeComponent
|
||||||
|
import fr.openium.consentium_ui.ui.components.core.button.ConsentButton
|
||||||
|
import fr.openium.consentium_ui.ui.components.core.button.ConsentiumUIButtonStyle
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
||||||
|
import fr.openium.consentium_ui.ui.model.DetailConsentUI
|
||||||
|
import fr.openium.consentium_ui.ui.model.LoadingElement
|
||||||
|
import fr.openium.consentium_ui.ui.utils.htmlToAnnotatedString
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ConsentiumUIDetailConsentComponent(
|
||||||
|
detailConsentUI: DetailConsentUI,
|
||||||
|
loadingElement: LoadingElement,
|
||||||
|
onNavigateBack: () -> Unit,
|
||||||
|
onSave: () -> Unit,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 24.dp),
|
||||||
|
) {
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Start,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
IconButton(onClick = { onNavigateBack() }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
|
contentDescription = "Back",
|
||||||
|
tint = ConsentiumUITheme.colors.onSurface,
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.parameters),
|
||||||
|
style = ConsentiumUITheme.typography.h2,
|
||||||
|
color = ConsentiumUITheme.colors.onSurface,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(25.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = htmlToAnnotatedString(detailConsentUI.conservationDurationText),
|
||||||
|
style = ConsentiumUITheme.typography.p3,
|
||||||
|
color = ConsentiumUITheme.colors.onSurface,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f)
|
||||||
|
) {
|
||||||
|
items(detailConsentUI.purposes) { purposeUI ->
|
||||||
|
PurposeComponent(
|
||||||
|
purposeUI = purposeUI,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
ConsentButton(
|
||||||
|
text = stringResource(R.string.save),
|
||||||
|
buttonStyle = ConsentiumUIButtonStyle.PRIMARY,
|
||||||
|
onclick = onSave,
|
||||||
|
isLoading = loadingElement == LoadingElement.BUTTON,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ConsentiumUIErrorComponent(
|
||||||
|
errorMessage: String,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = errorMessage,
|
||||||
|
color = ConsentiumUITheme.colors.error,
|
||||||
|
style = ConsentiumUITheme.typography.b2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components
|
||||||
|
|
||||||
|
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.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import coil3.compose.AsyncImage
|
||||||
|
import fr.openium.consentium_ui.R
|
||||||
|
import fr.openium.consentium_ui.ui.components.core.TextLink
|
||||||
|
import fr.openium.consentium_ui.ui.components.core.button.ConsentButton
|
||||||
|
import fr.openium.consentium_ui.ui.components.core.button.ConsentiumUIButtonStyle
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
||||||
|
import fr.openium.consentium_ui.ui.model.GeneralConsentUI
|
||||||
|
import fr.openium.consentium_ui.ui.model.LoadingElement
|
||||||
|
import fr.openium.consentium_ui.ui.utils.htmlToAnnotatedString
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ConsentiumUIGeneralConsentComponent(
|
||||||
|
generalConsentUI: GeneralConsentUI,
|
||||||
|
loadingElement: LoadingElement,
|
||||||
|
onClickCookiesPolicies: () -> Unit,
|
||||||
|
onDenyAndClose: () -> Unit,
|
||||||
|
onAcceptAndClose: () -> Unit,
|
||||||
|
onNavigateToDetails: () -> Unit,
|
||||||
|
) {
|
||||||
|
// View
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 24.dp),
|
||||||
|
) {
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
) {
|
||||||
|
AsyncImage(
|
||||||
|
modifier = Modifier.heightIn(min = 48.dp),
|
||||||
|
model = generalConsentUI.iconUrl,
|
||||||
|
contentDescription = "Image",
|
||||||
|
contentScale = ContentScale.FillBounds,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = htmlToAnnotatedString(generalConsentUI.mainConsentText),
|
||||||
|
style = ConsentiumUITheme.typography.p3,
|
||||||
|
textAlign = TextAlign.Start,
|
||||||
|
color = ConsentiumUITheme.colors.onSurface,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
TextLink(
|
||||||
|
text = stringResource(R.string.cookies),
|
||||||
|
onclick = onClickCookiesPolicies,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
|
TextLink(
|
||||||
|
text = stringResource(R.string.refuse),
|
||||||
|
onclick = onDenyAndClose,
|
||||||
|
isLoading = loadingElement == LoadingElement.LINK,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
ConsentButton(
|
||||||
|
text = stringResource(R.string.accept),
|
||||||
|
buttonStyle = ConsentiumUIButtonStyle.PRIMARY,
|
||||||
|
onclick = onAcceptAndClose,
|
||||||
|
isLoading = loadingElement == LoadingElement.BUTTON,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
ConsentButton(
|
||||||
|
text = stringResource(R.string.parameters),
|
||||||
|
buttonStyle = ConsentiumUIButtonStyle.SECONDARY,
|
||||||
|
onclick = onNavigateToDetails,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showSystemUi = true)
|
||||||
|
@Composable
|
||||||
|
private fun ConsentiumUIGeneralConsentComponentPreview() {
|
||||||
|
ConsentiumUIGeneralConsentComponent(
|
||||||
|
generalConsentUI = GeneralConsentUI(
|
||||||
|
iconUrl = "https://amp.openium.fr/openium.png",
|
||||||
|
mainConsentText = "[Nom de l’application] utilise des cookies pour différents objectifs : faire fonctionner l’application, améliorer nos services en mesurant l’efficacité de nos contenus et afficher des publicités susceptibles de vous intéresser. \n\n En cliquant sur “Accepter et fermer”, vous acceptez cette utilisation sur l’application mobile. Vous pouvez également paramétrer vos choix en cliquant sur “Paramétrer mes choix” ou refuser ces cookies en cliquant sur “Continuer sans accepter”. Vous pouvez changer d’avis à tout moment depuis les paramètres de votre compte via l’onglet “Notifications et cookies”.",
|
||||||
|
applicationName = "Application name",
|
||||||
|
consentPageUrl = "https://www.google.com"
|
||||||
|
),
|
||||||
|
onClickCookiesPolicies = {},
|
||||||
|
onDenyAndClose = {},
|
||||||
|
onAcceptAndClose = {},
|
||||||
|
onNavigateToDetails = {},
|
||||||
|
loadingElement = LoadingElement.LINK,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ConsentiumUILoadingComponent() {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
color = ConsentiumUITheme.colors.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components.core
|
||||||
|
|
||||||
|
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.height
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import fr.openium.consentium_ui.R
|
||||||
|
import fr.openium.consentium_ui.ui.components.core.toggle.ConsentiumUISwitch
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
||||||
|
import fr.openium.consentium_ui.ui.model.PurposeStatusUI
|
||||||
|
import fr.openium.consentium_ui.ui.model.PurposeUI
|
||||||
|
import fr.openium.consentium_ui.ui.utils.htmlToAnnotatedString
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun PurposeComponent(
|
||||||
|
purposeUI: PurposeUI,
|
||||||
|
) {
|
||||||
|
// Properties
|
||||||
|
var isChecked by remember { mutableStateOf(purposeUI.isAccepted != PurposeStatusUI.REJECTED) }
|
||||||
|
|
||||||
|
// View
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = purposeUI.title,
|
||||||
|
style = ConsentiumUITheme.typography.b3,
|
||||||
|
color = ConsentiumUITheme.colors.onSurface,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
|
if (purposeUI.isRequired) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.require),
|
||||||
|
style = ConsentiumUITheme.typography.b3,
|
||||||
|
color = ConsentiumUITheme.colors.onSurface,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ConsentiumUISwitch(
|
||||||
|
checked = isChecked,
|
||||||
|
onCheckedChange = {
|
||||||
|
isChecked = !isChecked
|
||||||
|
purposeUI.isAccepted = if (isChecked) PurposeStatusUI.ACCEPTED else PurposeStatusUI.REJECTED
|
||||||
|
purposeUI.vendors.forEach { vendorUI ->
|
||||||
|
vendorUI.isAccepted = isChecked
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** To enable in v2
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = ConsentiumUITheme.colors.onSurface,
|
||||||
|
)
|
||||||
|
**/
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = htmlToAnnotatedString(purposeUI.description),
|
||||||
|
style = ConsentiumUITheme.typography.p3,
|
||||||
|
color = ConsentiumUITheme.colors.onSurface,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun PurposeComponentPreview() {
|
||||||
|
PurposeComponent(
|
||||||
|
purposeUI = PurposeUI(
|
||||||
|
id = "1",
|
||||||
|
isRequired = true,
|
||||||
|
isAccepted = PurposeStatusUI.ACCEPTED,
|
||||||
|
title = "Title",
|
||||||
|
description = "Description",
|
||||||
|
vendors = emptyList(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components.core
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun TextLink(
|
||||||
|
text: String,
|
||||||
|
onclick: () -> Unit,
|
||||||
|
color: Color = ConsentiumUITheme.colors.primary,
|
||||||
|
isLoading: Boolean = false,
|
||||||
|
) {
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable(
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
indication = null,
|
||||||
|
onClick = onclick,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
color = color,
|
||||||
|
style = ConsentiumUITheme.typography.b3,
|
||||||
|
textDecoration = TextDecoration.Underline
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
|
||||||
|
CircularProgressIndicator(
|
||||||
|
color = color,
|
||||||
|
modifier = Modifier.size(14.dp),
|
||||||
|
strokeWidth = 3.dp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun TextLinkPreview() {
|
||||||
|
TextLink(
|
||||||
|
text = "Continuer sans accepter",
|
||||||
|
onclick = {},
|
||||||
|
isLoading = true,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components.core.button
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ConsentButton(
|
||||||
|
text: String,
|
||||||
|
buttonStyle: ConsentiumUIButtonStyle,
|
||||||
|
onclick: () -> Unit,
|
||||||
|
isLoading: Boolean = false,
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = onclick,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(40.dp),
|
||||||
|
shape = RoundedCornerShape(6.dp),
|
||||||
|
colors = ButtonDefaults.buttonColors(buttonStyle.backgroundColor())
|
||||||
|
) {
|
||||||
|
if (isLoading) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
color = ConsentiumUITheme.colors.onPrimary,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
color = ConsentiumUITheme.colors.onPrimary,
|
||||||
|
style = ConsentiumUITheme.typography.button,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun ConsentButtonPreview() {
|
||||||
|
ConsentButton(
|
||||||
|
text = "Accept and close",
|
||||||
|
buttonStyle = ConsentiumUIButtonStyle.PRIMARY,
|
||||||
|
onclick = {}
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components.core.button
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
||||||
|
|
||||||
|
internal enum class ConsentiumUIButtonStyle {
|
||||||
|
PRIMARY,
|
||||||
|
SECONDARY,
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ConsentiumUIButtonStyle.backgroundColor(): Color {
|
||||||
|
return when (this) {
|
||||||
|
ConsentiumUIButtonStyle.PRIMARY -> ConsentiumUITheme.colors.primary
|
||||||
|
ConsentiumUIButtonStyle.SECONDARY -> ConsentiumUITheme.colors.secondary
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components.core.toggle
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
|
import androidx.compose.material3.SwitchDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ConsentiumUISwitch(
|
||||||
|
checked: Boolean,
|
||||||
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
|
) {
|
||||||
|
Switch(
|
||||||
|
checked = checked,
|
||||||
|
onCheckedChange = onCheckedChange,
|
||||||
|
colors = SwitchDefaults.colors(
|
||||||
|
checkedTrackColor = ConsentiumUITheme.colors.secondary,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun ConsentiumUISwitchPreview() {
|
||||||
|
Column {
|
||||||
|
ConsentiumUISwitch(
|
||||||
|
checked = true,
|
||||||
|
onCheckedChange = {},
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
ConsentiumUISwitch(
|
||||||
|
checked = false,
|
||||||
|
onCheckedChange = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,181 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components.style
|
||||||
|
|
||||||
|
import androidx.compose.material3.LocalTextStyle
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.core.graphics.ColorUtils
|
||||||
|
|
||||||
|
|
||||||
|
object ConsentiumDefaults {
|
||||||
|
private const val OUTLINE_COLOR_BLEND_RATIO = 0.1f
|
||||||
|
|
||||||
|
private object Typography {
|
||||||
|
private val DEFAULT_LINE_HEIGHT = 24.sp
|
||||||
|
private val LINE_HEIGHT_18 = 18.sp
|
||||||
|
private val LINE_HEIGHT_22 = 22.sp
|
||||||
|
private val DEFAULT_LETTER_SPACING = 0.5.sp
|
||||||
|
|
||||||
|
val h1 = TextStyle(
|
||||||
|
fontSize = 32.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
letterSpacing = DEFAULT_LETTER_SPACING
|
||||||
|
)
|
||||||
|
|
||||||
|
val h2 = TextStyle(
|
||||||
|
fontSize = 24.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
letterSpacing = DEFAULT_LETTER_SPACING
|
||||||
|
)
|
||||||
|
|
||||||
|
val b1 = TextStyle(
|
||||||
|
fontSize = 18.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
lineHeight = DEFAULT_LINE_HEIGHT,
|
||||||
|
letterSpacing = DEFAULT_LETTER_SPACING
|
||||||
|
)
|
||||||
|
|
||||||
|
val p1 = TextStyle(
|
||||||
|
fontSize = 18.sp,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
lineHeight = DEFAULT_LINE_HEIGHT,
|
||||||
|
letterSpacing = DEFAULT_LETTER_SPACING
|
||||||
|
)
|
||||||
|
|
||||||
|
val b2 = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
lineHeight = LINE_HEIGHT_22,
|
||||||
|
letterSpacing = DEFAULT_LETTER_SPACING
|
||||||
|
)
|
||||||
|
|
||||||
|
val p2 = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
lineHeight = LINE_HEIGHT_22,
|
||||||
|
letterSpacing = DEFAULT_LETTER_SPACING
|
||||||
|
)
|
||||||
|
|
||||||
|
val b3 = TextStyle(
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
lineHeight = LINE_HEIGHT_18,
|
||||||
|
letterSpacing = DEFAULT_LETTER_SPACING
|
||||||
|
)
|
||||||
|
|
||||||
|
val p3 = TextStyle(
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
lineHeight = LINE_HEIGHT_18,
|
||||||
|
letterSpacing = DEFAULT_LETTER_SPACING
|
||||||
|
)
|
||||||
|
|
||||||
|
val button = TextStyle(
|
||||||
|
fontSize = 18.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
letterSpacing = DEFAULT_LETTER_SPACING
|
||||||
|
)
|
||||||
|
|
||||||
|
val sm2 = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
lineHeight = LINE_HEIGHT_22,
|
||||||
|
letterSpacing = DEFAULT_LETTER_SPACING
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun colors(
|
||||||
|
primary: Color = MaterialTheme.colorScheme.primary,
|
||||||
|
onPrimary: Color = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
secondary: Color = MaterialTheme.colorScheme.background,
|
||||||
|
onSecondary: Color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
tertiary: Color = MaterialTheme.colorScheme.onBackground,
|
||||||
|
onSurfaceVariant: Color = MaterialTheme.colorScheme.onBackground,
|
||||||
|
onSurface: Color = MaterialTheme.colorScheme.onBackground,
|
||||||
|
outline: Color = Color(
|
||||||
|
ColorUtils.blendARGB(
|
||||||
|
onPrimary.toArgb(),
|
||||||
|
primary.toArgb(),
|
||||||
|
OUTLINE_COLOR_BLEND_RATIO,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
error: Color = MaterialTheme.colorScheme.error,
|
||||||
|
surfaceHighest: Color = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||||
|
surfaceHigh: Color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
|
surfaceMiddle: Color = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
|
success: Color = Color(0xFF479B3F),
|
||||||
|
): ConsentiumColors = ConsentiumColors(
|
||||||
|
primary = primary,
|
||||||
|
onPrimary = onPrimary,
|
||||||
|
secondary = secondary,
|
||||||
|
onSecondary = onSecondary,
|
||||||
|
tertiary = tertiary,
|
||||||
|
onSurfaceVariant = onSurfaceVariant,
|
||||||
|
onSurface = onSurface,
|
||||||
|
outline = outline,
|
||||||
|
error = error,
|
||||||
|
surfaceHighest = surfaceHighest,
|
||||||
|
surfaceHigh = surfaceHigh,
|
||||||
|
surfaceMiddle = surfaceMiddle,
|
||||||
|
success = success,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun typography(
|
||||||
|
h1: TextStyle = LocalTextStyle.current.merge(Typography.h1),
|
||||||
|
h2: TextStyle = LocalTextStyle.current.merge(Typography.h2),
|
||||||
|
b1: TextStyle = LocalTextStyle.current.merge(Typography.b1),
|
||||||
|
p1: TextStyle = LocalTextStyle.current.merge(Typography.p1),
|
||||||
|
b2: TextStyle = LocalTextStyle.current.merge(Typography.b2),
|
||||||
|
p2: TextStyle = LocalTextStyle.current.merge(Typography.p2),
|
||||||
|
b3: TextStyle = LocalTextStyle.current.merge(Typography.b3),
|
||||||
|
p3: TextStyle = LocalTextStyle.current.merge(Typography.p3),
|
||||||
|
button: TextStyle = LocalTextStyle.current.merge(Typography.button),
|
||||||
|
sm2: TextStyle = LocalTextStyle.current.merge(Typography.sm2),
|
||||||
|
): ConsentiumTypography = ConsentiumTypography(
|
||||||
|
h1 = h1,
|
||||||
|
h2 = h2,
|
||||||
|
b1 = b1,
|
||||||
|
p1 = p1,
|
||||||
|
b2 = b2,
|
||||||
|
p2 = p2,
|
||||||
|
b3 = b3,
|
||||||
|
p3 = p3,
|
||||||
|
button = button,
|
||||||
|
sm2 = sm2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ConsentiumColors internal constructor(
|
||||||
|
val primary: Color,
|
||||||
|
val onPrimary: Color,
|
||||||
|
val secondary: Color,
|
||||||
|
val onSecondary: Color,
|
||||||
|
val tertiary: Color,
|
||||||
|
val onSurfaceVariant: Color,
|
||||||
|
val onSurface: Color,
|
||||||
|
val outline: Color,
|
||||||
|
val error: Color,
|
||||||
|
val surfaceHighest: Color,
|
||||||
|
val surfaceHigh: Color,
|
||||||
|
val surfaceMiddle: Color,
|
||||||
|
val success: Color,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class ConsentiumTypography internal constructor(
|
||||||
|
val h1: TextStyle,
|
||||||
|
val h2: TextStyle,
|
||||||
|
val b1: TextStyle,
|
||||||
|
val p1: TextStyle,
|
||||||
|
val b2: TextStyle,
|
||||||
|
val p2: TextStyle,
|
||||||
|
val b3: TextStyle,
|
||||||
|
val p3: TextStyle,
|
||||||
|
val button: TextStyle,
|
||||||
|
val sm2: TextStyle,
|
||||||
|
)
|
@ -0,0 +1,52 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components.style
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
|
||||||
|
internal val LocalColors = staticCompositionLocalOf {
|
||||||
|
ConsentiumColors(
|
||||||
|
primary = Color.Unspecified,
|
||||||
|
onPrimary = Color.Unspecified,
|
||||||
|
secondary = Color.Unspecified,
|
||||||
|
onSecondary = Color.Unspecified,
|
||||||
|
tertiary = Color.Unspecified,
|
||||||
|
onSurfaceVariant = Color.Unspecified,
|
||||||
|
onSurface = Color.Unspecified,
|
||||||
|
outline = Color.Unspecified,
|
||||||
|
error = Color.Unspecified,
|
||||||
|
surfaceHighest = Color.Unspecified,
|
||||||
|
surfaceHigh = Color.Unspecified,
|
||||||
|
surfaceMiddle = Color.Unspecified,
|
||||||
|
success = Color.Unspecified,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val LocalTypography = staticCompositionLocalOf {
|
||||||
|
ConsentiumTypography(
|
||||||
|
h1 = TextStyle.Default,
|
||||||
|
h2 = TextStyle.Default,
|
||||||
|
b1 = TextStyle.Default,
|
||||||
|
p1 = TextStyle.Default,
|
||||||
|
b2 = TextStyle.Default,
|
||||||
|
p2 = TextStyle.Default,
|
||||||
|
b3 = TextStyle.Default,
|
||||||
|
p3 = TextStyle.Default,
|
||||||
|
button = TextStyle.Default,
|
||||||
|
sm2 = TextStyle.Default,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object ConsentiumUITheme {
|
||||||
|
val colors: ConsentiumColors
|
||||||
|
@Composable
|
||||||
|
@ReadOnlyComposable
|
||||||
|
get() = LocalColors.current
|
||||||
|
|
||||||
|
val typography: ConsentiumTypography
|
||||||
|
@Composable
|
||||||
|
@ReadOnlyComposable
|
||||||
|
get() = LocalTypography.current
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.model
|
||||||
|
|
||||||
|
enum class ConsentiumPageUI {
|
||||||
|
GENERAL_CONSENT,
|
||||||
|
DETAILS_CONSENT
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.model
|
||||||
|
|
||||||
|
internal data class DetailConsentUI(
|
||||||
|
val conservationDurationText: String,
|
||||||
|
val purposes: List<PurposeUI>,
|
||||||
|
)
|
@ -0,0 +1,8 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.model
|
||||||
|
|
||||||
|
internal data class GeneralConsentUI(
|
||||||
|
val applicationName: String,
|
||||||
|
val iconUrl: String,
|
||||||
|
val mainConsentText: String,
|
||||||
|
val consentPageUrl: String,
|
||||||
|
)
|
@ -0,0 +1,7 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.model
|
||||||
|
|
||||||
|
internal enum class LoadingElement {
|
||||||
|
BUTTON,
|
||||||
|
LINK,
|
||||||
|
NONE,
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.model
|
||||||
|
|
||||||
|
internal enum class PurposeStatusUI {
|
||||||
|
ACCEPTED,
|
||||||
|
REJECTED,
|
||||||
|
NOT_DEFINED,
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.model
|
||||||
|
|
||||||
|
internal data class PurposeUI(
|
||||||
|
val id: String,
|
||||||
|
val isRequired: Boolean,
|
||||||
|
var isAccepted: PurposeStatusUI,
|
||||||
|
val title: String,
|
||||||
|
val description: String,
|
||||||
|
val vendors: List<VendorUI>,
|
||||||
|
)
|
@ -0,0 +1,6 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.model
|
||||||
|
|
||||||
|
internal data class VendorUI(
|
||||||
|
val id: String,
|
||||||
|
var isAccepted: Boolean,
|
||||||
|
)
|
@ -0,0 +1,17 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.state
|
||||||
|
|
||||||
|
import fr.openium.consentium_ui.ui.model.DetailConsentUI
|
||||||
|
import fr.openium.consentium_ui.ui.model.GeneralConsentUI
|
||||||
|
|
||||||
|
internal sealed interface ConsentiumUIState {
|
||||||
|
|
||||||
|
data object Loading : ConsentiumUIState
|
||||||
|
|
||||||
|
data class Loaded(
|
||||||
|
val generalConsentUI: GeneralConsentUI,
|
||||||
|
val detailConsentUI: DetailConsentUI,
|
||||||
|
) : ConsentiumUIState
|
||||||
|
|
||||||
|
data class Error(val message: String) : ConsentiumUIState
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.utils
|
||||||
|
|
||||||
|
import android.text.ParcelableSpan
|
||||||
|
import android.text.Spanned
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
import androidx.compose.ui.text.withStyle
|
||||||
|
import androidx.core.text.HtmlCompat
|
||||||
|
|
||||||
|
fun htmlToAnnotatedString(html: String): AnnotatedString {
|
||||||
|
val spanned: Spanned = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||||
|
return buildAnnotatedString {
|
||||||
|
val text = spanned.toString()
|
||||||
|
var start = 0
|
||||||
|
spanned.getSpans(0, spanned.length, Any::class.java).map { it as ParcelableSpan }.forEach { span ->
|
||||||
|
val startSpan = spanned.getSpanStart(span)
|
||||||
|
val endSpan = spanned.getSpanEnd(span)
|
||||||
|
|
||||||
|
// Ajoutez le texte précédent sans style
|
||||||
|
if (start < startSpan) {
|
||||||
|
append(text.substring(start, startSpan))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ajoutez le texte stylé
|
||||||
|
withStyle(style = span.toSpanStyle()) {
|
||||||
|
append(text.substring(startSpan, endSpan))
|
||||||
|
}
|
||||||
|
start = endSpan
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ajoutez le texte restant sans style
|
||||||
|
if (start < text.length) {
|
||||||
|
append(text.substring(start, text.length).trimEnd())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ParcelableSpan.toSpanStyle(): SpanStyle {
|
||||||
|
return when (this) {
|
||||||
|
is android.text.style.StyleSpan -> {
|
||||||
|
when (style) {
|
||||||
|
android.graphics.Typeface.BOLD -> SpanStyle(fontWeight = androidx.compose.ui.text.font.FontWeight.Bold)
|
||||||
|
android.graphics.Typeface.ITALIC -> SpanStyle(fontStyle = androidx.compose.ui.text.font.FontStyle.Italic)
|
||||||
|
else -> SpanStyle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is android.text.style.UnderlineSpan -> SpanStyle(textDecoration = TextDecoration.Underline)
|
||||||
|
is android.text.style.StrikethroughSpan -> SpanStyle(textDecoration = TextDecoration.LineThrough)
|
||||||
|
else -> SpanStyle()
|
||||||
|
}
|
||||||
|
}
|
10
consentium-ui/src/main/res/values/strings.xml
Normal file
10
consentium-ui/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="cookies">Politique de cookies</string>
|
||||||
|
<string name="accept">Accepter et fermer</string>
|
||||||
|
<string name="refuse">Continuer sans accepter</string>
|
||||||
|
<string name="parameters">Paramétrer mes choix</string>
|
||||||
|
<string name="save">Enregistrer et fermer</string>
|
||||||
|
<string name="require">Requis</string>
|
||||||
|
<string name="save_consents_error_message">Une erreur est survenue l\'or de la ssauvegarde de vos consentements.</string>
|
||||||
|
</resources>
|
@ -3,10 +3,6 @@ package fr.openium.consentium.api
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dagger.hilt.android.EntryPointAccessors
|
import dagger.hilt.android.EntryPointAccessors
|
||||||
import fr.openium.consentium.api.model.PurposeChoice
|
import fr.openium.consentium.api.model.PurposeChoice
|
||||||
import fr.openium.consentium.api.model.PurposeIdentifier
|
|
||||||
import fr.openium.consentium.api.model.PurposeStatus
|
|
||||||
import fr.openium.consentium.api.model.VendorIdentifier
|
|
||||||
import fr.openium.consentium.api.model.VendorStatus
|
|
||||||
import fr.openium.consentium.api.state.FetchConsentiumState
|
import fr.openium.consentium.api.state.FetchConsentiumState
|
||||||
import fr.openium.consentium.api.state.SetConsentiumState
|
import fr.openium.consentium.api.state.SetConsentiumState
|
||||||
import fr.openium.consentium.domain.di.RepositoryEntryPoint
|
import fr.openium.consentium.domain.di.RepositoryEntryPoint
|
||||||
@ -23,8 +19,8 @@ class Consentium(
|
|||||||
private val _fetchConsentState = MutableStateFlow<FetchConsentiumState>(FetchConsentiumState.Idle)
|
private val _fetchConsentState = MutableStateFlow<FetchConsentiumState>(FetchConsentiumState.Idle)
|
||||||
val fetchConsentState by lazy { _fetchConsentState.asStateFlow() }
|
val fetchConsentState by lazy { _fetchConsentState.asStateFlow() }
|
||||||
|
|
||||||
private val _setConsentState = MutableStateFlow<SetConsentiumState>(SetConsentiumState.Idle)
|
private val _saveConsentState = MutableStateFlow<SetConsentiumState>(SetConsentiumState.Idle)
|
||||||
val setConsentState by lazy { _setConsentState.asStateFlow() }
|
val saveConsentState by lazy { _saveConsentState.asStateFlow() }
|
||||||
|
|
||||||
private val consentiumRepository: ConsentiumRepository by lazy {
|
private val consentiumRepository: ConsentiumRepository by lazy {
|
||||||
val appContext = context.applicationContext
|
val appContext = context.applicationContext
|
||||||
@ -32,34 +28,6 @@ class Consentium(
|
|||||||
entryPoint.provideConsentiumRepository()
|
entryPoint.provideConsentiumRepository()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getConsentStatus(purpose: PurposeIdentifier): PurposeStatus {
|
|
||||||
return when (val state = fetchConsentState.value) {
|
|
||||||
is FetchConsentiumState.Valid -> {
|
|
||||||
val purposeChoice = state.purposes.find { it.identifier == purpose }
|
|
||||||
purposeChoice?.isAccepted ?: PurposeStatus.UNKNOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> PurposeStatus.UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getConsentStatus(purpose: PurposeIdentifier, vendorIdentifier: VendorIdentifier): VendorStatus {
|
|
||||||
return when (val state = fetchConsentState.value) {
|
|
||||||
is FetchConsentiumState.Valid -> {
|
|
||||||
val vendorChoice = state.purposes.find { it.identifier == purpose }
|
|
||||||
?.vendors?.find { it.identifier == vendorIdentifier }
|
|
||||||
|
|
||||||
when (vendorChoice?.isAccepted) {
|
|
||||||
true -> VendorStatus.ACCEPTED
|
|
||||||
false -> VendorStatus.REJECTED
|
|
||||||
else -> VendorStatus.UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> VendorStatus.UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun fetchConsents() {
|
suspend fun fetchConsents() {
|
||||||
_fetchConsentState.value = FetchConsentiumState.Loading
|
_fetchConsentState.value = FetchConsentiumState.Loading
|
||||||
try {
|
try {
|
||||||
@ -75,24 +43,27 @@ class Consentium(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
_fetchConsentState.value = FetchConsentiumState.Error
|
_fetchConsentState.value = FetchConsentiumState.Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun setConsents(
|
suspend fun saveConsents(
|
||||||
consent: List<PurposeChoice>,
|
consent: List<PurposeChoice>,
|
||||||
) {
|
) {
|
||||||
_setConsentState.value = SetConsentiumState.Loading
|
_saveConsentState.emit(SetConsentiumState.Loading)
|
||||||
try {
|
try {
|
||||||
when (consentiumRepository.setConsents(applicationId, consent)) {
|
when (consentiumRepository.setConsents(applicationId, consent)) {
|
||||||
ConsentiumRepositorySetResponse.Error -> _setConsentState.value = SetConsentiumState.Error
|
ConsentiumRepositorySetResponse.Error -> {
|
||||||
ConsentiumRepositorySetResponse.SetConsentsSuccess -> _setConsentState.value = SetConsentiumState.Success
|
_saveConsentState.emit(SetConsentiumState.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConsentiumRepositorySetResponse.SetConsentsSuccess -> {
|
||||||
|
_saveConsentState.emit(SetConsentiumState.Success)
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
_setConsentState.value = SetConsentiumState.Error
|
_saveConsentState.emit(SetConsentiumState.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,10 @@
|
|||||||
package fr.openium.consentium.api.adapter
|
package fr.openium.consentium.api.adapter
|
||||||
|
|
||||||
import fr.openium.consentium.api.model.Purpose
|
import fr.openium.consentium.api.model.Purpose
|
||||||
import fr.openium.consentium.api.model.PurposeIdentifier
|
|
||||||
import fr.openium.consentium.data.remote.model.GetConsent
|
import fr.openium.consentium.data.remote.model.GetConsent
|
||||||
|
|
||||||
internal fun GetConsent.PurposeDTO.toPurpose() = Purpose(
|
internal fun GetConsent.PurposeDTO.toPurpose() = Purpose(
|
||||||
identifier = PurposeIdentifier.fromString(identifier),
|
identifier = identifier,
|
||||||
isRequired = isRequired,
|
isRequired = isRequired,
|
||||||
isAccepted = isAccepted.toPurposeStatus(),
|
isAccepted = isAccepted.toPurposeStatus(),
|
||||||
vendors = vendors?.map { it.toVendor() } ?: emptyList(),
|
vendors = vendors?.map { it.toVendor() } ?: emptyList(),
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package fr.openium.consentium.api.adapter
|
package fr.openium.consentium.api.adapter
|
||||||
|
|
||||||
import fr.openium.consentium.api.model.Vendor
|
import fr.openium.consentium.api.model.Vendor
|
||||||
import fr.openium.consentium.api.model.VendorIdentifier
|
|
||||||
import fr.openium.consentium.data.remote.model.GetConsent
|
import fr.openium.consentium.data.remote.model.GetConsent
|
||||||
|
|
||||||
internal fun GetConsent.VendorDTO.toVendor() = Vendor(
|
internal fun GetConsent.VendorDTO.toVendor() = Vendor(
|
||||||
identifier = VendorIdentifier.fromString(identifier),
|
identifier = identifier,
|
||||||
isAccepted = isAccepted,
|
isAccepted = isAccepted,
|
||||||
isRequired = isRequired,
|
isRequired = isRequired,
|
||||||
)
|
)
|
@ -1,7 +1,7 @@
|
|||||||
package fr.openium.consentium.api.model
|
package fr.openium.consentium.api.model
|
||||||
|
|
||||||
data class Purpose(
|
data class Purpose(
|
||||||
val identifier: PurposeIdentifier,
|
val identifier: String,
|
||||||
val isRequired: Boolean,
|
val isRequired: Boolean,
|
||||||
val isAccepted: PurposeStatus,
|
val isAccepted: PurposeStatus,
|
||||||
val vendors: List<Vendor>,
|
val vendors: List<Vendor>,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package fr.openium.consentium.api.model
|
package fr.openium.consentium.api.model
|
||||||
|
|
||||||
data class PurposeChoice(
|
data class PurposeChoice(
|
||||||
val purposeIdentifier: PurposeIdentifier,
|
val purposeIdentifier: String,
|
||||||
val isAccepted: Boolean,
|
val isAccepted: Boolean,
|
||||||
val vendors: List<VendorChoice>,
|
val vendors: List<VendorChoice>,
|
||||||
)
|
)
|
@ -1,24 +0,0 @@
|
|||||||
package fr.openium.consentium.api.model
|
|
||||||
|
|
||||||
enum class PurposeIdentifier {
|
|
||||||
REQUIRED,
|
|
||||||
AUDIENCE,
|
|
||||||
ADS,
|
|
||||||
UNKNOWN;
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromString(string: String): PurposeIdentifier = when (string) {
|
|
||||||
"purpose-required" -> REQUIRED
|
|
||||||
"purpose-audience" -> AUDIENCE
|
|
||||||
"purpose-ads" -> ADS
|
|
||||||
else -> UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = when (this) {
|
|
||||||
REQUIRED -> "purpose-required"
|
|
||||||
AUDIENCE -> "purpose-audience"
|
|
||||||
ADS -> "purpose-ads"
|
|
||||||
else -> "purpose-unknown"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
package fr.openium.consentium.api.model
|
package fr.openium.consentium.api.model
|
||||||
|
|
||||||
data class Vendor(
|
data class Vendor(
|
||||||
val identifier: VendorIdentifier,
|
val identifier: String,
|
||||||
val isAccepted: Boolean,
|
val isAccepted: Boolean,
|
||||||
val isRequired: Boolean,
|
val isRequired: Boolean,
|
||||||
)
|
)
|
@ -1,6 +1,6 @@
|
|||||||
package fr.openium.consentium.api.model
|
package fr.openium.consentium.api.model
|
||||||
|
|
||||||
data class VendorChoice(
|
data class VendorChoice(
|
||||||
val vendorIdentifier: VendorIdentifier,
|
val vendorIdentifier: String,
|
||||||
val isAccepted: Boolean,
|
val isAccepted: Boolean,
|
||||||
)
|
)
|
@ -1,27 +0,0 @@
|
|||||||
package fr.openium.consentium.api.model
|
|
||||||
|
|
||||||
enum class VendorIdentifier {
|
|
||||||
CRASHLYTICS,
|
|
||||||
MATOMO,
|
|
||||||
GA4,
|
|
||||||
CLARITY,
|
|
||||||
UNKNOWN;
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromString(string: String): VendorIdentifier = when (string) {
|
|
||||||
"vendor-clarity" -> CLARITY
|
|
||||||
"vendor-crashlytics" -> CRASHLYTICS
|
|
||||||
"vendor-matomo" -> MATOMO
|
|
||||||
"vendor-ga4" -> GA4
|
|
||||||
else -> UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = when (this) {
|
|
||||||
CLARITY -> "vendor-clarity"
|
|
||||||
CRASHLYTICS -> "vendor-crashlytics"
|
|
||||||
MATOMO -> "vendor-matomo"
|
|
||||||
GA4 -> "vendor-ga4"
|
|
||||||
else -> "vendor-unknown"
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,7 +8,7 @@ import dagger.Reusable
|
|||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import fr.openium.consentium.BuildConfig
|
import fr.openium.consentium.BuildConfig
|
||||||
import fr.openium.consentium.api.mock.ConsentiumMockApi
|
import fr.openium.consentium.data.remote.mock.ConsentiumMockApi
|
||||||
import fr.openium.consentium.data.remote.ConsentiumApi
|
import fr.openium.consentium.data.remote.ConsentiumApi
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.Cache
|
import okhttp3.Cache
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package fr.openium.consentium.api.mock
|
package fr.openium.consentium.data.remote.mock
|
||||||
|
|
||||||
import fr.openium.consentium.data.remote.ConsentiumApi
|
import fr.openium.consentium.data.remote.ConsentiumApi
|
||||||
import fr.openium.consentium.data.remote.model.GetConsent
|
import fr.openium.consentium.data.remote.model.GetConsent
|
||||||
@ -36,16 +36,16 @@ internal object ConsentiumMockApi : ConsentiumApi {
|
|||||||
vendors = null
|
vendors = null
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
isValid = true
|
isValid = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun getConsents(applicationId: String, installationId: String): Response<GetConsent.GetConsentPayloadDTO> {
|
override suspend fun getConsents(applicationId: String, installationId: String): Response<GetConsent.GetConsentPayloadDTO> {
|
||||||
delay(500)
|
delay(2000)
|
||||||
return Response.success(consents)
|
return Response.success(consents)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setConsents(applicationId: String, patchConsent: PatchConsent.PatchConsentPayloadDTO): Response<Any> {
|
override suspend fun setConsents(applicationId: String, patchConsent: PatchConsent.PatchConsentPayloadDTO): Response<Any> {
|
||||||
delay(500)
|
delay(2000)
|
||||||
return Response.success(Unit)
|
return Response.success(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,8 +65,18 @@ firebaseCrashlyticsKtx = "19.2.0"
|
|||||||
# Clarity
|
# Clarity
|
||||||
clarityVersion = "1.3.2"
|
clarityVersion = "1.3.2"
|
||||||
|
|
||||||
|
# Coil
|
||||||
|
coil = "3.0.4"
|
||||||
|
|
||||||
|
|
||||||
# GA4
|
# GA4
|
||||||
ga4 = "22.1.2"
|
ga4 = "22.1.2"
|
||||||
|
runtimeAndroid = "1.7.6"
|
||||||
|
foundationAndroid = "1.7.6"
|
||||||
|
foundationLayoutAndroid = "1.7.5"
|
||||||
|
uiAndroid = "1.7.5"
|
||||||
|
uiToolingPreviewAndroid = "1.7.6"
|
||||||
|
material3Android = "1.3.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|
||||||
@ -94,9 +104,11 @@ compose-ui = { module = "androidx.compose.ui:ui" }
|
|||||||
compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" }
|
compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" }
|
||||||
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||||
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
|
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||||
|
|
||||||
# Material
|
# Material
|
||||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||||
|
androidx-material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3Android" }
|
||||||
|
|
||||||
# Kotlin serizalization
|
# Kotlin serizalization
|
||||||
kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "jsonSerialization" }
|
kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "jsonSerialization" }
|
||||||
@ -132,6 +144,15 @@ clarity = { group = "com.microsoft.clarity", name = "clarity", version.ref = "cl
|
|||||||
|
|
||||||
# GA4 (Firebase Analytics)
|
# GA4 (Firebase Analytics)
|
||||||
ga4 = { module = "com.google.firebase:firebase-analytics", version.ref = "ga4" }
|
ga4 = { module = "com.google.firebase:firebase-analytics", version.ref = "ga4" }
|
||||||
|
androidx-runtime-android = { group = "androidx.compose.runtime", name = "runtime-android", version.ref = "runtimeAndroid" }
|
||||||
|
androidx-foundation-android = { group = "androidx.compose.foundation", name = "foundation-android", version.ref = "foundationAndroid" }
|
||||||
|
androidx-foundation-layout-android = { group = "androidx.compose.foundation", name = "foundation-layout-android", version.ref = "foundationLayoutAndroid" }
|
||||||
|
androidx-ui-android = { group = "androidx.compose.ui", name = "ui-android", version.ref = "uiAndroid" }
|
||||||
|
androidx-ui-tooling-preview-android = { group = "androidx.compose.ui", name = "ui-tooling-preview-android", version.ref = "uiToolingPreviewAndroid" }
|
||||||
|
|
||||||
|
# Coil
|
||||||
|
coil = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||||
|
coil-network = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user