Compare commits
20 Commits
86a7020c2b
...
develop/v1
Author | SHA1 | Date | |
---|---|---|---|
7a4692f062 | |||
b03c37352a | |||
0f4a3254e4 | |||
3ffd190406 | |||
76d021d35d | |||
ed2d0ddb43 | |||
4276adaf22 | |||
ad08a4325f | |||
c94f8a4f05 | |||
9f9a66451f | |||
af91841857 | |||
c57c9b7d05 | |||
5303e594da | |||
18f9fff4a1 | |||
cc578e3117 | |||
75d179e046 | |||
63cfc39d1e | |||
bf5f7b47b7 | |||
338d9f624a | |||
1f40d2fc98 |
15
Jenkinsfile
vendored
15
Jenkinsfile
vendored
@ -1,3 +1,16 @@
|
|||||||
library "openiumpipeline"
|
library "openiumpipeline"
|
||||||
|
|
||||||
openiumDroidJob()
|
openiumDroidJob modules: [
|
||||||
|
"consentium": [
|
||||||
|
unitTestTasks: ["testDevDebugUnitTest"],
|
||||||
|
],
|
||||||
|
"consentium-ui": [
|
||||||
|
unitTestTasks: ["testDevDebugUnitTest"],
|
||||||
|
],
|
||||||
|
"app": [
|
||||||
|
unitTestTasks: ["testDevDebugUnitTest"],
|
||||||
|
testTasks: ["pixel5DevDebugAndroidTest"],
|
||||||
|
publishApkVariants : ["devDebug", "devRelease", "demoRelease", "prodRelease"],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
publishChannel: '#int-consentium'
|
@ -1,3 +1,4 @@
|
|||||||
|
import com.android.build.api.dsl.ManagedVirtualDevice
|
||||||
import org.gradle.language.nativeplatform.internal.BuildType
|
import org.gradle.language.nativeplatform.internal.BuildType
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
@ -9,7 +10,9 @@ plugins {
|
|||||||
alias(libs.plugins.hilt)
|
alias(libs.plugins.hilt)
|
||||||
alias(libs.plugins.serialization)
|
alias(libs.plugins.serialization)
|
||||||
alias(libs.plugins.kotlin.compose)
|
alias(libs.plugins.kotlin.compose)
|
||||||
|
id("fr.openium.publish")
|
||||||
}
|
}
|
||||||
|
apply(from = "publish.build.gradle")
|
||||||
|
|
||||||
// Keystore
|
// Keystore
|
||||||
val keystorePropertiesFile = rootProject.file("keys/keystore.properties")
|
val keystorePropertiesFile = rootProject.file("keys/keystore.properties")
|
||||||
@ -24,8 +27,6 @@ android {
|
|||||||
applicationId = "fr.openium.consentium"
|
applicationId = "fr.openium.consentium"
|
||||||
minSdk = libs.versions.minSdk.get().toInt()
|
minSdk = libs.versions.minSdk.get().toInt()
|
||||||
targetSdk = libs.versions.targetSdk.get().toInt()
|
targetSdk = libs.versions.targetSdk.get().toInt()
|
||||||
versionCode = 1
|
|
||||||
versionName = "1.0"
|
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
@ -48,6 +49,7 @@ android {
|
|||||||
buildTypes {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
|
isShrinkResources = false
|
||||||
|
|
||||||
versionNameSuffix = "-debug"
|
versionNameSuffix = "-debug"
|
||||||
applicationIdSuffix = ".debug"
|
applicationIdSuffix = ".debug"
|
||||||
@ -72,6 +74,10 @@ android {
|
|||||||
dimension = "version"
|
dimension = "version"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create("dev") {
|
||||||
|
dimension = "version"
|
||||||
|
}
|
||||||
|
|
||||||
create("demo") {
|
create("demo") {
|
||||||
dimension = "version"
|
dimension = "version"
|
||||||
}
|
}
|
||||||
@ -87,6 +93,19 @@ android {
|
|||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
animationsDisabled = true
|
||||||
|
managedDevices {
|
||||||
|
devices {
|
||||||
|
create<ManagedVirtualDevice>("pixel5") {
|
||||||
|
device = "Pixel 5"
|
||||||
|
apiLevel = 34
|
||||||
|
systemImageSource = "google"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -129,5 +148,4 @@ dependencies {
|
|||||||
|
|
||||||
// Kotlin serialization
|
// Kotlin serialization
|
||||||
implementation(libs.kotlin.serialization)
|
implementation(libs.kotlin.serialization)
|
||||||
|
|
||||||
}
|
}
|
6
app/publish.build.gradle
Normal file
6
app/publish.build.gradle
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
android {
|
||||||
|
defaultConfig {
|
||||||
|
versionName publish.versionName
|
||||||
|
versionCode publish.versionCode
|
||||||
|
}
|
||||||
|
}
|
@ -17,8 +17,6 @@ import org.junit.Assert.*
|
|||||||
class ExampleInstrumentedTest {
|
class ExampleInstrumentedTest {
|
||||||
@Test
|
@Test
|
||||||
fun useAppContext() {
|
fun useAppContext() {
|
||||||
// Context of the app under test.
|
assertTrue(true)
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
|
||||||
assertEquals("fr.openium.consentium", appContext.packageName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,56 +1,82 @@
|
|||||||
|
import androidx.compose.animation.EnterTransition
|
||||||
|
import androidx.compose.animation.ExitTransition
|
||||||
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.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
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 androidx.navigation.toRoute
|
||||||
|
import fr.openium.consentium.R
|
||||||
import fr.openium.consentium.api.Consentium
|
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.ConsentiumComponent
|
||||||
import fr.openium.consentium_ui.ui.components.style.ConsentiumDefaults
|
|
||||||
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
|
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
|
||||||
|
|
||||||
private const val NAV_ANIMATION_TIME = 500
|
private const val NAV_ANIMATION_TIME = 500
|
||||||
|
|
||||||
|
private val SLIDE_IN_FROM_RIGHT_ENTER_TRANSITION = slideIn(
|
||||||
|
animationSpec = tween(NAV_ANIMATION_TIME),
|
||||||
|
initialOffset = { fullSize -> IntOffset(x = fullSize.height, y = 0) }
|
||||||
|
)
|
||||||
|
|
||||||
|
private val SLIDE_IN_FROM_LEFT_ENTER_TRANSITION = slideIn(
|
||||||
|
animationSpec = tween(NAV_ANIMATION_TIME),
|
||||||
|
initialOffset = { fullSize -> IntOffset(x = -fullSize.height, y = 0) }
|
||||||
|
)
|
||||||
|
|
||||||
|
private val SLIDE_OUT_TO_LEFT_EXIT_TRANSITION = slideOut(
|
||||||
|
animationSpec = tween(NAV_ANIMATION_TIME),
|
||||||
|
targetOffset = { fullSize -> IntOffset(x = -fullSize.height, y = 0) }
|
||||||
|
)
|
||||||
|
|
||||||
|
private val SLIDE_OUT_TO_RIGHT_EXIT_TRANSITION = slideOut(
|
||||||
|
animationSpec = tween(NAV_ANIMATION_TIME),
|
||||||
|
targetOffset = { fullSize -> IntOffset(x = fullSize.height, y = 0) }
|
||||||
|
)
|
||||||
|
|
||||||
|
private infix fun String?.ifIn(destinations: List<Destination>): Boolean {
|
||||||
|
return this in destinations.map { it::class.qualifiedName }
|
||||||
|
}
|
||||||
|
|
||||||
|
private infix fun Boolean.thenTransitionWith(transition: EnterTransition): EnterTransition? {
|
||||||
|
return if (this) transition else null
|
||||||
|
}
|
||||||
|
|
||||||
|
private infix fun EnterTransition?.elseTransitionWith(transition: EnterTransition): EnterTransition {
|
||||||
|
return this ?: transition
|
||||||
|
}
|
||||||
|
|
||||||
|
private infix fun Boolean.thenTransitionWith(transition: ExitTransition): ExitTransition? {
|
||||||
|
return if (this) transition else null
|
||||||
|
}
|
||||||
|
|
||||||
|
private infix fun ExitTransition?.elseTransitionWith(transition: ExitTransition): ExitTransition {
|
||||||
|
return this ?: transition
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DemoNavGraph(navHostController: NavHostController) {
|
fun DemoNavGraph(navHostController: NavHostController) {
|
||||||
|
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navHostController,
|
navController = navHostController,
|
||||||
startDestination = Destination.Splash,
|
startDestination = Destination.Splash,
|
||||||
enterTransition = {
|
|
||||||
slideIn(
|
|
||||||
animationSpec = tween(NAV_ANIMATION_TIME),
|
|
||||||
initialOffset = { fullSize -> IntOffset(x = fullSize.height, y = 0) }
|
|
||||||
)
|
|
||||||
},
|
|
||||||
exitTransition = {
|
|
||||||
slideOut(
|
|
||||||
animationSpec = tween(NAV_ANIMATION_TIME),
|
|
||||||
targetOffset = { fullSize -> IntOffset(x = -fullSize.height, y = 0) }
|
|
||||||
)
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
composable<Destination.Splash> {
|
composable<Destination.Splash>(
|
||||||
|
enterTransition = {
|
||||||
|
SLIDE_IN_FROM_RIGHT_ENTER_TRANSITION
|
||||||
|
},
|
||||||
|
exitTransition = {
|
||||||
|
SLIDE_OUT_TO_LEFT_EXIT_TRANSITION
|
||||||
|
}
|
||||||
|
) {
|
||||||
SplashScreen(
|
SplashScreen(
|
||||||
navigateToMain = {
|
navigateToMain = {
|
||||||
navHostController.navigate(Destination.Main) {
|
navHostController.navigate(Destination.Main) {
|
||||||
@ -65,7 +91,18 @@ fun DemoNavGraph(navHostController: NavHostController) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable<Destination.Main> {
|
composable<Destination.Main>(
|
||||||
|
enterTransition = {
|
||||||
|
initialState.destination.route ifIn listOf(
|
||||||
|
Destination.Splash
|
||||||
|
) thenTransitionWith SLIDE_IN_FROM_RIGHT_ENTER_TRANSITION elseTransitionWith SLIDE_IN_FROM_LEFT_ENTER_TRANSITION
|
||||||
|
},
|
||||||
|
exitTransition = {
|
||||||
|
targetState.destination.route ifIn listOf(
|
||||||
|
Destination.Splash
|
||||||
|
) thenTransitionWith SLIDE_OUT_TO_RIGHT_EXIT_TRANSITION elseTransitionWith SLIDE_OUT_TO_LEFT_EXIT_TRANSITION
|
||||||
|
}
|
||||||
|
) {
|
||||||
MainScreen(
|
MainScreen(
|
||||||
onGoToConsentMaster = {
|
onGoToConsentMaster = {
|
||||||
navHostController.navigate(Destination.Consent(ConsentiumPageUI.GENERAL_CONSENT))
|
navHostController.navigate(Destination.Consent(ConsentiumPageUI.GENERAL_CONSENT))
|
||||||
@ -76,9 +113,25 @@ fun DemoNavGraph(navHostController: NavHostController) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable<Destination.Consent> { backStackEntry ->
|
composable<Destination.Consent>(
|
||||||
|
enterTransition = {
|
||||||
|
initialState.destination.route ifIn listOf(
|
||||||
|
Destination.Splash,
|
||||||
|
Destination.Main,
|
||||||
|
) thenTransitionWith SLIDE_IN_FROM_RIGHT_ENTER_TRANSITION elseTransitionWith SLIDE_IN_FROM_LEFT_ENTER_TRANSITION
|
||||||
|
},
|
||||||
|
exitTransition = {
|
||||||
|
targetState.destination.route ifIn listOf(
|
||||||
|
Destination.Splash,
|
||||||
|
Destination.Main,
|
||||||
|
) thenTransitionWith SLIDE_OUT_TO_RIGHT_EXIT_TRANSITION elseTransitionWith SLIDE_OUT_TO_LEFT_EXIT_TRANSITION
|
||||||
|
}
|
||||||
|
) { backStackEntry ->
|
||||||
val consent = backStackEntry.toRoute<Destination.Consent>()
|
val consent = backStackEntry.toRoute<Destination.Consent>()
|
||||||
|
|
||||||
|
val appId = stringResource(R.string.app_id)
|
||||||
|
val apiKey = stringResource(R.string.api_key)
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
ConsentiumComponent(
|
ConsentiumComponent(
|
||||||
defaultLandingPage = consent.landingPage,
|
defaultLandingPage = consent.landingPage,
|
||||||
@ -91,21 +144,8 @@ fun DemoNavGraph(navHostController: NavHostController) {
|
|||||||
},
|
},
|
||||||
consentium = Consentium(
|
consentium = Consentium(
|
||||||
context = context,
|
context = context,
|
||||||
applicationId = "ApplicationId",
|
apiKey = apiKey,
|
||||||
),
|
appId = appId,
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ fun MainScreen(
|
|||||||
onGoToConsentDetail: () -> Unit,
|
onGoToConsentDetail: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// View
|
// View
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
|
@ -14,8 +14,11 @@ import androidx.compose.runtime.remember
|
|||||||
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.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import fr.openium.consentium.R
|
||||||
import fr.openium.consentium.api.Consentium
|
import fr.openium.consentium.api.Consentium
|
||||||
|
import fr.openium.consentium.api.checkPurposeState
|
||||||
import fr.openium.consentium.api.state.FetchConsentiumState
|
import fr.openium.consentium.api.state.FetchConsentiumState
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -25,14 +28,18 @@ fun SplashScreen(
|
|||||||
) {
|
) {
|
||||||
// Property
|
// Property
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val consentium = remember { Consentium(context = context, applicationId = "DemoApplicationId") }
|
val apiKey = stringResource(R.string.api_key)
|
||||||
|
val appId = stringResource(R.string.app_id)
|
||||||
|
val consentium = remember { Consentium(context = context, apiKey = apiKey, appId = appId) }
|
||||||
|
|
||||||
// Effect
|
// Effect
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
consentium.fetchConsentState.collect { consentState ->
|
consentium.fetchConsentState.collect { consentState ->
|
||||||
when (consentState) {
|
when (consentState) {
|
||||||
FetchConsentiumState.Idle,
|
FetchConsentiumState.Idle,
|
||||||
FetchConsentiumState.Loading -> {}
|
FetchConsentiumState.Loading,
|
||||||
|
-> {
|
||||||
|
}
|
||||||
|
|
||||||
FetchConsentiumState.Error -> {
|
FetchConsentiumState.Error -> {
|
||||||
// Handle error
|
// Handle error
|
||||||
@ -45,6 +52,22 @@ fun SplashScreen(
|
|||||||
|
|
||||||
is FetchConsentiumState.Valid -> {
|
is FetchConsentiumState.Valid -> {
|
||||||
// The tracking services should be initialized here
|
// The tracking services should be initialized here
|
||||||
|
val consents = consentState.purposes
|
||||||
|
|
||||||
|
consents.checkPurposeState("analytics") { consentState ->
|
||||||
|
// Initialize analytics
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
consents.checkPurposeState("ads") { consentState ->
|
||||||
|
// Initialize ads
|
||||||
|
}
|
||||||
|
|
||||||
|
consents.checkPurposeState("matomo") { consentState ->
|
||||||
|
// Initialize push
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigate on completion
|
||||||
navigateToMain()
|
navigateToMain()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,38 @@ import androidx.compose.runtime.Composable
|
|||||||
|
|
||||||
private val DarkColorScheme = darkColorScheme(
|
private val DarkColorScheme = darkColorScheme(
|
||||||
primary = Primary,
|
primary = Primary,
|
||||||
|
onPrimary = OnPrimary,
|
||||||
|
|
||||||
secondary = Secondary,
|
secondary = Secondary,
|
||||||
tertiary = Tertiary
|
onSecondary = OnSecondary,
|
||||||
|
|
||||||
|
tertiary = Tertiary,
|
||||||
|
|
||||||
|
onSurfaceVariant = OnSurfaceVariant,
|
||||||
|
onSurface = OnSurface,
|
||||||
|
|
||||||
|
error = Error,
|
||||||
|
surfaceContainerHighest = SurfaceHighest,
|
||||||
|
surfaceContainerHigh = SurfaceHigh,
|
||||||
|
surfaceContainer = SurfaceMiddle,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val LightColorScheme = lightColorScheme(
|
private val LightColorScheme = lightColorScheme(
|
||||||
primary = Primary,
|
primary = Primary,
|
||||||
|
onPrimary = OnPrimary,
|
||||||
|
|
||||||
secondary = Secondary,
|
secondary = Secondary,
|
||||||
tertiary = Tertiary
|
onSecondary = OnSecondary,
|
||||||
|
|
||||||
|
tertiary = Tertiary,
|
||||||
|
|
||||||
|
onSurfaceVariant = OnSurfaceVariant,
|
||||||
|
onSurface = OnSurface,
|
||||||
|
|
||||||
|
error = Error,
|
||||||
|
surfaceContainerHighest = SurfaceHighest,
|
||||||
|
surfaceContainerHigh = SurfaceHigh,
|
||||||
|
surfaceContainer = SurfaceMiddle,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Consentium</string>
|
<string name="app_name">Consentium</string>
|
||||||
|
<string name="app_id" translatable="false">01938ce4-331a-7592-9e90-f09201ff4f36</string>
|
||||||
|
<string name="api_key" translatable="false">c452a27f-2e90-427d-be82-2f631c31dd09</string>
|
||||||
</resources>
|
</resources>
|
3
app/version.properties
Normal file
3
app/version.properties
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#Thu Feb 20 15:26:27 CET 2025
|
||||||
|
VERSION_CODE=2
|
||||||
|
VERSION_NAME=1.0.0
|
@ -20,11 +20,13 @@ plugins {
|
|||||||
|
|
||||||
// Kotlin serialization
|
// Kotlin serialization
|
||||||
alias(libs.plugins.serialization) apply false
|
alias(libs.plugins.serialization) apply false
|
||||||
|
}
|
||||||
|
|
||||||
// Firebase crashlytics
|
buildscript {
|
||||||
alias(libs.plugins.firebaseCrashlytics) apply false
|
repositories {
|
||||||
|
maven { url = uri("https://maven.openium.fr/") }
|
||||||
// Google services
|
}
|
||||||
alias(libs.plugins.googleServices) apply false
|
dependencies {
|
||||||
|
classpath(libs.openium.publish)
|
||||||
|
}
|
||||||
}
|
}
|
@ -19,12 +19,26 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
debug {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
|
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
"proguard-rules.pro"
|
"proguard-rules.pro"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
consumerProguardFiles("proguard-rules.pro")
|
||||||
|
}
|
||||||
|
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
|
||||||
|
consumerProguardFiles("proguard-rules.pro")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,6 +48,10 @@ android {
|
|||||||
dimension = "version"
|
dimension = "version"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create("dev") {
|
||||||
|
dimension = "version"
|
||||||
|
}
|
||||||
|
|
||||||
create("demo") {
|
create("demo") {
|
||||||
dimension = "version"
|
dimension = "version"
|
||||||
}
|
}
|
||||||
@ -67,8 +85,6 @@ dependencies {
|
|||||||
implementation(libs.androidx.foundation.android)
|
implementation(libs.androidx.foundation.android)
|
||||||
implementation(libs.androidx.ui.android)
|
implementation(libs.androidx.ui.android)
|
||||||
implementation(libs.androidx.foundation.layout.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.ui.tooling.preview)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
|
|
||||||
@ -100,10 +116,11 @@ dependencies {
|
|||||||
implementation(libs.coil)
|
implementation(libs.coil)
|
||||||
implementation(libs.coil.network)
|
implementation(libs.coil.network)
|
||||||
|
|
||||||
|
// Rich text formatting
|
||||||
|
implementation(libs.rich.text)
|
||||||
|
|
||||||
// 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)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
54
consentium-ui/proguard-rules.pro
vendored
54
consentium-ui/proguard-rules.pro
vendored
@ -18,4 +18,56 @@
|
|||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
# If you keep the line number information, uncomment this to
|
||||||
# hide the original source file name.
|
# hide the original source file name.
|
||||||
#-renamesourcefileattribute SourceFile
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
|
-dontwarn fr.openium.consentium.data.di.ConsentiumUrl
|
||||||
|
-dontwarn fr.openium.consentium.data.di.OkHttpClientDefault
|
||||||
|
-dontwarn java.lang.invoke.StringConcatFactory
|
||||||
|
|
||||||
|
-keepattributes SourceFile,LineNumberTable
|
||||||
|
-keep public class * extends java.lang.Exception
|
||||||
|
|
||||||
|
-keep class com.google.firebase.crashlytics.** { *; }
|
||||||
|
-dontwarn com.google.firebase.crashlytics.**
|
||||||
|
|
||||||
|
# Keep enums
|
||||||
|
-keep public enum fr.openium.consentium.**{
|
||||||
|
*;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Garder les annotations de Kotlin
|
||||||
|
-keepattributes *Annotation*
|
||||||
|
|
||||||
|
# Garder les classes et les membres annotés avec @Keep
|
||||||
|
-keep @androidx.annotation.Keep class * { *; }
|
||||||
|
-keepclassmembers class ** {
|
||||||
|
@androidx.annotation.Keep *;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Garder les classes et les membres annotés avec @Serializable
|
||||||
|
-keep @kotlinx.serialization.Serializable class * { *; }
|
||||||
|
-keepclassmembers class ** {
|
||||||
|
@kotlinx.serialization.Serializable *;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Garder les classes annotées avec @HiltAndroidApp
|
||||||
|
-keep @dagger.hilt.android.HiltAndroidApp class * { *; }
|
||||||
|
|
||||||
|
# Garder les classes et les membres pour Timber
|
||||||
|
-keep class timber.log.Timber { *; }
|
||||||
|
-keep interface timber.log.Timber$Tree { *; }
|
||||||
|
|
||||||
|
-keep public enum fr.openium.consentium_ui.ui.model.ConsentiumPageUI
|
||||||
|
-keepclassmembers enum fr.openium.consentium_ui.ui.model.ConsentiumPageUI {
|
||||||
|
<fields>;
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep class fr.openium.consentium_ui.data.** { *; }
|
||||||
|
-keep class fr.openium.consentium_ui.domain.repository.** { *; }
|
||||||
|
-keep class fr.openium.consentium_ui.domain.usecase.** { *; }
|
||||||
|
-keep class fr.openium.consentium_ui.ui.** { *; }
|
||||||
|
-keepclassmembers class fr.openium.consentium_ui.data.** { *; }
|
||||||
|
-keepclassmembers class fr.openium.consentium_ui.domain.repository.** { *; }
|
||||||
|
-keepclassmembers class fr.openium.consentium_ui.domain.usecase.** { *; }
|
||||||
|
-keepclassmembers class fr.openium.consentium_ui.ui.** { *; }
|
||||||
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
package fr.openium.consentium_ui
|
package fr.openium.consentium_ui
|
||||||
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
import org.junit.Assert.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instrumented test, which will execute on an Android device.
|
* Instrumented test, which will execute on an Android device.
|
||||||
*
|
*
|
||||||
@ -17,8 +14,6 @@ import org.junit.Assert.*
|
|||||||
class ExampleInstrumentedTest {
|
class ExampleInstrumentedTest {
|
||||||
@Test
|
@Test
|
||||||
fun useAppContext() {
|
fun useAppContext() {
|
||||||
// Context of the app under test.
|
assertTrue(true)
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
|
||||||
assertEquals("fr.openium.consentium_ui.test", appContext.packageName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
<?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" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
</manifest>
|
</manifest>
|
@ -3,13 +3,15 @@ package fr.openium.consentium_ui.data.remote
|
|||||||
import fr.openium.consentium_ui.data.remote.model.GetConsentConfigDTO
|
import fr.openium.consentium_ui.data.remote.model.GetConsentConfigDTO
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Header
|
||||||
|
import retrofit2.http.Path
|
||||||
|
|
||||||
internal interface ConsentiumUIApi {
|
internal interface ConsentiumUIApi {
|
||||||
|
|
||||||
@GET("consent-config")
|
@GET("applications/{application}")
|
||||||
suspend fun getConsentConfig(
|
suspend fun getConsentConfig(
|
||||||
applicationID: String,
|
@Header("Authorization") token: String,
|
||||||
installationID: String,
|
@Path("application") applicationId: String,
|
||||||
): Response<GetConsentConfigDTO>
|
): Response<GetConsentConfigDTO>
|
||||||
|
|
||||||
}
|
}
|
@ -6,8 +6,6 @@ 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.PurposeDTO
|
||||||
import fr.openium.consentium_ui.data.remote.model.PurposeStatusDTO
|
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.PurposeTranslationDTO
|
||||||
import fr.openium.consentium_ui.data.remote.model.VendorDTO
|
|
||||||
import fr.openium.consentium_ui.data.remote.model.VendorTranslationDTO
|
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@ -17,24 +15,22 @@ internal object ConsentiumUIMockApi : ConsentiumUIApi {
|
|||||||
installationId = UUID.randomUUID().toString(),
|
installationId = UUID.randomUUID().toString(),
|
||||||
appName = "Consentium",
|
appName = "Consentium",
|
||||||
icon = "https://amp.openium.fr/openium.png",
|
icon = "https://amp.openium.fr/openium.png",
|
||||||
primaryColor = "#FF0000",
|
|
||||||
secondaryColor = "#00FF00",
|
|
||||||
textColor = "#0000FF",
|
|
||||||
consentMainTextTranslation = listOf(
|
consentMainTextTranslation = listOf(
|
||||||
MainConsentTextTranslationDTO(
|
MainConsentTextTranslationDTO(
|
||||||
id = "UUID",
|
id = "UUID",
|
||||||
language = "fr",
|
language = "fr",
|
||||||
consentPageUrl = "https:consentium.fr",
|
consentPageUrl = "https://www.openium.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",
|
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"
|
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"
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
consentPageUrl = "https://www.openium.fr",
|
||||||
purposes = listOf(
|
purposes = listOf(
|
||||||
PurposeDTO(
|
PurposeDTO(
|
||||||
id = "purpose-required",
|
id = "purpose-required",
|
||||||
order = 0,
|
order = 0,
|
||||||
isRequired = true,
|
isRequired = true,
|
||||||
isAccepted = PurposeStatusDTO.ACCEPTED,
|
choice = PurposeStatusDTO.ACCEPTED,
|
||||||
translations = listOf(
|
translations = listOf(
|
||||||
PurposeTranslationDTO(
|
PurposeTranslationDTO(
|
||||||
id = "UUID",
|
id = "UUID",
|
||||||
@ -43,40 +39,12 @@ internal object ConsentiumUIMockApi : ConsentiumUIApi {
|
|||||||
name = "Nécessaire"
|
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(
|
PurposeDTO(
|
||||||
id = "purpose-advertising",
|
id = "purpose-advertising",
|
||||||
order = 1,
|
order = 1,
|
||||||
isRequired = false,
|
isRequired = false,
|
||||||
isAccepted = PurposeStatusDTO.REJECTED,
|
choice = PurposeStatusDTO.REFUSED,
|
||||||
translations = listOf(
|
translations = listOf(
|
||||||
PurposeTranslationDTO(
|
PurposeTranslationDTO(
|
||||||
id = "UUID",
|
id = "UUID",
|
||||||
@ -85,27 +53,12 @@ internal object ConsentiumUIMockApi : ConsentiumUIApi {
|
|||||||
name = "Publicité"
|
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(
|
PurposeDTO(
|
||||||
id = "purpose-analytics",
|
id = "purpose-analytics",
|
||||||
order = 2,
|
order = 2,
|
||||||
isRequired = false,
|
isRequired = false,
|
||||||
isAccepted = PurposeStatusDTO.ACCEPTED,
|
choice = PurposeStatusDTO.ACCEPTED,
|
||||||
translations = listOf(
|
translations = listOf(
|
||||||
PurposeTranslationDTO(
|
PurposeTranslationDTO(
|
||||||
id = "UUID",
|
id = "UUID",
|
||||||
@ -114,27 +67,12 @@ internal object ConsentiumUIMockApi : ConsentiumUIApi {
|
|||||||
name = "Analyse"
|
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(
|
PurposeDTO(
|
||||||
id = "purpose-personalization",
|
id = "purpose-personalization",
|
||||||
order = 3,
|
order = 3,
|
||||||
isRequired = false,
|
isRequired = false,
|
||||||
isAccepted = PurposeStatusDTO.ACCEPTED,
|
choice = PurposeStatusDTO.ACCEPTED,
|
||||||
translations = listOf(
|
translations = listOf(
|
||||||
PurposeTranslationDTO(
|
PurposeTranslationDTO(
|
||||||
id = "UUID",
|
id = "UUID",
|
||||||
@ -143,27 +81,12 @@ internal object ConsentiumUIMockApi : ConsentiumUIApi {
|
|||||||
name = "Personnalisation"
|
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(
|
PurposeDTO(
|
||||||
id = "purpose-social",
|
id = "purpose-social",
|
||||||
order = 4,
|
order = 4,
|
||||||
isRequired = false,
|
isRequired = false,
|
||||||
isAccepted = PurposeStatusDTO.ACCEPTED,
|
choice = PurposeStatusDTO.ACCEPTED,
|
||||||
translations = listOf(
|
translations = listOf(
|
||||||
PurposeTranslationDTO(
|
PurposeTranslationDTO(
|
||||||
id = "UUID",
|
id = "UUID",
|
||||||
@ -172,28 +95,13 @@ internal object ConsentiumUIMockApi : ConsentiumUIApi {
|
|||||||
name = "Social"
|
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(
|
override suspend fun getConsentConfig(
|
||||||
applicationID: String,
|
token: String,
|
||||||
installationID: String,
|
applicationId: String,
|
||||||
): Response<GetConsentConfigDTO> {
|
): Response<GetConsentConfigDTO> {
|
||||||
return Response.success(consents)
|
return Response.success(consents)
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,8 @@ import kotlinx.serialization.Serializable
|
|||||||
internal data class GetConsentConfigDTO(
|
internal data class GetConsentConfigDTO(
|
||||||
@SerialName("id") val installationId: String,
|
@SerialName("id") val installationId: String,
|
||||||
@SerialName("name") val appName: String,
|
@SerialName("name") val appName: String,
|
||||||
@SerialName("icon") val icon: String,
|
@SerialName("icon") val icon: String? = null,
|
||||||
@SerialName("primaryColor") val primaryColor: String,
|
@SerialName("consentPageUrl") val consentPageUrl: String,
|
||||||
@SerialName("secondaryColor") val secondaryColor: String,
|
@SerialName("translations") val consentMainTextTranslation: List<MainConsentTextTranslationDTO>,
|
||||||
@SerialName("textColor") val textColor: String,
|
|
||||||
@SerialName("translation") val consentMainTextTranslation: List<MainConsentTextTranslationDTO>,
|
|
||||||
@SerialName("purposes") val purposes: List<PurposeDTO>,
|
@SerialName("purposes") val purposes: List<PurposeDTO>,
|
||||||
)
|
)
|
@ -7,7 +7,8 @@ import kotlinx.serialization.Serializable
|
|||||||
internal data class MainConsentTextTranslationDTO(
|
internal data class MainConsentTextTranslationDTO(
|
||||||
@SerialName("id") val id: String,
|
@SerialName("id") val id: String,
|
||||||
@SerialName("lang") val language: String,
|
@SerialName("lang") val language: String,
|
||||||
@SerialName("consentPageUrl") val consentPageUrl: String,
|
|
||||||
@SerialName("mainConsentText") val mainConsentText: String,
|
@SerialName("mainConsentText") val mainConsentText: String,
|
||||||
@SerialName("durationText") val durationText: String,
|
@SerialName("durationText") val durationText: String,
|
||||||
|
@SerialName("consentPageUrl") val consentPageUrl: String,
|
||||||
|
|
||||||
)
|
)
|
@ -5,10 +5,10 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class PurposeDTO(
|
internal data class PurposeDTO(
|
||||||
@SerialName("identifier") val id: String,
|
@SerialName("sortOrder") val order: Int,
|
||||||
@SerialName("order") val order: Int,
|
|
||||||
@SerialName("isRequired") val isRequired: Boolean,
|
@SerialName("isRequired") val isRequired: Boolean,
|
||||||
@SerialName("isAccepted") val isAccepted: PurposeStatusDTO,
|
@SerialName("choice") val choice: PurposeStatusDTO,
|
||||||
@SerialName("translations") val translations: List<PurposeTranslationDTO>,
|
@SerialName("translations") val translations: List<PurposeTranslationDTO>,
|
||||||
@SerialName("vendors") val vendors: List<VendorDTO>,
|
@SerialName("identifier") val id: String,
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -5,12 +5,12 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal enum class PurposeStatusDTO {
|
internal enum class PurposeStatusDTO {
|
||||||
@SerialName("ACCEPTED")
|
@SerialName("accepted")
|
||||||
ACCEPTED,
|
ACCEPTED,
|
||||||
|
|
||||||
@SerialName("REJECTED")
|
@SerialName("refused")
|
||||||
REJECTED,
|
REFUSED,
|
||||||
|
|
||||||
@SerialName("NOT_DEFINED")
|
@SerialName("partial")
|
||||||
NOT_DEFINED,
|
PARTIAL,
|
||||||
}
|
}
|
@ -7,9 +7,9 @@ internal fun PurposeDTO.toPurposeData() =
|
|||||||
PurposeData(
|
PurposeData(
|
||||||
identifier = id,
|
identifier = id,
|
||||||
isRequired = isRequired,
|
isRequired = isRequired,
|
||||||
isAccepted = isAccepted.toPurposeStatusData(),
|
choice = choice.toPurposeStatusData(),
|
||||||
order = order,
|
order = order,
|
||||||
vendors = vendors.toVendorDataList(),
|
vendors = emptyList(),
|
||||||
translations = translations.toPurposeTranslationDataList(),
|
translations = translations.toPurposeTranslationDataList(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import fr.openium.consentium_ui.domain.model.PurposeStatusData
|
|||||||
internal fun PurposeStatusDTO.toPurposeStatusData(): PurposeStatusData {
|
internal fun PurposeStatusDTO.toPurposeStatusData(): PurposeStatusData {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
PurposeStatusDTO.ACCEPTED -> PurposeStatusData.ACCEPTED
|
PurposeStatusDTO.ACCEPTED -> PurposeStatusData.ACCEPTED
|
||||||
PurposeStatusDTO.REJECTED -> PurposeStatusData.REJECTED
|
PurposeStatusDTO.REFUSED -> PurposeStatusData.REJECTED
|
||||||
PurposeStatusDTO.NOT_DEFINED -> PurposeStatusData.NOT_DEFINED
|
PurposeStatusDTO.PARTIAL -> PurposeStatusData.PARTIAL
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ package fr.openium.consentium_ui.domain.model
|
|||||||
|
|
||||||
internal data class ContentConfigData(
|
internal data class ContentConfigData(
|
||||||
val applicationName: String,
|
val applicationName: String,
|
||||||
val iconUrl : String,
|
val iconUrl : String?,
|
||||||
val mainTextTranslation: List<MainConsentTextTranslationData>,
|
val mainTextTranslation: List<MainConsentTextTranslationData>,
|
||||||
val purposes: List<PurposeData>
|
val purposes: List<PurposeData>
|
||||||
)
|
)
|
||||||
|
@ -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: PurposeStatusData,
|
val choice: PurposeStatusData,
|
||||||
val translations: List<PurposeTranslationData>,
|
val translations: List<PurposeTranslationData>,
|
||||||
val vendors: List<VendorData>,
|
val vendors: List<VendorData>,
|
||||||
)
|
)
|
||||||
|
@ -3,5 +3,5 @@ package fr.openium.consentium_ui.domain.model
|
|||||||
internal enum class PurposeStatusData {
|
internal enum class PurposeStatusData {
|
||||||
ACCEPTED,
|
ACCEPTED,
|
||||||
REJECTED,
|
REJECTED,
|
||||||
NOT_DEFINED,
|
PARTIAL,
|
||||||
}
|
}
|
@ -1,22 +1,23 @@
|
|||||||
package fr.openium.consentium_ui.domain.repository
|
package fr.openium.consentium_ui.domain.repository
|
||||||
|
|
||||||
import fr.openium.consentium.domain.useCase.GetConsentiumUniqueInstallationIdUseCase
|
import fr.openium.consentium.domain.useCase.GetAuthTokenUseCase
|
||||||
import fr.openium.consentium_ui.data.remote.ConsentiumUIApi
|
import fr.openium.consentium_ui.data.remote.ConsentiumUIApi
|
||||||
import fr.openium.consentium_ui.domain.adapter.toConsentConfigData
|
import fr.openium.consentium_ui.domain.adapter.toConsentConfigData
|
||||||
import fr.openium.consentium_ui.domain.model.ContentConfigData
|
import fr.openium.consentium_ui.domain.model.ContentConfigData
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class ConsentiumRepository @Inject constructor(
|
internal class ConsentiumRepository @Inject constructor(
|
||||||
private val getConsentiumUniqueInstallationIdUseCase: GetConsentiumUniqueInstallationIdUseCase,
|
|
||||||
private val consentiumUIApi: ConsentiumUIApi,
|
private val consentiumUIApi: ConsentiumUIApi,
|
||||||
|
private val getAuthTokenUseCase: GetAuthTokenUseCase,
|
||||||
) {
|
) {
|
||||||
suspend fun getConsentiumConfig(
|
suspend fun getConsentiumConfig(
|
||||||
applicationId: String,
|
applicationId: String,
|
||||||
|
apiKey: String,
|
||||||
): ConsentiumUIRepositoryResponse {
|
): ConsentiumUIRepositoryResponse {
|
||||||
val installationId = getConsentiumUniqueInstallationIdUseCase.invoke()
|
|
||||||
val consentsResponse = consentiumUIApi.getConsentConfig(applicationId, installationId)
|
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
|
val authToken = getAuthTokenUseCase(apiKey)
|
||||||
|
val consentsResponse = consentiumUIApi.getConsentConfig(authToken, applicationId)
|
||||||
val consentsBody = if (consentsResponse.isSuccessful) {
|
val consentsBody = if (consentsResponse.isSuccessful) {
|
||||||
consentsResponse.body() ?: throw Exception()
|
consentsResponse.body() ?: throw Exception()
|
||||||
} else {
|
} else {
|
||||||
@ -25,10 +26,10 @@ internal class ConsentiumRepository @Inject constructor(
|
|||||||
|
|
||||||
ConsentiumUIRepositoryResponse.Success(consentsBody.toConsentConfigData())
|
ConsentiumUIRepositoryResponse.Success(consentsBody.toConsentConfigData())
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Timber.d("$e")
|
||||||
ConsentiumUIRepositoryResponse.Error
|
ConsentiumUIRepositoryResponse.Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface ConsentiumUIRepositoryResponse {
|
internal interface ConsentiumUIRepositoryResponse {
|
||||||
|
@ -10,6 +10,7 @@ import fr.openium.consentium_ui.ui.components.ConsentiumUIDetailConsentComponent
|
|||||||
import fr.openium.consentium_ui.ui.components.ConsentiumUIErrorComponent
|
import fr.openium.consentium_ui.ui.components.ConsentiumUIErrorComponent
|
||||||
import fr.openium.consentium_ui.ui.components.ConsentiumUIGeneralConsentComponent
|
import fr.openium.consentium_ui.ui.components.ConsentiumUIGeneralConsentComponent
|
||||||
import fr.openium.consentium_ui.ui.components.ConsentiumUILoadingComponent
|
import fr.openium.consentium_ui.ui.components.ConsentiumUILoadingComponent
|
||||||
|
import fr.openium.consentium_ui.ui.components.ConsentiumUIWebview
|
||||||
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
|
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
|
||||||
import fr.openium.consentium_ui.ui.model.DetailConsentUI
|
import fr.openium.consentium_ui.ui.model.DetailConsentUI
|
||||||
import fr.openium.consentium_ui.ui.model.LoadingElement
|
import fr.openium.consentium_ui.ui.model.LoadingElement
|
||||||
@ -26,6 +27,7 @@ internal fun ConsentiumScreen(
|
|||||||
onSaveAndCloseDetails: (consents: DetailConsentUI) -> Unit,
|
onSaveAndCloseDetails: (consents: DetailConsentUI) -> Unit,
|
||||||
onNavigateToDetails: () -> Unit,
|
onNavigateToDetails: () -> Unit,
|
||||||
onClickCookiesPolicies: () -> Unit,
|
onClickCookiesPolicies: () -> Unit,
|
||||||
|
onClickRetry: () -> Unit,
|
||||||
) {
|
) {
|
||||||
when (state) {
|
when (state) {
|
||||||
is ConsentiumUIState.Loading -> {
|
is ConsentiumUIState.Loading -> {
|
||||||
@ -35,6 +37,7 @@ internal fun ConsentiumScreen(
|
|||||||
is ConsentiumUIState.Error -> {
|
is ConsentiumUIState.Error -> {
|
||||||
ConsentiumUIErrorComponent(
|
ConsentiumUIErrorComponent(
|
||||||
errorMessage = state.message,
|
errorMessage = state.message,
|
||||||
|
onRetry = onClickRetry
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +56,7 @@ internal fun ConsentiumScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsentiumPageUI.DETAILS_CONSENT -> {
|
else -> {
|
||||||
slideIn(
|
slideIn(
|
||||||
initialOffset = { fullSize -> IntOffset(fullSize.width, 0) }
|
initialOffset = { fullSize -> IntOffset(fullSize.width, 0) }
|
||||||
).togetherWith(
|
).togetherWith(
|
||||||
@ -92,6 +95,12 @@ internal fun ConsentiumScreen(
|
|||||||
loadingElement = loadingElement
|
loadingElement = loadingElement
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConsentiumPageUI.COOKIES_POLICY -> {
|
||||||
|
ConsentiumUIWebview(
|
||||||
|
url = state.detailConsentUI.cookiePolicyUrl
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import fr.openium.consentium_ui.R
|
||||||
import fr.openium.consentium_ui.domain.repository.ConsentiumRepository
|
import fr.openium.consentium_ui.domain.repository.ConsentiumRepository
|
||||||
import fr.openium.consentium_ui.domain.repository.ConsentiumUIRepositoryResponse
|
import fr.openium.consentium_ui.domain.repository.ConsentiumUIRepositoryResponse
|
||||||
import fr.openium.consentium_ui.domain.usecase.GetApplicationLanguageUseCase
|
import fr.openium.consentium_ui.domain.usecase.GetApplicationLanguageUseCase
|
||||||
@ -30,12 +31,12 @@ internal class ConsentiumUIViewModel @Inject constructor(
|
|||||||
private val _state = MutableStateFlow<ConsentiumUIState>(ConsentiumUIState.Loading)
|
private val _state = MutableStateFlow<ConsentiumUIState>(ConsentiumUIState.Loading)
|
||||||
val state: StateFlow<ConsentiumUIState> by lazy { _state.asStateFlow() }
|
val state: StateFlow<ConsentiumUIState> by lazy { _state.asStateFlow() }
|
||||||
|
|
||||||
fun init(appId: String) {
|
fun init(appId: String, apiKey: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|
||||||
_state.value = ConsentiumUIState.Loading
|
_state.value = ConsentiumUIState.Loading
|
||||||
|
|
||||||
when (val consentiumUIFetchResponse = consentiumUIRepository.getConsentiumConfig(appId)) {
|
when (val consentiumUIFetchResponse = consentiumUIRepository.getConsentiumConfig(appId, apiKey)) {
|
||||||
|
|
||||||
is ConsentiumUIRepositoryResponse.Success -> {
|
is ConsentiumUIRepositoryResponse.Success -> {
|
||||||
val consentUIResponse =
|
val consentUIResponse =
|
||||||
@ -44,28 +45,32 @@ internal class ConsentiumUIViewModel @Inject constructor(
|
|||||||
when (val consentUI = consentUIResponse) {
|
when (val consentUI = consentUIResponse) {
|
||||||
|
|
||||||
is GetConfigTextForLanguageUseCaseResponse.Success -> {
|
is GetConfigTextForLanguageUseCaseResponse.Success -> {
|
||||||
_state.emit(ConsentiumUIState.Loaded(
|
_state.emit(
|
||||||
generalConsentUI = consentUI.configData.toGeneralConsentUI(),
|
ConsentiumUIState.Loaded(
|
||||||
detailConsentUI = consentUI.configData.toDetailConsentUI(),
|
generalConsentUI = consentUI.configData.toGeneralConsentUI(),
|
||||||
))
|
detailConsentUI = consentUI.configData.toDetailConsentUI(),
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is GetConfigTextForLanguageUseCaseResponse.DefaultLanguage -> {
|
is GetConfigTextForLanguageUseCaseResponse.DefaultLanguage -> {
|
||||||
_state.emit(ConsentiumUIState.Loaded(
|
_state.emit(
|
||||||
generalConsentUI = consentUI.configData.toGeneralConsentUI(),
|
ConsentiumUIState.Loaded(
|
||||||
detailConsentUI = consentUI.configData.toDetailConsentUI(),
|
generalConsentUI = consentUI.configData.toGeneralConsentUI(),
|
||||||
))
|
detailConsentUI = consentUI.configData.toDetailConsentUI(),
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is GetConfigTextForLanguageUseCaseResponse.Error -> {
|
is GetConfigTextForLanguageUseCaseResponse.Error -> {
|
||||||
_state.emit(ConsentiumUIState.Error("Failed to load data"))
|
_state.emit(ConsentiumUIState.Error(context.getString(R.string.consents_error_loading_consents)))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ConsentiumUIRepositoryResponse.Error -> {
|
is ConsentiumUIRepositoryResponse.Error -> {
|
||||||
_state.value = ConsentiumUIState.Error("Failed to load data")
|
_state.value = ConsentiumUIState.Error(context.getString(R.string.consents_error_loading_consents))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package fr.openium.consentium_ui.ui.adapter
|
package fr.openium.consentium_ui.ui.adapter
|
||||||
|
|
||||||
import fr.openium.consentium.api.model.PurposeChoice
|
import fr.openium.consentium.api.model.PurposeChoice
|
||||||
|
import fr.openium.consentium.api.model.PurposeStatus
|
||||||
import fr.openium.consentium_ui.domain.model.ContentConfigData
|
import fr.openium.consentium_ui.domain.model.ContentConfigData
|
||||||
import fr.openium.consentium_ui.ui.model.DetailConsentUI
|
import fr.openium.consentium_ui.ui.model.DetailConsentUI
|
||||||
import fr.openium.consentium_ui.ui.model.PurposeStatusUI
|
import fr.openium.consentium_ui.ui.model.PurposeStatusUI
|
||||||
@ -9,6 +10,7 @@ import fr.openium.consentium_ui.ui.model.PurposeUI
|
|||||||
internal fun ContentConfigData.toDetailConsentUI(): DetailConsentUI = DetailConsentUI(
|
internal fun ContentConfigData.toDetailConsentUI(): DetailConsentUI = DetailConsentUI(
|
||||||
conservationDurationText = mainTextTranslation.first().durationText,
|
conservationDurationText = mainTextTranslation.first().durationText,
|
||||||
purposes = purposes.sortedBy { it.order }.map { it.toPurposeUI() },
|
purposes = purposes.sortedBy { it.order }.map { it.toPurposeUI() },
|
||||||
|
cookiePolicyUrl = mainTextTranslation.first().consentPageUrl,
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun DetailConsentUI.toPurposeChoices(): List<PurposeChoice> = purposes.map {
|
internal fun DetailConsentUI.toPurposeChoices(): List<PurposeChoice> = purposes.map {
|
||||||
@ -16,16 +18,23 @@ internal fun DetailConsentUI.toPurposeChoices(): List<PurposeChoice> = purposes.
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun DetailConsentUI.toDeniedPurposeChoices(): List<PurposeChoice> = purposes.map {
|
internal fun DetailConsentUI.toDeniedPurposeChoices(): List<PurposeChoice> = purposes.map {
|
||||||
it.toPurposeChoice().copy(isAccepted = false)
|
it.toPurposeChoice().copy(choice = PurposeStatus.REJECTED)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun DetailConsentUI.toAcceptedPurposeChoices(): List<PurposeChoice> = purposes.map {
|
internal fun DetailConsentUI.toAcceptedPurposeChoices(): List<PurposeChoice> = purposes.map {
|
||||||
it.toPurposeChoice().copy(isAccepted = true)
|
it.toPurposeChoice().copy(choice = PurposeStatus.ACCEPTED)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun PurposeUI.toPurposeChoice(): PurposeChoice =
|
internal fun PurposeUI.toPurposeChoice(): PurposeChoice =
|
||||||
PurposeChoice(
|
PurposeChoice(
|
||||||
purposeIdentifier = id,
|
purposeIdentifier = id,
|
||||||
isAccepted = isAccepted == PurposeStatusUI.ACCEPTED,
|
choice = choice.toPurposeStatus(),
|
||||||
vendors = emptyList(), // Not in v1
|
vendors = emptyList(), // Not in v1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal fun PurposeStatusUI.toPurposeStatus(): PurposeStatus =
|
||||||
|
when (this) {
|
||||||
|
PurposeStatusUI.ACCEPTED -> PurposeStatus.ACCEPTED
|
||||||
|
PurposeStatusUI.REJECTED -> PurposeStatus.REJECTED
|
||||||
|
PurposeStatusUI.PARTIAL -> PurposeStatus.PARTIAL
|
||||||
|
}
|
@ -6,5 +6,5 @@ import fr.openium.consentium_ui.ui.model.PurposeStatusUI
|
|||||||
internal fun PurposeStatusData.toPurposeStatusUI(): PurposeStatusUI = when(this) {
|
internal fun PurposeStatusData.toPurposeStatusUI(): PurposeStatusUI = when(this) {
|
||||||
PurposeStatusData.ACCEPTED -> PurposeStatusUI.ACCEPTED
|
PurposeStatusData.ACCEPTED -> PurposeStatusUI.ACCEPTED
|
||||||
PurposeStatusData.REJECTED -> PurposeStatusUI.REJECTED
|
PurposeStatusData.REJECTED -> PurposeStatusUI.REJECTED
|
||||||
PurposeStatusData.NOT_DEFINED -> PurposeStatusUI.NOT_DEFINED
|
PurposeStatusData.PARTIAL -> PurposeStatusUI.PARTIAL
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import fr.openium.consentium_ui.ui.model.PurposeUI
|
|||||||
internal fun PurposeData.toPurposeUI(): PurposeUI = PurposeUI(
|
internal fun PurposeData.toPurposeUI(): PurposeUI = PurposeUI(
|
||||||
id = identifier,
|
id = identifier,
|
||||||
isRequired = isRequired,
|
isRequired = isRequired,
|
||||||
isAccepted = isAccepted.toPurposeStatusUI(),
|
choice = choice.toPurposeStatusUI(),
|
||||||
title = translations.first().name,
|
title = translations.first().name,
|
||||||
description = translations.first().text,
|
description = translations.first().text,
|
||||||
vendors = vendors.toVendorUIList(),
|
vendors = vendors.toVendorUIList(),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package fr.openium.consentium_ui.ui.components
|
package fr.openium.consentium_ui.ui.components
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@ -50,7 +51,7 @@ fun ConsentiumComponent(
|
|||||||
|
|
||||||
// Effect
|
// Effect
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.init(consentium.applicationId)
|
viewModel.init(apiKey = consentium.apiKey, appId = consentium.appId)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(consentiumState) {
|
LaunchedEffect(consentiumState) {
|
||||||
@ -70,6 +71,22 @@ fun ConsentiumComponent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BackHandler {
|
||||||
|
when {
|
||||||
|
defaultLandingPage == ConsentiumPageUI.GENERAL_CONSENT && currentPage == ConsentiumPageUI.DETAILS_CONSENT -> {
|
||||||
|
currentPage = ConsentiumPageUI.GENERAL_CONSENT
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPage == ConsentiumPageUI.COOKIES_POLICY -> {
|
||||||
|
currentPage = ConsentiumPageUI.GENERAL_CONSENT
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
onQuitConsent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// View
|
// View
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalColors provides colors,
|
LocalColors provides colors,
|
||||||
@ -112,8 +129,11 @@ fun ConsentiumComponent(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClickCookiesPolicies = {
|
onClickCookiesPolicies = {
|
||||||
// TODO Open cookies policies
|
currentPage = ConsentiumPageUI.COOKIES_POLICY
|
||||||
},
|
},
|
||||||
|
onClickRetry = {
|
||||||
|
viewModel.init(apiKey = consentium.apiKey, appId = consentium.appId)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ import fr.openium.consentium_ui.ui.components.core.button.ConsentiumUIButtonStyl
|
|||||||
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
||||||
import fr.openium.consentium_ui.ui.model.DetailConsentUI
|
import fr.openium.consentium_ui.ui.model.DetailConsentUI
|
||||||
import fr.openium.consentium_ui.ui.model.LoadingElement
|
import fr.openium.consentium_ui.ui.model.LoadingElement
|
||||||
import fr.openium.consentium_ui.ui.utils.htmlToAnnotatedString
|
import fr.openium.consentium_ui.ui.utils.toRichHtmlString
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -55,7 +55,7 @@ internal fun ConsentiumUIDetailConsentComponent(
|
|||||||
IconButton(onClick = { onNavigateBack() }) {
|
IconButton(onClick = { onNavigateBack() }) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
contentDescription = "Back",
|
contentDescription = stringResource(R.string.back),
|
||||||
tint = ConsentiumUITheme.colors.onSurface,
|
tint = ConsentiumUITheme.colors.onSurface,
|
||||||
modifier = Modifier.size(26.dp),
|
modifier = Modifier.size(26.dp),
|
||||||
)
|
)
|
||||||
@ -73,7 +73,7 @@ internal fun ConsentiumUIDetailConsentComponent(
|
|||||||
Spacer(modifier = Modifier.height(25.dp))
|
Spacer(modifier = Modifier.height(25.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = htmlToAnnotatedString(detailConsentUI.conservationDurationText),
|
text = detailConsentUI.conservationDurationText.toRichHtmlString(),
|
||||||
style = ConsentiumUITheme.typography.p3,
|
style = ConsentiumUITheme.typography.p3,
|
||||||
color = ConsentiumUITheme.colors.onSurface,
|
color = ConsentiumUITheme.colors.onSurface,
|
||||||
)
|
)
|
||||||
|
@ -2,19 +2,31 @@ package fr.openium.consentium_ui.ui.components
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import fr.openium.consentium_ui.R
|
||||||
|
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.components.style.ConsentiumUITheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ConsentiumUIErrorComponent(
|
internal fun ConsentiumUIErrorComponent(
|
||||||
errorMessage: String,
|
errorMessage: String,
|
||||||
|
onRetry: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 24.dp),
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
@ -22,6 +34,15 @@ internal fun ConsentiumUIErrorComponent(
|
|||||||
text = errorMessage,
|
text = errorMessage,
|
||||||
color = ConsentiumUITheme.colors.error,
|
color = ConsentiumUITheme.colors.error,
|
||||||
style = ConsentiumUITheme.typography.b2,
|
style = ConsentiumUITheme.typography.b2,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
|
ConsentButton(
|
||||||
|
text = stringResource(R.string.retry),
|
||||||
|
buttonStyle = ConsentiumUIButtonStyle.PRIMARY,
|
||||||
|
onclick = onRetry,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
@ -25,7 +26,7 @@ import fr.openium.consentium_ui.ui.components.core.button.ConsentiumUIButtonStyl
|
|||||||
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
||||||
import fr.openium.consentium_ui.ui.model.GeneralConsentUI
|
import fr.openium.consentium_ui.ui.model.GeneralConsentUI
|
||||||
import fr.openium.consentium_ui.ui.model.LoadingElement
|
import fr.openium.consentium_ui.ui.model.LoadingElement
|
||||||
import fr.openium.consentium_ui.ui.utils.htmlToAnnotatedString
|
import fr.openium.consentium_ui.ui.utils.toRichHtmlString
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ConsentiumUIGeneralConsentComponent(
|
internal fun ConsentiumUIGeneralConsentComponent(
|
||||||
@ -42,36 +43,40 @@ internal fun ConsentiumUIGeneralConsentComponent(
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(horizontal = 24.dp),
|
.padding(horizontal = 24.dp),
|
||||||
) {
|
) {
|
||||||
|
if (generalConsentUI.iconUrl != null) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
) {
|
||||||
|
AsyncImage(
|
||||||
|
modifier = Modifier.heightIn(min = 48.dp),
|
||||||
|
model = generalConsentUI.iconUrl,
|
||||||
|
contentDescription = stringResource(R.string.app_icon),
|
||||||
|
contentScale = ContentScale.FillBounds,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Row(
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
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(
|
||||||
text = htmlToAnnotatedString(generalConsentUI.mainConsentText),
|
text = generalConsentUI.mainConsentText.toRichHtmlString(),
|
||||||
style = ConsentiumUITheme.typography.p3,
|
style = ConsentiumUITheme.typography.p3,
|
||||||
textAlign = TextAlign.Start,
|
textAlign = TextAlign.Start,
|
||||||
color = ConsentiumUITheme.colors.onSurface,
|
color = ConsentiumUITheme.colors.onSurface,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
TextLink(
|
TextLink(
|
||||||
text = stringResource(R.string.cookies),
|
text = stringResource(R.string.cookies),
|
||||||
onclick = onClickCookiesPolicies,
|
onclick = onClickCookiesPolicies,
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier
|
||||||
|
.heightIn(16.dp)
|
||||||
|
.weight(1f))
|
||||||
|
|
||||||
TextLink(
|
TextLink(
|
||||||
text = stringResource(R.string.refuse),
|
text = stringResource(R.string.refuse),
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package fr.openium.consentium_ui.ui.components
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.webkit.WebView
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ConsentiumUIWebview(
|
||||||
|
url: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
AndroidView(
|
||||||
|
modifier = modifier,
|
||||||
|
factory = {
|
||||||
|
WebView(it).apply {
|
||||||
|
layoutParams = ViewGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
update = { webView ->
|
||||||
|
webView.loadUrl(url)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
@ -14,6 +14,8 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import fr.openium.consentium_ui.R
|
import fr.openium.consentium_ui.R
|
||||||
@ -21,14 +23,14 @@ 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.components.style.ConsentiumUITheme
|
||||||
import fr.openium.consentium_ui.ui.model.PurposeStatusUI
|
import fr.openium.consentium_ui.ui.model.PurposeStatusUI
|
||||||
import fr.openium.consentium_ui.ui.model.PurposeUI
|
import fr.openium.consentium_ui.ui.model.PurposeUI
|
||||||
import fr.openium.consentium_ui.ui.utils.htmlToAnnotatedString
|
import fr.openium.consentium_ui.ui.utils.toRichHtmlString
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun PurposeComponent(
|
internal fun PurposeComponent(
|
||||||
purposeUI: PurposeUI,
|
purposeUI: PurposeUI,
|
||||||
) {
|
) {
|
||||||
// Properties
|
// Properties
|
||||||
var isChecked by remember { mutableStateOf(purposeUI.isAccepted != PurposeStatusUI.REJECTED) }
|
var isChecked by remember { mutableStateOf(purposeUI.choice != PurposeStatusUI.REJECTED) }
|
||||||
|
|
||||||
// View
|
// View
|
||||||
Column(
|
Column(
|
||||||
@ -58,11 +60,15 @@ internal fun PurposeComponent(
|
|||||||
checked = isChecked,
|
checked = isChecked,
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
isChecked = !isChecked
|
isChecked = !isChecked
|
||||||
purposeUI.isAccepted = if (isChecked) PurposeStatusUI.ACCEPTED else PurposeStatusUI.REJECTED
|
purposeUI.choice = if (isChecked) PurposeStatusUI.ACCEPTED else PurposeStatusUI.REJECTED
|
||||||
purposeUI.vendors.forEach { vendorUI ->
|
purposeUI.vendors.forEach { vendorUI ->
|
||||||
vendorUI.isAccepted = isChecked
|
vendorUI.isAccepted = isChecked
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.semantics {
|
||||||
|
contentDescription = purposeUI.title
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +87,7 @@ internal fun PurposeComponent(
|
|||||||
Spacer(modifier = Modifier.height(10.dp))
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = htmlToAnnotatedString(purposeUI.description),
|
text = purposeUI.description.toRichHtmlString(),
|
||||||
style = ConsentiumUITheme.typography.p3,
|
style = ConsentiumUITheme.typography.p3,
|
||||||
color = ConsentiumUITheme.colors.onSurface,
|
color = ConsentiumUITheme.colors.onSurface,
|
||||||
)
|
)
|
||||||
@ -95,7 +101,7 @@ private fun PurposeComponentPreview() {
|
|||||||
purposeUI = PurposeUI(
|
purposeUI = PurposeUI(
|
||||||
id = "1",
|
id = "1",
|
||||||
isRequired = true,
|
isRequired = true,
|
||||||
isAccepted = PurposeStatusUI.ACCEPTED,
|
choice = PurposeStatusUI.ACCEPTED,
|
||||||
title = "Title",
|
title = "Title",
|
||||||
description = "Description",
|
description = "Description",
|
||||||
vendors = emptyList(),
|
vendors = emptyList(),
|
||||||
|
@ -15,6 +15,7 @@ import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
|
|||||||
internal fun ConsentiumUISwitch(
|
internal fun ConsentiumUISwitch(
|
||||||
checked: Boolean,
|
checked: Boolean,
|
||||||
onCheckedChange: (Boolean) -> Unit,
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Switch(
|
Switch(
|
||||||
checked = checked,
|
checked = checked,
|
||||||
@ -22,6 +23,7 @@ internal fun ConsentiumUISwitch(
|
|||||||
colors = SwitchDefaults.colors(
|
colors = SwitchDefaults.colors(
|
||||||
checkedTrackColor = ConsentiumUITheme.colors.secondary,
|
checkedTrackColor = ConsentiumUITheme.colors.secondary,
|
||||||
),
|
),
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ object ConsentiumDefaults {
|
|||||||
fun colors(
|
fun colors(
|
||||||
primary: Color = MaterialTheme.colorScheme.primary,
|
primary: Color = MaterialTheme.colorScheme.primary,
|
||||||
onPrimary: Color = MaterialTheme.colorScheme.onPrimary,
|
onPrimary: Color = MaterialTheme.colorScheme.onPrimary,
|
||||||
secondary: Color = MaterialTheme.colorScheme.background,
|
secondary: Color = MaterialTheme.colorScheme.secondary,
|
||||||
onSecondary: Color = MaterialTheme.colorScheme.surfaceVariant,
|
onSecondary: Color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
tertiary: Color = MaterialTheme.colorScheme.onBackground,
|
tertiary: Color = MaterialTheme.colorScheme.onBackground,
|
||||||
onSurfaceVariant: Color = MaterialTheme.colorScheme.onBackground,
|
onSurfaceVariant: Color = MaterialTheme.colorScheme.onBackground,
|
||||||
|
@ -2,5 +2,6 @@ package fr.openium.consentium_ui.ui.model
|
|||||||
|
|
||||||
enum class ConsentiumPageUI {
|
enum class ConsentiumPageUI {
|
||||||
GENERAL_CONSENT,
|
GENERAL_CONSENT,
|
||||||
DETAILS_CONSENT
|
DETAILS_CONSENT,
|
||||||
|
COOKIES_POLICY,
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package fr.openium.consentium_ui.ui.model
|
package fr.openium.consentium_ui.ui.model
|
||||||
|
|
||||||
internal data class DetailConsentUI(
|
internal data class DetailConsentUI(
|
||||||
|
val cookiePolicyUrl: String,
|
||||||
val conservationDurationText: String,
|
val conservationDurationText: String,
|
||||||
val purposes: List<PurposeUI>,
|
val purposes: List<PurposeUI>,
|
||||||
)
|
)
|
@ -2,7 +2,7 @@ package fr.openium.consentium_ui.ui.model
|
|||||||
|
|
||||||
internal data class GeneralConsentUI(
|
internal data class GeneralConsentUI(
|
||||||
val applicationName: String,
|
val applicationName: String,
|
||||||
val iconUrl: String,
|
val iconUrl: String?,
|
||||||
val mainConsentText: String,
|
val mainConsentText: String,
|
||||||
val consentPageUrl: String,
|
val consentPageUrl: String,
|
||||||
)
|
)
|
@ -3,5 +3,5 @@ package fr.openium.consentium_ui.ui.model
|
|||||||
internal enum class PurposeStatusUI {
|
internal enum class PurposeStatusUI {
|
||||||
ACCEPTED,
|
ACCEPTED,
|
||||||
REJECTED,
|
REJECTED,
|
||||||
NOT_DEFINED,
|
PARTIAL,
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ package fr.openium.consentium_ui.ui.model
|
|||||||
internal data class PurposeUI(
|
internal data class PurposeUI(
|
||||||
val id: String,
|
val id: String,
|
||||||
val isRequired: Boolean,
|
val isRequired: Boolean,
|
||||||
var isAccepted: PurposeStatusUI,
|
var choice: PurposeStatusUI,
|
||||||
val title: String,
|
val title: String,
|
||||||
val description: String,
|
val description: String,
|
||||||
val vendors: List<VendorUI>,
|
val vendors: List<VendorUI>,
|
||||||
|
@ -1,54 +1,17 @@
|
|||||||
package fr.openium.consentium_ui.ui.utils
|
package fr.openium.consentium_ui.ui.utils
|
||||||
|
|
||||||
import android.text.ParcelableSpan
|
import androidx.compose.runtime.Composable
|
||||||
import android.text.Spanned
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import com.mohamedrejeb.richeditor.model.rememberRichTextState
|
||||||
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 {
|
@Composable
|
||||||
val spanned: Spanned = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
fun String.toRichHtmlString(): AnnotatedString {
|
||||||
return buildAnnotatedString {
|
val state = rememberRichTextState()
|
||||||
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
|
LaunchedEffect(this) {
|
||||||
if (start < startSpan) {
|
state.setHtml(this@toRichHtmlString)
|
||||||
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 state.annotatedString
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
14
consentium-ui/src/main/res/values-fr/strings.xml
Normal file
14
consentium-ui/src/main/res/values-fr/strings.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_icon">Icone de l\'application</string>
|
||||||
|
<string name="back">Retour</string>
|
||||||
|
<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="retry">Réessayer</string>
|
||||||
|
<string name="save_consents_error_message">Une erreur est survenue l\'or de la sauvegarde de vos consentements.</string>
|
||||||
|
<string name="consents_error_loading_consents">Il y a eu une erreur lors du chargement de vos consentements</string>
|
||||||
|
</resources>
|
@ -1,10 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="cookies">Politique de cookies</string>
|
<string name="app_icon">Application icon</string>
|
||||||
<string name="accept">Accepter et fermer</string>
|
<string name="back">Back</string>
|
||||||
<string name="refuse">Continuer sans accepter</string>
|
<string name="cookies">Cookie policy</string>
|
||||||
<string name="parameters">Paramétrer mes choix</string>
|
<string name="accept">Accept and close</string>
|
||||||
<string name="save">Enregistrer et fermer</string>
|
<string name="refuse">Continue without accepting</string>
|
||||||
<string name="require">Requis</string>
|
<string name="parameters">Set my choices</string>
|
||||||
<string name="save_consents_error_message">Une erreur est survenue l\'or de la ssauvegarde de vos consentements.</string>
|
<string name="save">Save and close</string>
|
||||||
|
<string name="require">Required</string>
|
||||||
|
<string name="retry">Try again</string>
|
||||||
|
<string name="save_consents_error_message">An error occurred while saving your consents.</string>
|
||||||
|
<string name="consents_error_loading_consents">There was an error uploading your consents</string>
|
||||||
</resources>
|
</resources>
|
@ -20,11 +20,14 @@ android {
|
|||||||
buildTypes {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||||
|
consumerProguardFiles("proguard-rules.pro")
|
||||||
}
|
}
|
||||||
|
|
||||||
release {
|
release {
|
||||||
isMinifyEnabled = true
|
isMinifyEnabled = true
|
||||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||||
|
consumerProguardFiles("proguard-rules.pro")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,6 +37,10 @@ android {
|
|||||||
dimension = "version"
|
dimension = "version"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create("dev") {
|
||||||
|
dimension = "version"
|
||||||
|
}
|
||||||
|
|
||||||
create("demo") {
|
create("demo") {
|
||||||
dimension = "version"
|
dimension = "version"
|
||||||
}
|
}
|
||||||
|
40
consentium/proguard-rules.pro
vendored
40
consentium/proguard-rules.pro
vendored
@ -18,4 +18,42 @@
|
|||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
# If you keep the line number information, uncomment this to
|
||||||
# hide the original source file name.
|
# hide the original source file name.
|
||||||
#-renamesourcefileattribute SourceFile
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
|
-dontwarn java.lang.invoke.StringConcatFactory
|
||||||
|
|
||||||
|
-keepattributes SourceFile,LineNumberTable
|
||||||
|
-keep public class * extends java.lang.Exception
|
||||||
|
|
||||||
|
-keep class com.google.firebase.crashlytics.** { *; }
|
||||||
|
-dontwarn com.google.firebase.crashlytics.**
|
||||||
|
|
||||||
|
# Keep enums
|
||||||
|
-keep public enum fr.openium.consentium.**{
|
||||||
|
*;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Garder les annotations de Kotlin
|
||||||
|
-keepattributes *Annotation*
|
||||||
|
|
||||||
|
# Garder les classes et les membres annotés avec @Keep
|
||||||
|
-keep @androidx.annotation.Keep class * { *; }
|
||||||
|
-keepclassmembers class ** {
|
||||||
|
@androidx.annotation.Keep *;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Garder les classes et les membres annotés avec @Serializable
|
||||||
|
-keep @kotlinx.serialization.Serializable class * { *; }
|
||||||
|
-keepclassmembers class ** {
|
||||||
|
@kotlinx.serialization.Serializable *;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Garder les classes annotées avec @HiltAndroidApp
|
||||||
|
-keep @dagger.hilt.android.HiltAndroidApp class * { *; }
|
||||||
|
|
||||||
|
# Garder les classes et les membres pour Timber
|
||||||
|
-keep class timber.log.Timber { *; }
|
||||||
|
-keep interface timber.log.Timber$Tree { *; }
|
||||||
|
|
||||||
|
-keep class fr.openium.consentium.** { *; }
|
||||||
|
-keepclassmembers class fr.openium.consentium.** { *; }
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
package fr.openium.consentium
|
package fr.openium.consentium
|
||||||
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
import org.junit.Assert.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instrumented test, which will execute on an Android device.
|
* Instrumented test, which will execute on an Android device.
|
||||||
*
|
*
|
||||||
@ -17,8 +14,6 @@ import org.junit.Assert.*
|
|||||||
class ExampleInstrumentedTest {
|
class ExampleInstrumentedTest {
|
||||||
@Test
|
@Test
|
||||||
fun useAppContext() {
|
fun useAppContext() {
|
||||||
// Context of the app under test.
|
assertTrue(true)
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
|
||||||
assertEquals("fr.openium.consentium.test", appContext.packageName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package fr.openium.consentium
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import fr.openium.consentium.data.di.ConsentiumUrl
|
||||||
|
import fr.openium.consentium.data.di.NetworkModule
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
class DevNetworkModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideOkHttpBuilder(
|
||||||
|
@ApplicationContext context: Context,
|
||||||
|
): OkHttpClient.Builder =
|
||||||
|
NetworkModule.standardOkHttpBuilder(context)
|
||||||
|
.addNetworkInterceptor(HttpLoggingInterceptor { message ->
|
||||||
|
Timber.tag("OkHttp")
|
||||||
|
Timber.v(message)
|
||||||
|
}.apply {
|
||||||
|
level = HttpLoggingInterceptor.Level.BODY
|
||||||
|
})
|
||||||
|
|
||||||
|
@ConsentiumUrl
|
||||||
|
@Provides
|
||||||
|
fun okHttpUrlConsentium(@ApplicationContext context: Context): HttpUrl =
|
||||||
|
"https://www.exemple.com".toHttpUrl()
|
||||||
|
}
|
@ -16,7 +16,7 @@ import timber.log.Timber
|
|||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
class ConsentiumDebugNetworkModule {
|
class DevNetworkModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun provideOkHttpBuilder(
|
fun provideOkHttpBuilder(
|
4
consentium/src/dev/res/values/strings.xml
Normal file
4
consentium/src/dev/res/values/strings.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="backend_url" translatable="false">https://consentium-api-dev.openium.fr/api/v1/app/</string>
|
||||||
|
</resources>
|
@ -1,4 +1,6 @@
|
|||||||
<?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>
|
@ -2,7 +2,10 @@ 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.ConsentState
|
||||||
|
import fr.openium.consentium.api.model.Purpose
|
||||||
import fr.openium.consentium.api.model.PurposeChoice
|
import fr.openium.consentium.api.model.PurposeChoice
|
||||||
|
import fr.openium.consentium.api.model.PurposeStatus
|
||||||
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
|
||||||
@ -14,7 +17,8 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||||||
|
|
||||||
class Consentium(
|
class Consentium(
|
||||||
context: Context,
|
context: Context,
|
||||||
val applicationId: String,
|
val apiKey: String,
|
||||||
|
val appId: String,
|
||||||
) {
|
) {
|
||||||
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() }
|
||||||
@ -31,11 +35,11 @@ class Consentium(
|
|||||||
suspend fun fetchConsents() {
|
suspend fun fetchConsents() {
|
||||||
_fetchConsentState.value = FetchConsentiumState.Loading
|
_fetchConsentState.value = FetchConsentiumState.Loading
|
||||||
try {
|
try {
|
||||||
when (val consentResponse = consentiumRepository.getConsents(applicationId)) {
|
when (val consentResponse = consentiumRepository.getConsents(apiKey)) {
|
||||||
ConsentiumRepositoryGetResponse.Error -> _fetchConsentState.value = FetchConsentiumState.Error
|
ConsentiumRepositoryGetResponse.Error -> _fetchConsentState.value = FetchConsentiumState.Error
|
||||||
|
|
||||||
is ConsentiumRepositoryGetResponse.GetConsentsSuccess -> {
|
is ConsentiumRepositoryGetResponse.GetConsentsSuccess -> {
|
||||||
val areConsentsValid = consentResponse.isValid
|
val areConsentsValid = consentResponse.state == ConsentState.VALID
|
||||||
if (areConsentsValid) {
|
if (areConsentsValid) {
|
||||||
_fetchConsentState.value = FetchConsentiumState.Valid(purposes = consentResponse.purposes)
|
_fetchConsentState.value = FetchConsentiumState.Valid(purposes = consentResponse.purposes)
|
||||||
} else {
|
} else {
|
||||||
@ -53,7 +57,7 @@ class Consentium(
|
|||||||
) {
|
) {
|
||||||
_saveConsentState.emit(SetConsentiumState.Loading)
|
_saveConsentState.emit(SetConsentiumState.Loading)
|
||||||
try {
|
try {
|
||||||
when (consentiumRepository.setConsents(applicationId, consent)) {
|
when (consentiumRepository.setConsents(apiKey, consent)) {
|
||||||
ConsentiumRepositorySetResponse.Error -> {
|
ConsentiumRepositorySetResponse.Error -> {
|
||||||
_saveConsentState.emit(SetConsentiumState.Error)
|
_saveConsentState.emit(SetConsentiumState.Error)
|
||||||
}
|
}
|
||||||
@ -66,4 +70,12 @@ class Consentium(
|
|||||||
_saveConsentState.emit(SetConsentiumState.Error)
|
_saveConsentState.emit(SetConsentiumState.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<Purpose>.checkPurposeState(purposeId: String, onPurposeState: (PurposeStatus) -> Unit) {
|
||||||
|
onPurposeState(find { it.identifier == purposeId }?.choice ?: PurposeStatus.REJECTED)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<Purpose>.checkPurposeState(purposeId: String): PurposeStatus {
|
||||||
|
return find { it.identifier == purposeId }?.choice ?: PurposeStatus.REJECTED
|
||||||
}
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package fr.openium.consentium.api.adapter
|
||||||
|
|
||||||
|
import fr.openium.consentium.api.model.ConsentState
|
||||||
|
import fr.openium.consentium.data.remote.model.GetConsent
|
||||||
|
|
||||||
|
internal fun GetConsent.ConsentStateDTO.toConsentState(): ConsentState = when (this) {
|
||||||
|
GetConsent.ConsentStateDTO.UNSET -> ConsentState.UNSET
|
||||||
|
GetConsent.ConsentStateDTO.VALID -> ConsentState.VALID
|
||||||
|
GetConsent.ConsentStateDTO.EXPIRED -> ConsentState.EXPIRED
|
||||||
|
GetConsent.ConsentStateDTO.NEW_VERSION -> ConsentState.NEW_VERSION
|
||||||
|
}
|
@ -5,7 +5,6 @@ import fr.openium.consentium.data.remote.model.GetConsent
|
|||||||
|
|
||||||
internal fun GetConsent.PurposeDTO.toPurpose() = Purpose(
|
internal fun GetConsent.PurposeDTO.toPurpose() = Purpose(
|
||||||
identifier = identifier,
|
identifier = identifier,
|
||||||
isRequired = isRequired,
|
choice = choice.toPurposeStatus(),
|
||||||
isAccepted = isAccepted.toPurposeStatus(),
|
|
||||||
vendors = vendors?.map { it.toVendor() } ?: emptyList(),
|
vendors = vendors?.map { it.toVendor() } ?: emptyList(),
|
||||||
)
|
)
|
||||||
|
@ -4,7 +4,6 @@ import fr.openium.consentium.api.model.PurposeChoice
|
|||||||
import fr.openium.consentium.data.remote.model.PatchConsent
|
import fr.openium.consentium.data.remote.model.PatchConsent
|
||||||
|
|
||||||
internal fun PurposeChoice.toPatchConsentPurposeDTO() = PatchConsent.PurposeDTO(
|
internal fun PurposeChoice.toPatchConsentPurposeDTO() = PatchConsent.PurposeDTO(
|
||||||
identifier = purposeIdentifier.toString(),
|
identifier = purposeIdentifier,
|
||||||
isAccepted = isAccepted,
|
choice = choice.toPurposeStatusDTO(),
|
||||||
vendors = vendors.map { it.toPatchConsentVendorDTO() }
|
|
||||||
)
|
)
|
@ -2,9 +2,16 @@ package fr.openium.consentium.api.adapter
|
|||||||
|
|
||||||
import fr.openium.consentium.api.model.PurposeStatus
|
import fr.openium.consentium.api.model.PurposeStatus
|
||||||
import fr.openium.consentium.data.remote.model.GetConsent
|
import fr.openium.consentium.data.remote.model.GetConsent
|
||||||
|
import fr.openium.consentium.data.remote.model.PatchConsent
|
||||||
|
|
||||||
internal fun GetConsent.PurposeStatusDTO.toPurposeStatus() = when (this) {
|
internal fun GetConsent.PurposeStatusDTO.toPurposeStatus() = when (this) {
|
||||||
GetConsent.PurposeStatusDTO.ACCEPTED -> PurposeStatus.ACCEPTED
|
GetConsent.PurposeStatusDTO.ACCEPTED -> PurposeStatus.ACCEPTED
|
||||||
GetConsent.PurposeStatusDTO.REJECTED -> PurposeStatus.REJECTED
|
GetConsent.PurposeStatusDTO.REJECTED -> PurposeStatus.REJECTED
|
||||||
GetConsent.PurposeStatusDTO.NOT_DEFINED -> PurposeStatus.NOT_DEFINED
|
GetConsent.PurposeStatusDTO.PARTIAL -> PurposeStatus.PARTIAL
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun PurposeStatus.toPurposeStatusDTO() = when (this) {
|
||||||
|
PurposeStatus.ACCEPTED -> PatchConsent.PurposeStatusDTO.ACCEPTED
|
||||||
|
PurposeStatus.REJECTED -> PatchConsent.PurposeStatusDTO.REJECTED
|
||||||
|
PurposeStatus.PARTIAL -> PatchConsent.PurposeStatusDTO.PARTIAL
|
||||||
}
|
}
|
@ -4,6 +4,6 @@ import fr.openium.consentium.api.model.VendorChoice
|
|||||||
import fr.openium.consentium.data.remote.model.PatchConsent
|
import fr.openium.consentium.data.remote.model.PatchConsent
|
||||||
|
|
||||||
internal fun VendorChoice.toPatchConsentVendorDTO() = PatchConsent.VendorDTO(
|
internal fun VendorChoice.toPatchConsentVendorDTO() = PatchConsent.VendorDTO(
|
||||||
identifier = vendorIdentifier.toString(),
|
identifier = vendorIdentifier,
|
||||||
isAccepted = isAccepted,
|
isAccepted = isAccepted,
|
||||||
)
|
)
|
@ -0,0 +1,8 @@
|
|||||||
|
package fr.openium.consentium.api.model
|
||||||
|
|
||||||
|
enum class ConsentState {
|
||||||
|
UNSET,
|
||||||
|
VALID,
|
||||||
|
EXPIRED,
|
||||||
|
NEW_VERSION,
|
||||||
|
}
|
@ -2,7 +2,6 @@ package fr.openium.consentium.api.model
|
|||||||
|
|
||||||
data class Purpose(
|
data class Purpose(
|
||||||
val identifier: String,
|
val identifier: String,
|
||||||
val isRequired: Boolean,
|
val choice: PurposeStatus,
|
||||||
val isAccepted: PurposeStatus,
|
|
||||||
val vendors: List<Vendor>,
|
val vendors: List<Vendor>,
|
||||||
)
|
)
|
@ -2,6 +2,6 @@ package fr.openium.consentium.api.model
|
|||||||
|
|
||||||
data class PurposeChoice(
|
data class PurposeChoice(
|
||||||
val purposeIdentifier: String,
|
val purposeIdentifier: String,
|
||||||
val isAccepted: Boolean,
|
val choice: PurposeStatus,
|
||||||
val vendors: List<VendorChoice>,
|
val vendors: List<VendorChoice>,
|
||||||
)
|
)
|
@ -3,6 +3,5 @@ package fr.openium.consentium.api.model
|
|||||||
enum class PurposeStatus {
|
enum class PurposeStatus {
|
||||||
ACCEPTED,
|
ACCEPTED,
|
||||||
REJECTED,
|
REJECTED,
|
||||||
NOT_DEFINED,
|
PARTIAL,
|
||||||
UNKNOWN,
|
|
||||||
}
|
}
|
@ -5,21 +5,21 @@ import fr.openium.consentium.data.remote.model.PatchConsent
|
|||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.PATCH
|
import retrofit2.http.Header
|
||||||
|
import retrofit2.http.POST
|
||||||
|
|
||||||
|
|
||||||
internal interface ConsentiumApi {
|
internal interface ConsentiumApi {
|
||||||
|
|
||||||
@GET("/consents")
|
@GET("consents")
|
||||||
suspend fun getConsents(
|
suspend fun getConsents(
|
||||||
applicationId: String,
|
@Header("Authorization") token: String,
|
||||||
installationId: String,
|
|
||||||
): Response<GetConsent.GetConsentPayloadDTO>
|
): Response<GetConsent.GetConsentPayloadDTO>
|
||||||
|
|
||||||
@PATCH("/consents")
|
@POST("consents")
|
||||||
suspend fun setConsents(
|
suspend fun setConsents(
|
||||||
applicationId: String,
|
@Header("Authorization") token: String,
|
||||||
@Body patchConsent: PatchConsent.PatchConsentPayloadDTO,
|
@Body patchConsent: PatchConsent.PatchConsentPayloadDTO,
|
||||||
): Response<Any>
|
): Response<Unit>
|
||||||
|
|
||||||
}
|
}
|
@ -10,41 +10,29 @@ import java.util.UUID
|
|||||||
internal object ConsentiumMockApi : ConsentiumApi {
|
internal object ConsentiumMockApi : ConsentiumApi {
|
||||||
|
|
||||||
private val consents = GetConsent.GetConsentPayloadDTO(
|
private val consents = GetConsent.GetConsentPayloadDTO(
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
installationId = UUID.randomUUID().toString(),
|
installationId = UUID.randomUUID().toString(),
|
||||||
purposes = listOf(
|
purposes = listOf(
|
||||||
GetConsent.PurposeDTO(
|
GetConsent.PurposeDTO(
|
||||||
identifier = "purpose-audience",
|
identifier = "purpose-audience",
|
||||||
isRequired = true,
|
vendors = null,
|
||||||
isAccepted = GetConsent.PurposeStatusDTO.ACCEPTED,
|
choice = GetConsent.PurposeStatusDTO.ACCEPTED,
|
||||||
vendors = listOf(
|
|
||||||
GetConsent.VendorDTO(
|
|
||||||
identifier = "vendor-clarity",
|
|
||||||
isAccepted = true,
|
|
||||||
isRequired = true,
|
|
||||||
),
|
|
||||||
GetConsent.VendorDTO(
|
|
||||||
identifier = "vendor-matomo",
|
|
||||||
isAccepted = true,
|
|
||||||
isRequired = false,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
GetConsent.PurposeDTO(
|
GetConsent.PurposeDTO(
|
||||||
identifier = "purpose-required",
|
identifier = "purpose-required",
|
||||||
isRequired = true,
|
vendors = null,
|
||||||
isAccepted = GetConsent.PurposeStatusDTO.REJECTED,
|
choice = GetConsent.PurposeStatusDTO.REJECTED,
|
||||||
vendors = null
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
isValid = false,
|
state = GetConsent.ConsentStateDTO.VALID,
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun getConsents(applicationId: String, installationId: String): Response<GetConsent.GetConsentPayloadDTO> {
|
override suspend fun getConsents(token: String): Response<GetConsent.GetConsentPayloadDTO> {
|
||||||
delay(2000)
|
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(token: String, patchConsent: PatchConsent.PatchConsentPayloadDTO): Response<Unit> {
|
||||||
delay(2000)
|
delay(2000)
|
||||||
return Response.success(Unit)
|
return Response.success(Unit)
|
||||||
}
|
}
|
||||||
|
@ -7,16 +7,17 @@ internal sealed interface GetConsent {
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class GetConsentPayloadDTO(
|
data class GetConsentPayloadDTO(
|
||||||
|
@SerialName("id") val id: String? = null,
|
||||||
@SerialName("installationId") val installationId: String,
|
@SerialName("installationId") val installationId: String,
|
||||||
|
@SerialName("state") val state: ConsentStateDTO? = null,
|
||||||
|
@SerialName("acceptedDate") val acceptedData: Long? = null,
|
||||||
@SerialName("purposes") val purposes: List<PurposeDTO>? = null,
|
@SerialName("purposes") val purposes: List<PurposeDTO>? = null,
|
||||||
@SerialName("isValid") val isValid: Boolean,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PurposeDTO(
|
data class PurposeDTO(
|
||||||
@SerialName("identifier") val identifier: String,
|
@SerialName("identifier") val identifier: String,
|
||||||
@SerialName("isRequired") val isRequired: Boolean,
|
@SerialName("choice") val choice: PurposeStatusDTO,
|
||||||
@SerialName("isAccepted") val isAccepted: PurposeStatusDTO,
|
|
||||||
@SerialName("vendors") val vendors: List<VendorDTO>? = null,
|
@SerialName("vendors") val vendors: List<VendorDTO>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,16 +30,30 @@ internal sealed interface GetConsent {
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
enum class PurposeStatusDTO {
|
enum class PurposeStatusDTO {
|
||||||
@SerialName("ACCEPTED")
|
@SerialName("accepted")
|
||||||
ACCEPTED,
|
ACCEPTED,
|
||||||
|
|
||||||
@SerialName("REJECTED")
|
@SerialName("refused")
|
||||||
REJECTED,
|
REJECTED,
|
||||||
|
|
||||||
@SerialName("NOT_DEFINED")
|
@SerialName("partial")
|
||||||
NOT_DEFINED,
|
PARTIAL,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class ConsentStateDTO {
|
||||||
|
@SerialName("unset")
|
||||||
|
UNSET,
|
||||||
|
|
||||||
|
@SerialName("valid")
|
||||||
|
VALID,
|
||||||
|
|
||||||
|
@SerialName("expired")
|
||||||
|
EXPIRED,
|
||||||
|
|
||||||
|
@SerialName("new_version")
|
||||||
|
NEW_VERSION,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,15 +7,13 @@ internal sealed interface PatchConsent {
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PatchConsentPayloadDTO(
|
data class PatchConsentPayloadDTO(
|
||||||
@SerialName("installationId") val installationId: String,
|
@SerialName("consentPurposes") val purposes: List<PurposeDTO>? = null,
|
||||||
@SerialName("purposes") val purposes: List<PurposeDTO>? = null,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PurposeDTO(
|
data class PurposeDTO(
|
||||||
@SerialName("identifier") val identifier: String,
|
@SerialName("identifier") val identifier: String,
|
||||||
@SerialName("isAccepted") val isAccepted: Boolean,
|
@SerialName("choice") val choice: PurposeStatusDTO,
|
||||||
@SerialName("vendors") val vendors: List<VendorDTO>? = null,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -24,4 +22,16 @@ internal sealed interface PatchConsent {
|
|||||||
@SerialName("isAccepted") val isAccepted: Boolean,
|
@SerialName("isAccepted") val isAccepted: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class PurposeStatusDTO {
|
||||||
|
@SerialName("accepted")
|
||||||
|
ACCEPTED,
|
||||||
|
|
||||||
|
@SerialName("refused")
|
||||||
|
REJECTED,
|
||||||
|
|
||||||
|
@SerialName("partial")
|
||||||
|
PARTIAL,
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,26 @@
|
|||||||
package fr.openium.consentium.domain.repository
|
package fr.openium.consentium.domain.repository
|
||||||
|
|
||||||
|
import fr.openium.consentium.api.adapter.toConsentState
|
||||||
import fr.openium.consentium.api.adapter.toPatchConsentPurposeDTO
|
import fr.openium.consentium.api.adapter.toPatchConsentPurposeDTO
|
||||||
import fr.openium.consentium.api.adapter.toPurpose
|
import fr.openium.consentium.api.adapter.toPurpose
|
||||||
|
import fr.openium.consentium.api.model.ConsentState
|
||||||
import fr.openium.consentium.api.model.Purpose
|
import fr.openium.consentium.api.model.Purpose
|
||||||
import fr.openium.consentium.api.model.PurposeChoice
|
import fr.openium.consentium.api.model.PurposeChoice
|
||||||
import fr.openium.consentium.data.local.ConsentiumDataStore
|
|
||||||
import fr.openium.consentium.data.remote.ConsentiumApi
|
import fr.openium.consentium.data.remote.ConsentiumApi
|
||||||
import fr.openium.consentium.data.remote.model.PatchConsent
|
import fr.openium.consentium.data.remote.model.PatchConsent
|
||||||
|
import fr.openium.consentium.domain.useCase.GetAuthTokenUseCase
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class ConsentiumRepository @Inject constructor(
|
internal class ConsentiumRepository @Inject constructor(
|
||||||
private val consentiumApi: ConsentiumApi,
|
private val consentiumApi: ConsentiumApi,
|
||||||
private val consentiumDataStore: ConsentiumDataStore,
|
private val getAuthTokenUseCase: GetAuthTokenUseCase,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun getConsents(applicationId: String): ConsentiumRepositoryGetResponse {
|
suspend fun getConsents(apiKey: String): ConsentiumRepositoryGetResponse {
|
||||||
val installationId = consentiumDataStore.getInstallationUniqueId()
|
|
||||||
val consentsResponse = consentiumApi.getConsents(applicationId, installationId)
|
|
||||||
return try {
|
return try {
|
||||||
|
val authToken = getAuthTokenUseCase(apiKey)
|
||||||
|
val consentsResponse = consentiumApi.getConsents(authToken)
|
||||||
val consentsBody = if (consentsResponse.isSuccessful) {
|
val consentsBody = if (consentsResponse.isSuccessful) {
|
||||||
consentsResponse.body() ?: throw Exception()
|
consentsResponse.body() ?: throw Exception()
|
||||||
} else {
|
} else {
|
||||||
@ -25,7 +28,7 @@ internal class ConsentiumRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ConsentiumRepositoryGetResponse.GetConsentsSuccess(
|
return ConsentiumRepositoryGetResponse.GetConsentsSuccess(
|
||||||
isValid = consentsBody.isValid,
|
state = consentsBody.state?.toConsentState() ?: ConsentState.UNSET,
|
||||||
purposes = consentsBody.purposes?.map { purpose -> purpose.toPurpose() } ?: emptyList()
|
purposes = consentsBody.purposes?.map { purpose -> purpose.toPurpose() } ?: emptyList()
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -34,24 +37,28 @@ internal class ConsentiumRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun setConsents(
|
suspend fun setConsents(
|
||||||
applicationId: String,
|
apiKey: String,
|
||||||
consents: List<PurposeChoice>,
|
consents: List<PurposeChoice>,
|
||||||
): ConsentiumRepositorySetResponse {
|
): ConsentiumRepositorySetResponse {
|
||||||
val installationId = consentiumDataStore.getInstallationUniqueId()
|
return try {
|
||||||
|
val authToken = getAuthTokenUseCase(apiKey)
|
||||||
val setConsentResponse = consentiumApi.setConsents(
|
val setConsentResponse = consentiumApi.setConsents(
|
||||||
applicationId = applicationId,
|
token = authToken,
|
||||||
patchConsent = PatchConsent.PatchConsentPayloadDTO(
|
patchConsent = PatchConsent.PatchConsentPayloadDTO(
|
||||||
installationId = installationId,
|
purposes = consents.map { it.toPatchConsentPurposeDTO() },
|
||||||
purposes = consents.map { it.toPatchConsentPurposeDTO() },
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
return if (setConsentResponse.isSuccessful) {
|
if (setConsentResponse.isSuccessful) {
|
||||||
ConsentiumRepositorySetResponse.SetConsentsSuccess
|
ConsentiumRepositorySetResponse.SetConsentsSuccess
|
||||||
} else {
|
} else {
|
||||||
|
ConsentiumRepositorySetResponse.Error
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.d(e)
|
||||||
ConsentiumRepositorySetResponse.Error
|
ConsentiumRepositorySetResponse.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -61,7 +68,7 @@ internal interface ConsentiumRepositoryGetResponse {
|
|||||||
data object Error : ConsentiumRepositoryGetResponse
|
data object Error : ConsentiumRepositoryGetResponse
|
||||||
|
|
||||||
data class GetConsentsSuccess(
|
data class GetConsentsSuccess(
|
||||||
val isValid: Boolean,
|
val state: ConsentState,
|
||||||
val purposes: List<Purpose>,
|
val purposes: List<Purpose>,
|
||||||
) : ConsentiumRepositoryGetResponse
|
) : ConsentiumRepositoryGetResponse
|
||||||
|
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package fr.openium.consentium.domain.useCase
|
||||||
|
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.io.encoding.Base64
|
||||||
|
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||||
|
|
||||||
|
interface GetAuthTokenUseCase {
|
||||||
|
suspend operator fun invoke(apiKey: String): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class GetAuthTokenUseCaseImpl @Inject internal constructor(
|
||||||
|
private val getConsentiumUniqueInstallationIdUseCase: GetConsentiumUniqueInstallationIdUseCase,
|
||||||
|
) : GetAuthTokenUseCase {
|
||||||
|
|
||||||
|
@OptIn(ExperimentalEncodingApi::class)
|
||||||
|
override suspend operator fun invoke(apiKey: String): String {
|
||||||
|
val uniqueInstallationId = getConsentiumUniqueInstallationIdUseCase()
|
||||||
|
val clearToken = "$apiKey.$uniqueInstallationId"
|
||||||
|
val cipheredToken = Base64.Default.encode(clearToken.toByteArray())
|
||||||
|
return "Basic $cipheredToken"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,14 +4,14 @@ import fr.openium.consentium.data.local.ConsentiumDataStore
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
interface GetConsentiumUniqueInstallationIdUseCase {
|
interface GetConsentiumUniqueInstallationIdUseCase {
|
||||||
suspend fun invoke(): String
|
suspend operator fun invoke(): String
|
||||||
}
|
}
|
||||||
|
|
||||||
class GetConsentiumUniqueInstallationIdUseCaseImpl @Inject internal constructor(
|
class GetConsentiumUniqueInstallationIdUseCaseImpl @Inject internal constructor(
|
||||||
private val consentiumDataStore: ConsentiumDataStore,
|
private val consentiumDataStore: ConsentiumDataStore,
|
||||||
) : GetConsentiumUniqueInstallationIdUseCase {
|
) : GetConsentiumUniqueInstallationIdUseCase {
|
||||||
|
|
||||||
override suspend fun invoke(): String {
|
override suspend operator fun invoke(): String {
|
||||||
return consentiumDataStore.getInstallationUniqueId()
|
return consentiumDataStore.getInstallationUniqueId()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.domain.useCase.GetAuthTokenUseCase
|
||||||
|
import fr.openium.consentium.domain.useCase.GetAuthTokenUseCaseImpl
|
||||||
import fr.openium.consentium.domain.useCase.GetConsentiumUniqueInstallationIdUseCase
|
import fr.openium.consentium.domain.useCase.GetConsentiumUniqueInstallationIdUseCase
|
||||||
import fr.openium.consentium.domain.useCase.GetConsentiumUniqueInstallationIdUseCaseImpl
|
import fr.openium.consentium.domain.useCase.GetConsentiumUniqueInstallationIdUseCaseImpl
|
||||||
|
|
||||||
@ -16,4 +18,8 @@ interface ConsentiumUseCaseModule {
|
|||||||
getUniqueInstallationIdUseCaseImpl: GetConsentiumUniqueInstallationIdUseCaseImpl,
|
getUniqueInstallationIdUseCaseImpl: GetConsentiumUniqueInstallationIdUseCaseImpl,
|
||||||
): GetConsentiumUniqueInstallationIdUseCase
|
): GetConsentiumUniqueInstallationIdUseCase
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
fun bindGetAuthTokenUseCase(
|
||||||
|
getAuthTokenUseCaseImpl: GetAuthTokenUseCaseImpl,
|
||||||
|
): GetAuthTokenUseCase
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package fr.openium.consentium
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import fr.openium.consentium.data.di.ConsentiumUrl
|
||||||
|
import fr.openium.consentium.data.di.NetworkModule
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
object ReleaseNetworkModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideOkHttpBuilder(@ApplicationContext context: Context): OkHttpClient.Builder {
|
||||||
|
return NetworkModule.standardOkHttpBuilder(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConsentiumUrl
|
||||||
|
@Provides
|
||||||
|
fun okHttpUrlConsentium(@ApplicationContext context: Context): HttpUrl =
|
||||||
|
context.getString(R.string.backend_url).toHttpUrl()
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="backend_url" translatable="false" tools:ignore="UnusedResources">https://consentium-api-dev.openium.fr/api/v1/app</string>
|
<string name="backend_url" translatable="false" tools:ignore="UnusedResources">https://consentium-api.openium.fr/api/v1/app/</string>
|
||||||
</resources>
|
</resources>
|
@ -23,9 +23,6 @@ composeBom = "2024.11.00"
|
|||||||
# Timber
|
# Timber
|
||||||
timber = "5.0.1"
|
timber = "5.0.1"
|
||||||
|
|
||||||
# Material
|
|
||||||
material = "1.12.0"
|
|
||||||
|
|
||||||
# Serialization
|
# Serialization
|
||||||
serialization = "2.0.0"
|
serialization = "2.0.0"
|
||||||
jsonSerialization = "1.7.1"
|
jsonSerialization = "1.7.1"
|
||||||
@ -46,7 +43,7 @@ junitExtVersion = "1.2.1"
|
|||||||
preferencesDataStore = "1.1.1"
|
preferencesDataStore = "1.1.1"
|
||||||
|
|
||||||
# Plugins
|
# Plugins
|
||||||
agp = "8.7.3"
|
agp = "8.8.1"
|
||||||
kotlin = "2.0.0"
|
kotlin = "2.0.0"
|
||||||
ksp = "2.0.0-1.0.24"
|
ksp = "2.0.0-1.0.24"
|
||||||
junitVersion = "1.2.1"
|
junitVersion = "1.2.1"
|
||||||
@ -68,27 +65,31 @@ clarityVersion = "1.3.2"
|
|||||||
# Coil
|
# Coil
|
||||||
coil = "3.0.4"
|
coil = "3.0.4"
|
||||||
|
|
||||||
|
|
||||||
# GA4
|
# GA4
|
||||||
ga4 = "22.1.2"
|
ga4 = "22.1.2"
|
||||||
runtimeAndroid = "1.7.6"
|
runtimeAndroid = "1.7.6"
|
||||||
foundationAndroid = "1.7.6"
|
foundationAndroid = "1.7.6"
|
||||||
foundationLayoutAndroid = "1.7.5"
|
foundationLayoutAndroid = "1.7.5"
|
||||||
uiAndroid = "1.7.5"
|
uiAndroid = "1.7.5"
|
||||||
uiToolingPreviewAndroid = "1.7.6"
|
|
||||||
material3Android = "1.3.1"
|
material3Android = "1.3.1"
|
||||||
|
|
||||||
|
# Publish
|
||||||
|
publish = "1.2"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|
||||||
# AndroidX
|
# AndroidX
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||||
|
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" }
|
||||||
|
|
||||||
# Lifecycle
|
# Lifecycle
|
||||||
androidx-lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
androidx-lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
||||||
androidx-lifecycle-viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
|
androidx-lifecycle-viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
|
||||||
androidx-lifecycle-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
|
|
||||||
|
|
||||||
# Hilt
|
# Hilt
|
||||||
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
|
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
|
||||||
@ -100,22 +101,18 @@ timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
|
|||||||
|
|
||||||
# Compose
|
# Compose
|
||||||
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||||
compose-ui = { module = "androidx.compose.ui:ui" }
|
compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||||
compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" }
|
compose-ui-graphics = { group = "androidx.compose.ui", name = "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" }
|
|
||||||
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
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" }
|
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
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" }
|
||||||
|
|
||||||
# Compose navigation
|
# Compose navigation
|
||||||
androidx-navigation-common-ktx = { group = "androidx.navigation", name = "navigation-common-ktx", version.ref = "navigationCompose" }
|
|
||||||
androidx-navigation-runtime-ktx = { group = "androidx.navigation", name = "navigation-runtime-ktx", version.ref = "navigationCompose" }
|
|
||||||
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
|
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
|
||||||
|
|
||||||
# Preferences DataStore
|
# Preferences DataStore
|
||||||
@ -142,18 +139,19 @@ matomo = { module = "com.github.matomo-org:matomo-sdk-android", version.ref = "m
|
|||||||
# Clarity
|
# Clarity
|
||||||
clarity = { group = "com.microsoft.clarity", name = "clarity", version.ref = "clarityVersion" }
|
clarity = { group = "com.microsoft.clarity", name = "clarity", version.ref = "clarityVersion" }
|
||||||
|
|
||||||
# GA4 (Firebase Analytics)
|
# GA4
|
||||||
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
|
||||||
coil = { module = "io.coil-kt.coil3:coil-compose", version.ref = "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" }
|
coil-network = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
||||||
|
|
||||||
|
# Rich text formating
|
||||||
|
rich-text = { module = "com.mohamedrejeb.richeditor:richeditor-compose", version = "1.0.0-rc05-k2" }
|
||||||
|
|
||||||
|
# Publish
|
||||||
|
openium-publish = { group = "fr.openium", name = "publish-plugin", version.ref = "publish" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
@ -162,8 +160,8 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
|||||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||||
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
|
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
|
||||||
serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "serialization" }
|
serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "serialization" }
|
||||||
firebaseCrashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsPlugin" }
|
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsPlugin" }
|
||||||
googleServices = { id = "com.google.gms.google-services", version.ref = "googleServicesPlugin" }
|
google-services = { id = "com.google.gms.google-services", version.ref = "googleServicesPlugin" }
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
androidx = ["androidx-core-ktx", "androidx-activity-compose"]
|
androidx = ["androidx-core-ktx", "androidx-activity-compose"]
|
||||||
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
#Mon Dec 09 14:35:54 CET 2024
|
#Mon Feb 17 11:35:23 CET 2025
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
Reference in New Issue
Block a user