Compare commits

..

No commits in common. "develop/v1.0.0_build-1_2025-02-20-15-26" and "main" have entirely different histories.

156 changed files with 2 additions and 4744 deletions

1
.gitignore vendored
View File

@ -325,5 +325,4 @@ fabric.properties
!/gradle/wrapper/gradle-wrapper.jar
.idea
# End of https://www.gitignore.io/api/macos,carthage,symfony,xcode,android,androidstudio

15
Jenkinsfile vendored
View File

@ -1,16 +1,3 @@
library "openiumpipeline"
openiumDroidJob modules: [
"consentium": [
unitTestTasks: ["testDevDebugUnitTest"],
],
"consentium-ui": [
unitTestTasks: ["testDevDebugUnitTest"],
],
"app": [
unitTestTasks: ["testDevDebugUnitTest"],
testTasks: ["pixel5DevDebugAndroidTest"],
publishApkVariants : ["devDebug", "devRelease", "demoRelease", "prodRelease"],
],
]
publishChannel: '#int-consentium'
openiumDroidJob()

1
app/.gitignore vendored
View File

@ -1 +0,0 @@
/build

View File

@ -1,151 +0,0 @@
import com.android.build.api.dsl.ManagedVirtualDevice
import org.gradle.language.nativeplatform.internal.BuildType
import java.io.FileInputStream
import java.util.Properties
plugins {
alias(libs.plugins.ksp)
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.hilt)
alias(libs.plugins.serialization)
alias(libs.plugins.kotlin.compose)
id("fr.openium.publish")
}
apply(from = "publish.build.gradle")
// Keystore
val keystorePropertiesFile = rootProject.file("keys/keystore.properties")
val keystoreProperties = Properties()
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
android {
namespace = "fr.openium.consentium"
compileSdk = libs.versions.compileSdk.get().toInt()
defaultConfig {
applicationId = "fr.openium.consentium"
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}
signingConfigs {
named(BuildType.DEBUG.name) {
storeFile = file(keystoreProperties["debugStoreFile"].toString())
}
register(BuildType.RELEASE.name) {
storeFile = file(keystoreProperties["releaseStoreFile"].toString())
storePassword = keystoreProperties["passwordRelease"].toString()
keyAlias = keystoreProperties["aliasRelease"].toString()
keyPassword = keystoreProperties["passwordRelease"].toString()
}
}
buildTypes {
debug {
isMinifyEnabled = false
isShrinkResources = false
versionNameSuffix = "-debug"
applicationIdSuffix = ".debug"
signingConfig = signingConfigs.getByName(BuildType.DEBUG.name)
}
release {
isMinifyEnabled = true
isShrinkResources = true
signingConfig = signingConfigs.getByName(BuildType.RELEASE.name)
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
flavorDimensions += "version"
productFlavors {
create("prod") {
dimension = "version"
}
create("dev") {
dimension = "version"
}
create("demo") {
dimension = "version"
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
compose = true
}
testOptions {
animationsDisabled = true
managedDevices {
devices {
create<ManagedVirtualDevice>("pixel5") {
device = "Pixel 5"
apiLevel = 34
systemImageSource = "google"
}
}
}
}
}
dependencies {
// Internal dependencies
implementation(project(":consentium"))
implementation(project(":consentium-ui"))
// AndroidX
implementation(libs.bundles.androidx)
// Lifecycle
implementation(libs.androidx.lifecycle.runtime)
implementation(libs.androidx.lifecycle.viewmodel)
// Hilt
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
implementation(libs.hilt.navigation.compose)
// Libs analytics
implementation(libs.matomo)
implementation(libs.clarity)
implementation(libs.ga4)
// Compose
implementation(platform(libs.compose.bom))
implementation(libs.bundles.compose)
// Timber
implementation(libs.timber)
// Compose Navigation
implementation(libs.androidx.navigation.compose)
// Tests
testImplementation(libs.test.junit)
androidTestImplementation(libs.test.junit)
androidTestImplementation(libs.test.espresso)
androidTestImplementation(libs.test.androidx.junit)
// Kotlin serialization
implementation(libs.kotlin.serialization)
}

View File

@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -1,6 +0,0 @@
android {
defaultConfig {
versionName publish.versionName
versionCode publish.versionCode
}
}

View File

@ -1,22 +0,0 @@
package fr.openium.consentium
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
assertTrue(true)
}
}

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application
android:name=".DemoApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Consentium"
tools:targetApi="31" >
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Consentium" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -1,7 +0,0 @@
package fr.openium.consentium
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class DemoApplication : Application()

View File

@ -1,39 +0,0 @@
package fr.openium.consentium
import DemoNavGraph
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.ui.Modifier
import androidx.navigation.compose.rememberNavController
import dagger.hilt.android.AndroidEntryPoint
import fr.openium.consentium.ui.theme.ConsentiumTheme
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
val navHostController = rememberNavController()
ConsentiumTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
DemoNavGraph(navHostController = navHostController)
}
}
}
}
}
}

View File

@ -1,153 +0,0 @@
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideIn
import androidx.compose.animation.slideOut
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.IntOffset
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.toRoute
import fr.openium.consentium.R
import fr.openium.consentium.api.Consentium
import fr.openium.consentium.ui.screens.main.MainScreen
import fr.openium.consentium.ui.screens.splash.SplashScreen
import fr.openium.consentium_ui.ui.components.ConsentiumComponent
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
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
fun DemoNavGraph(navHostController: NavHostController) {
NavHost(
navController = navHostController,
startDestination = Destination.Splash,
) {
composable<Destination.Splash>(
enterTransition = {
SLIDE_IN_FROM_RIGHT_ENTER_TRANSITION
},
exitTransition = {
SLIDE_OUT_TO_LEFT_EXIT_TRANSITION
}
) {
SplashScreen(
navigateToMain = {
navHostController.navigate(Destination.Main) {
popUpTo(navHostController.graph.findStartDestination().id) {
saveState = true
}
}
},
navigateToConsent = {
navHostController.navigate(Destination.Consent(ConsentiumPageUI.GENERAL_CONSENT))
}
)
}
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(
onGoToConsentMaster = {
navHostController.navigate(Destination.Consent(ConsentiumPageUI.GENERAL_CONSENT))
},
onGoToConsentDetail = {
navHostController.navigate(Destination.Consent(ConsentiumPageUI.DETAILS_CONSENT))
},
)
}
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 appId = stringResource(R.string.app_id)
val apiKey = stringResource(R.string.api_key)
val context = LocalContext.current
ConsentiumComponent(
defaultLandingPage = consent.landingPage,
onQuitConsent = {
navHostController.navigate(Destination.Main) {
popUpTo(navHostController.graph.findStartDestination().id) {
saveState = true
}
}
},
consentium = Consentium(
context = context,
apiKey = apiKey,
appId = appId,
)
)
}
}
}

View File

@ -1,18 +0,0 @@
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
import kotlinx.serialization.Serializable
sealed interface Destination {
@Serializable
data object Splash : Destination
@Serializable
data object Main : Destination
@Serializable
data class Consent(
val landingPage: ConsentiumPageUI
): Destination
}

View File

@ -1,46 +0,0 @@
package fr.openium.consentium.ui.screens.main
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun MainScreen(
onGoToConsentMaster: () -> Unit,
onGoToConsentDetail: () -> Unit,
modifier: Modifier = Modifier,
) {
// View
Column(
modifier = modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Button(
onClick = onGoToConsentMaster,
) {
Text(
text = "Go to Consent Master"
)
}
Spacer(modifier = Modifier.height(14.dp))
Button(
onClick = onGoToConsentDetail,
) {
Text(
text = "Go to Consent Detail"
)
}
}
}

View File

@ -1,98 +0,0 @@
package fr.openium.consentium.ui.screens.splash
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import fr.openium.consentium.R
import fr.openium.consentium.api.Consentium
import fr.openium.consentium.api.checkPurposeState
import fr.openium.consentium.api.state.FetchConsentiumState
@Composable
fun SplashScreen(
navigateToMain: () -> Unit,
navigateToConsent: () -> Unit,
) {
// Property
val context = LocalContext.current
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
LaunchedEffect(Unit) {
consentium.fetchConsentState.collect { consentState ->
when (consentState) {
FetchConsentiumState.Idle,
FetchConsentiumState.Loading,
-> {
}
FetchConsentiumState.Error -> {
// Handle error
consentium.fetchConsents()
}
is FetchConsentiumState.Invalid -> {
navigateToConsent()
}
is FetchConsentiumState.Valid -> {
// 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()
}
}
}
}
LaunchedEffect(Unit) {
consentium.fetchConsents()
}
// View
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("Splash Screen") // TODO
Spacer(modifier = Modifier.height(14.dp))
CircularProgressIndicator(
modifier = Modifier.size(40.dp)
)
}
}

View File

@ -1,22 +0,0 @@
package fr.openium.consentium.ui.theme
import androidx.compose.ui.graphics.Color
val Primary = Color(0xFF70ACDC)
val OnPrimary = Color(0XFFFFFFFF)
val Secondary = Color(0xFFF29413)
val OnSecondary = Color(0xFFFFFFFF)
val Tertiary = Color(0xFF3470A0)
val OnSurfaceVariant = Color(0xFF3470A0)
val OnSurface = Color(0xFF163752)
val Error = Color(0xFF3470A0)
val SurfaceHighest = Color(0xFFFFFFFF)
val SurfaceHigh = Color(0xFFF2F8FC)
val SurfaceMiddle = Color(0xFFD6E6F5)
val Success = Color(0xFF479B3F)

View File

@ -1,60 +0,0 @@
package fr.openium.consentium.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
private val DarkColorScheme = darkColorScheme(
primary = Primary,
onPrimary = OnPrimary,
secondary = Secondary,
onSecondary = OnSecondary,
tertiary = Tertiary,
onSurfaceVariant = OnSurfaceVariant,
onSurface = OnSurface,
error = Error,
surfaceContainerHighest = SurfaceHighest,
surfaceContainerHigh = SurfaceHigh,
surfaceContainer = SurfaceMiddle,
)
private val LightColorScheme = lightColorScheme(
primary = Primary,
onPrimary = OnPrimary,
secondary = Secondary,
onSecondary = OnSecondary,
tertiary = Tertiary,
onSurfaceVariant = OnSurfaceVariant,
onSurface = OnSurface,
error = Error,
surfaceContainerHighest = SurfaceHighest,
surfaceContainerHigh = SurfaceHigh,
surfaceContainer = SurfaceMiddle,
)
@Composable
fun ConsentiumTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
val colorScheme = when {
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@ -1,18 +0,0 @@
package fr.openium.consentium.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
)

View File

@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -1,30 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@ -1,5 +0,0 @@
<resources>
<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>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Consentium" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@ -1,17 +0,0 @@
package fr.openium.consentium
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View File

@ -1,3 +0,0 @@
#Wed Sep 27 10:07:57 CEST 2023
VERSION_NAME=1.0.0
VERSION_CODE=1

View File

@ -1,32 +0,0 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
// Application
alias(libs.plugins.android.application) apply false
// Android
alias(libs.plugins.kotlin.android) apply false
// Compose
alias(libs.plugins.kotlin.compose) apply false
// Ksp
alias(libs.plugins.ksp) apply false
// Agp
alias(libs.plugins.android.library) apply false
// Hilt
alias(libs.plugins.hilt) apply false
// Kotlin serialization
alias(libs.plugins.serialization) apply false
}
buildscript {
repositories {
maven { url = uri("https://maven.openium.fr/") }
}
dependencies {
classpath(libs.openium.publish)
}
}

View File

@ -1 +0,0 @@
/build

View File

@ -1,126 +0,0 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.serialization)
alias(libs.plugins.ksp)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.hilt)
}
android {
namespace = "fr.openium.consentium_ui"
compileSdk = libs.versions.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
debug {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
consumerProguardFiles("proguard-rules.pro")
}
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
consumerProguardFiles("proguard-rules.pro")
}
}
flavorDimensions += "version"
productFlavors {
create("prod") {
dimension = "version"
}
create("dev") {
dimension = "version"
}
create("demo") {
dimension = "version"
}
}
buildFeatures {
buildConfig = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
compose = true
}
}
dependencies {
// Internal dependencies
implementation(project(":consentium"))
// AndroidX
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.runtime.android)
implementation(libs.androidx.foundation.android)
implementation(libs.androidx.ui.android)
implementation(libs.androidx.foundation.layout.android)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.activity.compose)
//Compose
implementation(platform(libs.compose.bom))
implementation(libs.compose.ui.graphics)
implementation(libs.compose.ui)
implementation(libs.compose.ui.tooling)
implementation(libs.compose.material3)
// Serialization
implementation(libs.kotlin.serialization)
// Retrofit
api(libs.retrofit)
implementation(libs.retrofitConverter)
implementation(libs.logging.interceptor)
// Hilt
implementation(libs.hilt.android)
implementation(libs.hilt.navigation.compose)
ksp(libs.hilt.compiler)
// Timber
implementation(libs.timber)
// Coil
implementation(libs.coil)
implementation(libs.coil.network)
// Rich text formatting
implementation(libs.rich.text)
// Tests
testImplementation(libs.test.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.test.espresso)
}

View File

@ -1,73 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-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.** { *; }

View File

@ -1,19 +0,0 @@
package fr.openium.consentium_ui
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
assertTrue(true)
}
}

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@ -1,54 +0,0 @@
package fr.openium.consentium_ui.data.di
import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.Reusable
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import fr.openium.consentium.data.di.ConsentiumUrl
import fr.openium.consentium.data.di.OkHttpClientDefault
import fr.openium.consentium_ui.BuildConfig
import fr.openium.consentium_ui.data.remote.ConsentiumUIApi
import fr.openium.consentium_ui.data.remote.mock.ConsentiumUIMockApi
import kotlinx.serialization.json.Json
import okhttp3.HttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.kotlinx.serialization.asConverterFactory
private val json by lazy {
Json {
isLenient = true
ignoreUnknownKeys = true
}
}
@Module
@InstallIn(SingletonComponent::class)
internal object NetworkModule {
@Reusable
@Provides
fun provideConsentiumUIApi(
@ConsentiumUrl url: HttpUrl,
@OkHttpClientDefault okHttpClient: Lazy<OkHttpClient>,
): ConsentiumUIApi = if (BuildConfig.FLAVOR != "demo") {
createRetrofit(
url,
okHttpClient
).create(ConsentiumUIApi::class.java)
} else {
ConsentiumUIMockApi
}
private fun createRetrofit(url: HttpUrl, okHttpClient: Lazy<OkHttpClient>): Retrofit =
Retrofit.Builder()
.callFactory { request ->
okHttpClient.get().newCall(request)
}.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.baseUrl(url)
.build()
}

View File

@ -1,17 +0,0 @@
package fr.openium.consentium_ui.data.remote
import fr.openium.consentium_ui.data.remote.model.GetConsentConfigDTO
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Path
internal interface ConsentiumUIApi {
@GET("applications/{application}")
suspend fun getConsentConfig(
@Header("Authorization") token: String,
@Path("application") applicationId: String,
): Response<GetConsentConfigDTO>
}

View File

@ -1,109 +0,0 @@
package fr.openium.consentium_ui.data.remote.mock
import fr.openium.consentium_ui.data.remote.ConsentiumUIApi
import fr.openium.consentium_ui.data.remote.model.GetConsentConfigDTO
import fr.openium.consentium_ui.data.remote.model.MainConsentTextTranslationDTO
import fr.openium.consentium_ui.data.remote.model.PurposeDTO
import fr.openium.consentium_ui.data.remote.model.PurposeStatusDTO
import fr.openium.consentium_ui.data.remote.model.PurposeTranslationDTO
import retrofit2.Response
import java.util.UUID
internal object ConsentiumUIMockApi : ConsentiumUIApi {
private val consents = GetConsentConfigDTO(
installationId = UUID.randomUUID().toString(),
appName = "Consentium",
icon = "https://amp.openium.fr/openium.png",
consentMainTextTranslation = listOf(
MainConsentTextTranslationDTO(
id = "UUID",
language = "fr",
consentPageUrl = "https://www.openium.fr",
mainConsentText = "<p>[Nom de lapplication] utilise des cookies pour différents objectifs : faire fonctionner lapplication, améliorer nos services en mesurant lefficacité 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 lapplication 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 davis à tout moment depuis les paramètres de votre compte via longlet “Notifications et cookies”</p>\n",
durationText = "<p>Nous conservons votre choix pendant 12 mois. Vous pouvez changer davis à tout moment depuis les paramètres de votre compte via longlet “Notification et cookies” tetetettetettetetstts</p>\n"
)
),
consentPageUrl = "https://www.openium.fr",
purposes = listOf(
PurposeDTO(
id = "purpose-required",
order = 0,
isRequired = true,
choice = PurposeStatusDTO.ACCEPTED,
translations = listOf(
PurposeTranslationDTO(
id = "UUID",
language = "fr",
text = "<p>Ces traceurs sont nécessaire au fonctionnement de lapplication. Ils permettent de vérifier la stabilité technique de lapplication et de mesurer notre audience. Ces données ne sont utilisées que pour notre compte exclusif (en ne produisant que des données statistiques anonymes).</p>",
name = "Nécessaire"
)
),
),
PurposeDTO(
id = "purpose-advertising",
order = 1,
isRequired = false,
choice = PurposeStatusDTO.REFUSED,
translations = listOf(
PurposeTranslationDTO(
id = "UUID",
language = "fr",
text = "<p>Ces traceurs sont nécessaires pour afficher des publicités susceptibles de vous intéresser. Ils permettent de mesurer lefficacité de nos campagnes publicitaires et de personnaliser les publicités affichées.</p>",
name = "Publicité"
)
),
),
PurposeDTO(
id = "purpose-analytics",
order = 2,
isRequired = false,
choice = PurposeStatusDTO.ACCEPTED,
translations = listOf(
PurposeTranslationDTO(
id = "UUID",
language = "fr",
text = "<p>Ces traceurs sont nécessaires pour mesurer lefficacité de nos contenus. Ils permettent de mesurer laudience de lapplication et de comprendre comment les utilisateurs interagissent avec lapplication.</p>",
name = "Analyse"
)
),
),
PurposeDTO(
id = "purpose-personalization",
order = 3,
isRequired = false,
choice = PurposeStatusDTO.ACCEPTED,
translations = listOf(
PurposeTranslationDTO(
id = "UUID",
language = "fr",
text = "<p>Ces traceurs sont nécessaires pour mesurer lefficacité de nos contenus. Ils permettent de mesurer laudience de lapplication et de comprendre comment les utilisateurs interagissent avec lapplication.</p>",
name = "Personnalisation"
)
),
),
PurposeDTO(
id = "purpose-social",
order = 4,
isRequired = false,
choice = PurposeStatusDTO.ACCEPTED,
translations = listOf(
PurposeTranslationDTO(
id = "UUID",
language = "fr",
text = "<p>Ces traceurs sont nécessaires pour mesurer lefficacité de nos contenus. Ils permettent de mesurer laudience de lapplication et de comprendre comment les utilisateurs interagissent avec lapplication.</p>",
name = "Social"
)
),
)
)
)
override suspend fun getConsentConfig(
token: String,
applicationId: String,
): Response<GetConsentConfigDTO> {
return Response.success(consents)
}
}

View File

@ -1,14 +0,0 @@
package fr.openium.consentium_ui.data.remote.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
internal data class GetConsentConfigDTO(
@SerialName("id") val installationId: String,
@SerialName("name") val appName: String,
@SerialName("icon") val icon: String? = null,
@SerialName("consentPageUrl") val consentPageUrl: String,
@SerialName("translations") val consentMainTextTranslation: List<MainConsentTextTranslationDTO>,
@SerialName("purposes") val purposes: List<PurposeDTO>,
)

View File

@ -1,14 +0,0 @@
package fr.openium.consentium_ui.data.remote.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
internal data class MainConsentTextTranslationDTO(
@SerialName("id") val id: String,
@SerialName("lang") val language: String,
@SerialName("mainConsentText") val mainConsentText: String,
@SerialName("durationText") val durationText: String,
@SerialName("consentPageUrl") val consentPageUrl: String,
)

View File

@ -1,14 +0,0 @@
package fr.openium.consentium_ui.data.remote.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
internal data class PurposeDTO(
@SerialName("sortOrder") val order: Int,
@SerialName("isRequired") val isRequired: Boolean,
@SerialName("choice") val choice: PurposeStatusDTO,
@SerialName("translations") val translations: List<PurposeTranslationDTO>,
@SerialName("identifier") val id: String,
)

View File

@ -1,16 +0,0 @@
package fr.openium.consentium_ui.data.remote.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
internal enum class PurposeStatusDTO {
@SerialName("accepted")
ACCEPTED,
@SerialName("refused")
REFUSED,
@SerialName("partial")
PARTIAL,
}

View File

@ -1,12 +0,0 @@
package fr.openium.consentium_ui.data.remote.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
internal data class PurposeTranslationDTO(
@SerialName("id") val id: String,
@SerialName("lang") val language: String,
@SerialName("text") val text: String,
@SerialName("name") val name: String,
)

View File

@ -1,13 +0,0 @@
package fr.openium.consentium_ui.data.remote.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
internal data class VendorDTO(
@SerialName("identifier") val id: String,
@SerialName("order") val order: Int,
@SerialName("isRequired") val isRequired: Boolean,
@SerialName("isAccepted") val isAccepted: Boolean,
@SerialName("translations") val translations: List<VendorTranslationDTO>,
)

View File

@ -1,11 +0,0 @@
package fr.openium.consentium_ui.data.remote.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
internal data class VendorTranslationDTO(
@SerialName("id") val id: String,
@SerialName("lang") val language: String,
@SerialName("text") val text: String,
)

View File

@ -1,12 +0,0 @@
package fr.openium.consentium_ui.domain.adapter
import fr.openium.consentium_ui.data.remote.model.GetConsentConfigDTO
import fr.openium.consentium_ui.domain.model.ContentConfigData
internal fun GetConsentConfigDTO.toConsentConfigData() =
ContentConfigData(
applicationName = appName,
iconUrl = icon,
mainTextTranslation = consentMainTextTranslation.toMainConsentTextTranslationDataList(),
purposes = purposes.toPurposeDataList()
)

View File

@ -1,15 +0,0 @@
package fr.openium.consentium_ui.domain.adapter
import fr.openium.consentium_ui.data.remote.model.MainConsentTextTranslationDTO
import fr.openium.consentium_ui.domain.model.MainConsentTextTranslationData
internal fun MainConsentTextTranslationDTO.toMainConsentTextTranslationData() =
MainConsentTextTranslationData(
id = id,
language = language,
consentPageUrl = consentPageUrl,
mainConsentText = mainConsentText,
durationText = durationText,
)
internal fun List<MainConsentTextTranslationDTO>.toMainConsentTextTranslationDataList() = map { it.toMainConsentTextTranslationData() }

View File

@ -1,16 +0,0 @@
package fr.openium.consentium_ui.domain.adapter
import fr.openium.consentium_ui.data.remote.model.PurposeDTO
import fr.openium.consentium_ui.domain.model.PurposeData
internal fun PurposeDTO.toPurposeData() =
PurposeData(
identifier = id,
isRequired = isRequired,
choice = choice.toPurposeStatusData(),
order = order,
vendors = emptyList(),
translations = translations.toPurposeTranslationDataList(),
)
internal fun List<PurposeDTO>.toPurposeDataList() = map { it.toPurposeData() }

View File

@ -1,12 +0,0 @@
package fr.openium.consentium_ui.domain.adapter
import fr.openium.consentium_ui.data.remote.model.PurposeStatusDTO
import fr.openium.consentium_ui.domain.model.PurposeStatusData
internal fun PurposeStatusDTO.toPurposeStatusData(): PurposeStatusData {
return when (this) {
PurposeStatusDTO.ACCEPTED -> PurposeStatusData.ACCEPTED
PurposeStatusDTO.REFUSED -> PurposeStatusData.REJECTED
PurposeStatusDTO.PARTIAL -> PurposeStatusData.PARTIAL
}
}

View File

@ -1,14 +0,0 @@
package fr.openium.consentium_ui.domain.adapter
import fr.openium.consentium_ui.data.remote.model.PurposeTranslationDTO
import fr.openium.consentium_ui.domain.model.PurposeTranslationData
internal fun PurposeTranslationDTO.toPurposeTranslationData() =
PurposeTranslationData(
id = id,
language = language,
text = text,
name = name,
)
internal fun List<PurposeTranslationDTO>.toPurposeTranslationDataList() = map { it.toPurposeTranslationData() }

View File

@ -1,15 +0,0 @@
package fr.openium.consentium_ui.domain.adapter
import fr.openium.consentium_ui.data.remote.model.VendorDTO
import fr.openium.consentium_ui.domain.model.VendorData
internal fun VendorDTO.toVendorData() =
VendorData(
identifier = id,
order = order,
isRequired = isRequired,
isAccepted = isAccepted,
translations = translations.toVendorTranslationDataList(),
)
internal fun List<VendorDTO>.toVendorDataList() = map { it.toVendorData() }

View File

@ -1,13 +0,0 @@
package fr.openium.consentium_ui.domain.adapter
import fr.openium.consentium_ui.data.remote.model.VendorTranslationDTO
import fr.openium.consentium_ui.domain.model.VendorTranslationData
internal fun VendorTranslationDTO.toVendorTranslationData() =
VendorTranslationData(
id = id,
language = language,
text = text,
)
internal fun List<VendorTranslationDTO>.toVendorTranslationDataList() = map { it.toVendorTranslationData() }

View File

@ -1,26 +0,0 @@
package fr.openium.consentium_ui.domain.di
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import fr.openium.consentium_ui.domain.usecase.GetApplicationLanguageUseCase
import fr.openium.consentium_ui.domain.usecase.GetApplicationLanguageUseCaseImpl
import fr.openium.consentium_ui.domain.usecase.GetConfigTextForLanguageUseCase
import fr.openium.consentium_ui.domain.usecase.GetConfigTextForLanguageUseCaseImpl
@Module
@InstallIn(SingletonComponent::class)
internal interface ConsentiumUseCaseModule {
@Binds
fun bindsGetConfigTextForLanguageUseCase(
getConfigTextForLanguageUseCaseImpl: GetConfigTextForLanguageUseCaseImpl,
): GetConfigTextForLanguageUseCase
@Binds
fun bindGetApplicationLangageUseCase(
getApplicationLangageUseCaseImpl: GetApplicationLanguageUseCaseImpl,
): GetApplicationLanguageUseCase
}

View File

@ -1,8 +0,0 @@
package fr.openium.consentium_ui.domain.model
internal data class ContentConfigData(
val applicationName: String,
val iconUrl : String?,
val mainTextTranslation: List<MainConsentTextTranslationData>,
val purposes: List<PurposeData>
)

View File

@ -1,9 +0,0 @@
package fr.openium.consentium_ui.domain.model
internal data class MainConsentTextTranslationData(
val id: String,
val language: String,
val consentPageUrl: String,
val mainConsentText: String,
val durationText: String,
)

View File

@ -1,10 +0,0 @@
package fr.openium.consentium_ui.domain.model
internal data class PurposeData(
val identifier: String,
val order: Int,
val isRequired: Boolean,
val choice: PurposeStatusData,
val translations: List<PurposeTranslationData>,
val vendors: List<VendorData>,
)

View File

@ -1,7 +0,0 @@
package fr.openium.consentium_ui.domain.model
internal enum class PurposeStatusData {
ACCEPTED,
REJECTED,
PARTIAL,
}

View File

@ -1,8 +0,0 @@
package fr.openium.consentium_ui.domain.model
internal data class PurposeTranslationData(
val id: String,
val language: String,
val text: String,
val name: String,
)

View File

@ -1,9 +0,0 @@
package fr.openium.consentium_ui.domain.model
internal data class VendorData(
val identifier: String,
val order: Int,
val isRequired: Boolean,
val isAccepted: Boolean,
val translations: List<VendorTranslationData>,
)

View File

@ -1,7 +0,0 @@
package fr.openium.consentium_ui.domain.model
internal data class VendorTranslationData(
val id: String,
val language: String,
val text: String,
)

View File

@ -1,41 +0,0 @@
package fr.openium.consentium_ui.domain.repository
import fr.openium.consentium.domain.useCase.GetAuthTokenUseCase
import fr.openium.consentium_ui.data.remote.ConsentiumUIApi
import fr.openium.consentium_ui.domain.adapter.toConsentConfigData
import fr.openium.consentium_ui.domain.model.ContentConfigData
import timber.log.Timber
import javax.inject.Inject
internal class ConsentiumRepository @Inject constructor(
private val consentiumUIApi: ConsentiumUIApi,
private val getAuthTokenUseCase: GetAuthTokenUseCase,
) {
suspend fun getConsentiumConfig(
applicationId: String,
apiKey: String,
): ConsentiumUIRepositoryResponse {
return try {
val authToken = getAuthTokenUseCase(apiKey)
val consentsResponse = consentiumUIApi.getConsentConfig(authToken, applicationId)
val consentsBody = if (consentsResponse.isSuccessful) {
consentsResponse.body() ?: throw Exception()
} else {
throw Exception()
}
ConsentiumUIRepositoryResponse.Success(consentsBody.toConsentConfigData())
} catch (e: Exception) {
Timber.d("$e")
ConsentiumUIRepositoryResponse.Error
}
}
}
internal interface ConsentiumUIRepositoryResponse {
data object Error : ConsentiumUIRepositoryResponse
data class Success(val contentConfigData: ContentConfigData) : ConsentiumUIRepositoryResponse
}

View File

@ -1,14 +0,0 @@
package fr.openium.consentium_ui.domain.usecase
import android.content.Context
import javax.inject.Inject
internal interface GetApplicationLanguageUseCase {
suspend operator fun invoke(context: Context): String
}
internal class GetApplicationLanguageUseCaseImpl @Inject constructor() : GetApplicationLanguageUseCase {
override suspend fun invoke(context: Context): String {
return context.resources.configuration.locales[0].language
}
}

View File

@ -1,87 +0,0 @@
package fr.openium.consentium_ui.domain.usecase
import fr.openium.consentium_ui.domain.model.ContentConfigData
import javax.inject.Inject
private const val FALLBACK_LANGUAGE = "en"
internal interface GetConfigTextForLanguageUseCase {
suspend operator fun invoke(
language: String = FALLBACK_LANGUAGE,
configData: ContentConfigData,
): GetConfigTextForLanguageUseCaseResponse
}
internal class GetConfigTextForLanguageUseCaseImpl @Inject constructor() :
GetConfigTextForLanguageUseCase {
override suspend fun invoke(
language: String,
configData: ContentConfigData,
): GetConfigTextForLanguageUseCaseResponse {
return try {
val canIUseTheTranslation =
configData.mainTextTranslation.any { it.language == language } &&
configData.purposes.all { purposeData ->
purposeData.translations.any { it.language == language } &&
purposeData.vendors.all { vendorData ->
vendorData.translations.any { it.language == language }
}
}
val languageToUse = if (canIUseTheTranslation) {
language
} else {
val isThereAGoodFallbackLanguage =
configData.mainTextTranslation.any { it.language == FALLBACK_LANGUAGE } &&
configData.purposes.all { purposeData ->
purposeData.translations.any { it.language == FALLBACK_LANGUAGE } &&
purposeData.vendors.all { vendorData ->
vendorData.translations.any { it.language == FALLBACK_LANGUAGE }
}
}
if (isThereAGoodFallbackLanguage) {
FALLBACK_LANGUAGE
} else {
throw Exception()
}
}
val filteredConfigData = configData.copy(
mainTextTranslation = configData.mainTextTranslation.filter { it.language == languageToUse },
purposes = configData.purposes.map { purposeData ->
purposeData.copy(
translations = purposeData.translations.filter { it.language == languageToUse },
vendors = purposeData.vendors.map { vendorData ->
vendorData.copy(
translations = vendorData.translations.filter { it.language == languageToUse }
)
}
)
}
)
if (languageToUse == FALLBACK_LANGUAGE) {
GetConfigTextForLanguageUseCaseResponse.DefaultLanguage(filteredConfigData)
} else {
GetConfigTextForLanguageUseCaseResponse.Success(filteredConfigData)
}
} catch (e: Exception) {
GetConfigTextForLanguageUseCaseResponse.Error
}
}
}
internal interface GetConfigTextForLanguageUseCaseResponse {
data object Error : GetConfigTextForLanguageUseCaseResponse
data class Success(val configData: ContentConfigData) : GetConfigTextForLanguageUseCaseResponse
data class DefaultLanguage(val configData: ContentConfigData) :
GetConfigTextForLanguageUseCaseResponse
}

View File

@ -1,110 +0,0 @@
package fr.openium.consentium_ui.ui
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.slideIn
import androidx.compose.animation.slideOut
import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.IntOffset
import fr.openium.consentium_ui.ui.components.ConsentiumUIDetailConsentComponent
import fr.openium.consentium_ui.ui.components.ConsentiumUIErrorComponent
import fr.openium.consentium_ui.ui.components.ConsentiumUIGeneralConsentComponent
import fr.openium.consentium_ui.ui.components.ConsentiumUILoadingComponent
import fr.openium.consentium_ui.ui.components.ConsentiumUIWebview
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
import fr.openium.consentium_ui.ui.model.DetailConsentUI
import fr.openium.consentium_ui.ui.model.LoadingElement
import fr.openium.consentium_ui.ui.state.ConsentiumUIState
@Composable
internal fun ConsentiumScreen(
page: ConsentiumPageUI,
state: ConsentiumUIState,
loadingElement: LoadingElement,
onNavigateBack: () -> Unit,
onAcceptAndClose: (consents: DetailConsentUI) -> Unit,
onDenyAndClose: (consents: DetailConsentUI) -> Unit,
onSaveAndCloseDetails: (consents: DetailConsentUI) -> Unit,
onNavigateToDetails: () -> Unit,
onClickCookiesPolicies: () -> Unit,
onClickRetry: () -> Unit,
) {
when (state) {
is ConsentiumUIState.Loading -> {
ConsentiumUILoadingComponent()
}
is ConsentiumUIState.Error -> {
ConsentiumUIErrorComponent(
errorMessage = state.message,
onRetry = onClickRetry
)
}
is ConsentiumUIState.Loaded -> {
AnimatedContent(
targetState = page,
transitionSpec = {
when (page) {
ConsentiumPageUI.GENERAL_CONSENT -> {
slideIn(
initialOffset = { fullSize -> IntOffset(-fullSize.width, 0) }
).togetherWith(
slideOut(
targetOffset = { fullSize -> IntOffset(fullSize.width, 0) }
)
)
}
else -> {
slideIn(
initialOffset = { fullSize -> IntOffset(fullSize.width, 0) }
).togetherWith(
slideOut(
targetOffset = { fullSize -> IntOffset(-fullSize.width, 0) }
)
)
}
}
}
) { currentPage ->
when (currentPage) {
ConsentiumPageUI.GENERAL_CONSENT -> {
ConsentiumUIGeneralConsentComponent(
generalConsentUI = state.generalConsentUI,
onNavigateToDetails = onNavigateToDetails,
onAcceptAndClose = {
onAcceptAndClose(state.detailConsentUI)
},
onDenyAndClose = {
onDenyAndClose(state.detailConsentUI)
},
onClickCookiesPolicies = onClickCookiesPolicies,
loadingElement = loadingElement
)
}
ConsentiumPageUI.DETAILS_CONSENT -> {
ConsentiumUIDetailConsentComponent(
detailConsentUI = state.detailConsentUI,
onNavigateBack = onNavigateBack,
onSave = {
onSaveAndCloseDetails(state.detailConsentUI)
},
loadingElement = loadingElement
)
}
ConsentiumPageUI.COOKIES_POLICY -> {
ConsentiumUIWebview(
url = state.detailConsentUI.cookiePolicyUrl
)
}
}
}
}
}
}

View File

@ -1,79 +0,0 @@
package fr.openium.consentium_ui.ui
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import fr.openium.consentium_ui.R
import fr.openium.consentium_ui.domain.repository.ConsentiumRepository
import fr.openium.consentium_ui.domain.repository.ConsentiumUIRepositoryResponse
import fr.openium.consentium_ui.domain.usecase.GetApplicationLanguageUseCase
import fr.openium.consentium_ui.domain.usecase.GetConfigTextForLanguageUseCase
import fr.openium.consentium_ui.domain.usecase.GetConfigTextForLanguageUseCaseResponse
import fr.openium.consentium_ui.ui.adapter.toDetailConsentUI
import fr.openium.consentium_ui.ui.adapter.toGeneralConsentUI
import fr.openium.consentium_ui.ui.state.ConsentiumUIState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
internal class ConsentiumUIViewModel @Inject constructor(
@ApplicationContext private val context: Context,
private val consentiumUIRepository: ConsentiumRepository,
private val configTextLanguageUseCase: GetConfigTextForLanguageUseCase,
private val getApplicationLanguageUseCase: GetApplicationLanguageUseCase,
) : ViewModel() {
private val _state = MutableStateFlow<ConsentiumUIState>(ConsentiumUIState.Loading)
val state: StateFlow<ConsentiumUIState> by lazy { _state.asStateFlow() }
fun init(appId: String, apiKey: String) {
viewModelScope.launch {
_state.value = ConsentiumUIState.Loading
when (val consentiumUIFetchResponse = consentiumUIRepository.getConsentiumConfig(appId, apiKey)) {
is ConsentiumUIRepositoryResponse.Success -> {
val consentUIResponse =
configTextLanguageUseCase(getApplicationLanguageUseCase(context), consentiumUIFetchResponse.contentConfigData)
when (val consentUI = consentUIResponse) {
is GetConfigTextForLanguageUseCaseResponse.Success -> {
_state.emit(
ConsentiumUIState.Loaded(
generalConsentUI = consentUI.configData.toGeneralConsentUI(),
detailConsentUI = consentUI.configData.toDetailConsentUI(),
)
)
}
is GetConfigTextForLanguageUseCaseResponse.DefaultLanguage -> {
_state.emit(
ConsentiumUIState.Loaded(
generalConsentUI = consentUI.configData.toGeneralConsentUI(),
detailConsentUI = consentUI.configData.toDetailConsentUI(),
)
)
}
is GetConfigTextForLanguageUseCaseResponse.Error -> {
_state.emit(ConsentiumUIState.Error(context.getString(R.string.consents_error_loading_consents)))
}
}
}
is ConsentiumUIRepositoryResponse.Error -> {
_state.value = ConsentiumUIState.Error(context.getString(R.string.consents_error_loading_consents))
}
}
}
}
}

View File

@ -1,40 +0,0 @@
package fr.openium.consentium_ui.ui.adapter
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.ui.model.DetailConsentUI
import fr.openium.consentium_ui.ui.model.PurposeStatusUI
import fr.openium.consentium_ui.ui.model.PurposeUI
internal fun ContentConfigData.toDetailConsentUI(): DetailConsentUI = DetailConsentUI(
conservationDurationText = mainTextTranslation.first().durationText,
purposes = purposes.sortedBy { it.order }.map { it.toPurposeUI() },
cookiePolicyUrl = mainTextTranslation.first().consentPageUrl,
)
internal fun DetailConsentUI.toPurposeChoices(): List<PurposeChoice> = purposes.map {
it.toPurposeChoice()
}
internal fun DetailConsentUI.toDeniedPurposeChoices(): List<PurposeChoice> = purposes.map {
it.toPurposeChoice().copy(choice = PurposeStatus.REJECTED)
}
internal fun DetailConsentUI.toAcceptedPurposeChoices(): List<PurposeChoice> = purposes.map {
it.toPurposeChoice().copy(choice = PurposeStatus.ACCEPTED)
}
internal fun PurposeUI.toPurposeChoice(): PurposeChoice =
PurposeChoice(
purposeIdentifier = id,
choice = choice.toPurposeStatus(),
vendors = emptyList(), // Not in v1
)
internal fun PurposeStatusUI.toPurposeStatus(): PurposeStatus =
when (this) {
PurposeStatusUI.ACCEPTED -> PurposeStatus.ACCEPTED
PurposeStatusUI.REJECTED -> PurposeStatus.REJECTED
PurposeStatusUI.PARTIAL -> PurposeStatus.PARTIAL
}

View File

@ -1,12 +0,0 @@
package fr.openium.consentium_ui.ui.adapter
import fr.openium.consentium_ui.domain.model.ContentConfigData
import fr.openium.consentium_ui.ui.model.GeneralConsentUI
internal fun ContentConfigData.toGeneralConsentUI(): GeneralConsentUI =
GeneralConsentUI(
applicationName = applicationName,
iconUrl = iconUrl,
mainConsentText = mainTextTranslation.first().mainConsentText,
consentPageUrl = mainTextTranslation.first().consentPageUrl,
)

View File

@ -1,10 +0,0 @@
package fr.openium.consentium_ui.ui.adapter
import fr.openium.consentium_ui.domain.model.PurposeStatusData
import fr.openium.consentium_ui.ui.model.PurposeStatusUI
internal fun PurposeStatusData.toPurposeStatusUI(): PurposeStatusUI = when(this) {
PurposeStatusData.ACCEPTED -> PurposeStatusUI.ACCEPTED
PurposeStatusData.REJECTED -> PurposeStatusUI.REJECTED
PurposeStatusData.PARTIAL -> PurposeStatusUI.PARTIAL
}

View File

@ -1,13 +0,0 @@
package fr.openium.consentium_ui.ui.adapter
import fr.openium.consentium_ui.domain.model.PurposeData
import fr.openium.consentium_ui.ui.model.PurposeUI
internal fun PurposeData.toPurposeUI(): PurposeUI = PurposeUI(
id = identifier,
isRequired = isRequired,
choice = choice.toPurposeStatusUI(),
title = translations.first().name,
description = translations.first().text,
vendors = vendors.toVendorUIList(),
)

View File

@ -1,11 +0,0 @@
package fr.openium.consentium_ui.ui.adapter
import fr.openium.consentium_ui.domain.model.VendorData
import fr.openium.consentium_ui.ui.model.VendorUI
internal fun VendorData.toVendorUI() = VendorUI(
id = identifier,
isAccepted = isAccepted,
)
internal fun List<VendorData>.toVendorUIList() = map { it.toVendorUI() }

View File

@ -1,139 +0,0 @@
package fr.openium.consentium_ui.ui.components
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import fr.openium.consentium.api.Consentium
import fr.openium.consentium.api.state.SetConsentiumState
import fr.openium.consentium_ui.R
import fr.openium.consentium_ui.ui.ConsentiumScreen
import fr.openium.consentium_ui.ui.ConsentiumUIViewModel
import fr.openium.consentium_ui.ui.adapter.toAcceptedPurposeChoices
import fr.openium.consentium_ui.ui.adapter.toDeniedPurposeChoices
import fr.openium.consentium_ui.ui.adapter.toPurposeChoices
import fr.openium.consentium_ui.ui.components.style.ConsentiumColors
import fr.openium.consentium_ui.ui.components.style.ConsentiumDefaults
import fr.openium.consentium_ui.ui.components.style.ConsentiumTypography
import fr.openium.consentium_ui.ui.components.style.LocalColors
import fr.openium.consentium_ui.ui.components.style.LocalTypography
import fr.openium.consentium_ui.ui.model.ConsentiumPageUI
import fr.openium.consentium_ui.ui.model.LoadingElement
import kotlinx.coroutines.launch
@Composable
fun ConsentiumComponent(
consentium: Consentium,
onQuitConsent: () -> (Unit),
colors: ConsentiumColors = ConsentiumDefaults.colors(),
typography: ConsentiumTypography = ConsentiumDefaults.typography(),
defaultLandingPage: ConsentiumPageUI = ConsentiumPageUI.GENERAL_CONSENT,
) {
// Property
val viewModel: ConsentiumUIViewModel = hiltViewModel()
val loadingState by viewModel.state.collectAsStateWithLifecycle()
var currentPage by remember(defaultLandingPage) { mutableStateOf(defaultLandingPage) }
val scope = rememberCoroutineScope()
val context = LocalContext.current
val errorMessage = stringResource(id = R.string.save_consents_error_message)
val consentiumState by consentium.saveConsentState.collectAsStateWithLifecycle()
var loadingElement by remember { mutableStateOf(LoadingElement.NONE) }
// Effect
LaunchedEffect(Unit) {
viewModel.init(apiKey = consentium.apiKey, appId = consentium.appId)
}
LaunchedEffect(consentiumState) {
when (consentiumState) {
SetConsentiumState.Idle,
SetConsentiumState.Loading,
-> {
}
SetConsentiumState.Error -> {
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show()
}
SetConsentiumState.Success -> {
onQuitConsent()
}
}
}
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
CompositionLocalProvider(
LocalColors provides colors,
LocalTypography provides typography,
) {
ConsentiumScreen(
state = loadingState,
page = currentPage,
loadingElement = if (consentiumState is SetConsentiumState.Loading) loadingElement else LoadingElement.NONE,
onNavigateBack = {
when {
defaultLandingPage == ConsentiumPageUI.GENERAL_CONSENT && currentPage == ConsentiumPageUI.DETAILS_CONSENT -> {
currentPage = ConsentiumPageUI.GENERAL_CONSENT
}
else -> {
onQuitConsent()
}
}
},
onNavigateToDetails = {
currentPage = ConsentiumPageUI.DETAILS_CONSENT
},
onAcceptAndClose = { consent ->
scope.launch {
loadingElement = LoadingElement.BUTTON
consentium.saveConsents(consent.toAcceptedPurposeChoices())
}
},
onDenyAndClose = { consent ->
scope.launch {
loadingElement = LoadingElement.LINK
consentium.saveConsents(consent.toDeniedPurposeChoices())
}
},
onSaveAndCloseDetails = { consent ->
scope.launch {
loadingElement = LoadingElement.BUTTON
consentium.saveConsents(consent.toPurposeChoices())
}
},
onClickCookiesPolicies = {
currentPage = ConsentiumPageUI.COOKIES_POLICY
},
onClickRetry = {
viewModel.init(apiKey = consentium.apiKey, appId = consentium.appId)
}
)
}
}

View File

@ -1,108 +0,0 @@
package fr.openium.consentium_ui.ui.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import fr.openium.consentium_ui.R
import fr.openium.consentium_ui.ui.components.core.PurposeComponent
import fr.openium.consentium_ui.ui.components.core.button.ConsentButton
import fr.openium.consentium_ui.ui.components.core.button.ConsentiumUIButtonStyle
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
import fr.openium.consentium_ui.ui.model.DetailConsentUI
import fr.openium.consentium_ui.ui.model.LoadingElement
import fr.openium.consentium_ui.ui.utils.toRichHtmlString
@Composable
internal fun ConsentiumUIDetailConsentComponent(
detailConsentUI: DetailConsentUI,
loadingElement: LoadingElement,
onNavigateBack: () -> Unit,
onSave: () -> Unit,
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 24.dp),
) {
Spacer(modifier = Modifier.height(16.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start,
modifier = Modifier.fillMaxWidth()
) {
IconButton(onClick = { onNavigateBack() }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.back),
tint = ConsentiumUITheme.colors.onSurface,
modifier = Modifier.size(26.dp),
)
}
Spacer(modifier = Modifier.width(10.dp))
Text(
text = stringResource(R.string.parameters),
style = ConsentiumUITheme.typography.h2,
color = ConsentiumUITheme.colors.onSurface,
)
}
Spacer(modifier = Modifier.height(25.dp))
Text(
text = detailConsentUI.conservationDurationText.toRichHtmlString(),
style = ConsentiumUITheme.typography.p3,
color = ConsentiumUITheme.colors.onSurface,
)
Spacer(modifier = Modifier.height(16.dp))
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
items(detailConsentUI.purposes) { purposeUI ->
PurposeComponent(
purposeUI = purposeUI,
)
Spacer(modifier = Modifier.height(16.dp))
}
}
Spacer(modifier = Modifier.height(16.dp))
ConsentButton(
text = stringResource(R.string.save),
buttonStyle = ConsentiumUIButtonStyle.PRIMARY,
onclick = onSave,
isLoading = loadingElement == LoadingElement.BUTTON,
)
Spacer(modifier = Modifier.height(10.dp))
}
}

View File

@ -1,48 +0,0 @@
package fr.openium.consentium_ui.ui.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
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.runtime.Composable
import androidx.compose.ui.Alignment
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
@Composable
internal fun ConsentiumUIErrorComponent(
errorMessage: String,
onRetry: () -> Unit,
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 24.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = errorMessage,
color = ConsentiumUITheme.colors.error,
style = ConsentiumUITheme.typography.b2,
textAlign = TextAlign.Center,
)
Spacer(modifier = Modifier.height(24.dp))
ConsentButton(
text = stringResource(R.string.retry),
buttonStyle = ConsentiumUIButtonStyle.PRIMARY,
onclick = onRetry,
)
}
}

View File

@ -1,124 +0,0 @@
package fr.openium.consentium_ui.ui.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import fr.openium.consentium_ui.R
import fr.openium.consentium_ui.ui.components.core.TextLink
import fr.openium.consentium_ui.ui.components.core.button.ConsentButton
import fr.openium.consentium_ui.ui.components.core.button.ConsentiumUIButtonStyle
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
import fr.openium.consentium_ui.ui.model.GeneralConsentUI
import fr.openium.consentium_ui.ui.model.LoadingElement
import fr.openium.consentium_ui.ui.utils.toRichHtmlString
@Composable
internal fun ConsentiumUIGeneralConsentComponent(
generalConsentUI: GeneralConsentUI,
loadingElement: LoadingElement,
onClickCookiesPolicies: () -> Unit,
onDenyAndClose: () -> Unit,
onAcceptAndClose: () -> Unit,
onNavigateToDetails: () -> Unit,
) {
// View
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 24.dp),
) {
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,
)
}
Spacer(modifier = Modifier.height(24.dp))
}
Text(
text = generalConsentUI.mainConsentText.toRichHtmlString(),
style = ConsentiumUITheme.typography.p3,
textAlign = TextAlign.Start,
color = ConsentiumUITheme.colors.onSurface,
overflow = TextOverflow.Ellipsis,
)
Spacer(modifier = Modifier.height(16.dp))
TextLink(
text = stringResource(R.string.cookies),
onclick = onClickCookiesPolicies,
)
Spacer(modifier = Modifier
.heightIn(16.dp)
.weight(1f))
TextLink(
text = stringResource(R.string.refuse),
onclick = onDenyAndClose,
isLoading = loadingElement == LoadingElement.LINK,
)
Spacer(modifier = Modifier.height(16.dp))
ConsentButton(
text = stringResource(R.string.accept),
buttonStyle = ConsentiumUIButtonStyle.PRIMARY,
onclick = onAcceptAndClose,
isLoading = loadingElement == LoadingElement.BUTTON,
)
Spacer(modifier = Modifier.height(16.dp))
ConsentButton(
text = stringResource(R.string.parameters),
buttonStyle = ConsentiumUIButtonStyle.SECONDARY,
onclick = onNavigateToDetails,
)
Spacer(modifier = Modifier.height(10.dp))
}
}
@Preview(showSystemUi = true)
@Composable
private fun ConsentiumUIGeneralConsentComponentPreview() {
ConsentiumUIGeneralConsentComponent(
generalConsentUI = GeneralConsentUI(
iconUrl = "https://amp.openium.fr/openium.png",
mainConsentText = "[Nom de lapplication] utilise des cookies pour différents objectifs : faire fonctionner lapplication, améliorer nos services en mesurant lefficacité de nos contenus et afficher des publicités susceptibles de vous intéresser. \n\n En cliquant sur “Accepter et fermer”, vous acceptez cette utilisation sur lapplication 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 davis à tout moment depuis les paramètres de votre compte via longlet “Notifications et cookies”.",
applicationName = "Application name",
consentPageUrl = "https://www.google.com"
),
onClickCookiesPolicies = {},
onDenyAndClose = {},
onAcceptAndClose = {},
onNavigateToDetails = {},
loadingElement = LoadingElement.LINK,
)
}

View File

@ -1,23 +0,0 @@
package fr.openium.consentium_ui.ui.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
@Composable
internal fun ConsentiumUILoadingComponent() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
CircularProgressIndicator(
color = ConsentiumUITheme.colors.primary
)
}
}

View File

@ -1,28 +0,0 @@
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)
}
)
}

View File

@ -1,110 +0,0 @@
package fr.openium.consentium_ui.ui.components.core
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import fr.openium.consentium_ui.R
import fr.openium.consentium_ui.ui.components.core.toggle.ConsentiumUISwitch
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
import fr.openium.consentium_ui.ui.model.PurposeStatusUI
import fr.openium.consentium_ui.ui.model.PurposeUI
import fr.openium.consentium_ui.ui.utils.toRichHtmlString
@Composable
internal fun PurposeComponent(
purposeUI: PurposeUI,
) {
// Properties
var isChecked by remember { mutableStateOf(purposeUI.choice != PurposeStatusUI.REJECTED) }
// View
Column(
modifier = Modifier.fillMaxWidth(),
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = purposeUI.title,
style = ConsentiumUITheme.typography.b3,
color = ConsentiumUITheme.colors.onSurface,
)
Spacer(modifier = Modifier.weight(1f))
if (purposeUI.isRequired) {
Text(
text = stringResource(id = R.string.require),
style = ConsentiumUITheme.typography.b3,
color = ConsentiumUITheme.colors.onSurface,
)
} else {
ConsentiumUISwitch(
checked = isChecked,
onCheckedChange = {
isChecked = !isChecked
purposeUI.choice = if (isChecked) PurposeStatusUI.ACCEPTED else PurposeStatusUI.REJECTED
purposeUI.vendors.forEach { vendorUI ->
vendorUI.isAccepted = isChecked
}
},
modifier = Modifier
.semantics {
contentDescription = purposeUI.title
}
)
}
/** To enable in v2
Spacer(modifier = Modifier.width(10.dp))
Icon(
modifier = Modifier.size(24.dp),
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
contentDescription = null,
tint = ConsentiumUITheme.colors.onSurface,
)
**/
}
Spacer(modifier = Modifier.height(10.dp))
Text(
text = purposeUI.description.toRichHtmlString(),
style = ConsentiumUITheme.typography.p3,
color = ConsentiumUITheme.colors.onSurface,
)
}
}
@Preview
@Composable
private fun PurposeComponentPreview() {
PurposeComponent(
purposeUI = PurposeUI(
id = "1",
isRequired = true,
choice = PurposeStatusUI.ACCEPTED,
title = "Title",
description = "Description",
vendors = emptyList(),
)
)
}

View File

@ -1,65 +0,0 @@
package fr.openium.consentium_ui.ui.components.core
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
@Composable
internal fun TextLink(
text: String,
onclick: () -> Unit,
color: Color = ConsentiumUITheme.colors.primary,
isLoading: Boolean = false,
) {
val interactionSource = remember { MutableInteractionSource() }
Row(
modifier = Modifier
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = onclick,
)
) {
Text(
text = text,
color = color,
style = ConsentiumUITheme.typography.b3,
textDecoration = TextDecoration.Underline
)
if (isLoading) {
Spacer(modifier = Modifier.width(10.dp))
CircularProgressIndicator(
color = color,
modifier = Modifier.size(14.dp),
strokeWidth = 3.dp
)
}
}
}
@Preview
@Composable
private fun TextLinkPreview() {
TextLink(
text = "Continuer sans accepter",
onclick = {},
isLoading = true,
)
}

View File

@ -1,55 +0,0 @@
package fr.openium.consentium_ui.ui.components.core.button
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
@Composable
internal fun ConsentButton(
text: String,
buttonStyle: ConsentiumUIButtonStyle,
onclick: () -> Unit,
isLoading: Boolean = false,
) {
Button(
onClick = onclick,
modifier = Modifier
.fillMaxWidth()
.height(40.dp),
shape = RoundedCornerShape(6.dp),
colors = ButtonDefaults.buttonColors(buttonStyle.backgroundColor())
) {
if (isLoading) {
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
color = ConsentiumUITheme.colors.onPrimary,
)
} else {
Text(
text = text,
color = ConsentiumUITheme.colors.onPrimary,
style = ConsentiumUITheme.typography.button,
)
}
}
}
@Preview
@Composable
private fun ConsentButtonPreview() {
ConsentButton(
text = "Accept and close",
buttonStyle = ConsentiumUIButtonStyle.PRIMARY,
onclick = {}
)
}

View File

@ -1,18 +0,0 @@
package fr.openium.consentium_ui.ui.components.core.button
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
internal enum class ConsentiumUIButtonStyle {
PRIMARY,
SECONDARY,
}
@Composable
internal fun ConsentiumUIButtonStyle.backgroundColor(): Color {
return when (this) {
ConsentiumUIButtonStyle.PRIMARY -> ConsentiumUITheme.colors.primary
ConsentiumUIButtonStyle.SECONDARY -> ConsentiumUITheme.colors.secondary
}
}

View File

@ -1,46 +0,0 @@
package fr.openium.consentium_ui.ui.components.core.toggle
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import fr.openium.consentium_ui.ui.components.style.ConsentiumUITheme
@Composable
internal fun ConsentiumUISwitch(
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
Switch(
checked = checked,
onCheckedChange = onCheckedChange,
colors = SwitchDefaults.colors(
checkedTrackColor = ConsentiumUITheme.colors.secondary,
),
modifier = modifier,
)
}
@Preview
@Composable
private fun ConsentiumUISwitchPreview() {
Column {
ConsentiumUISwitch(
checked = true,
onCheckedChange = {},
)
Spacer(modifier = Modifier.height(16.dp))
ConsentiumUISwitch(
checked = false,
onCheckedChange = {},
)
}
}

View File

@ -1,181 +0,0 @@
package fr.openium.consentium_ui.ui.components.style
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import androidx.core.graphics.ColorUtils
object ConsentiumDefaults {
private const val OUTLINE_COLOR_BLEND_RATIO = 0.1f
private object Typography {
private val DEFAULT_LINE_HEIGHT = 24.sp
private val LINE_HEIGHT_18 = 18.sp
private val LINE_HEIGHT_22 = 22.sp
private val DEFAULT_LETTER_SPACING = 0.5.sp
val h1 = TextStyle(
fontSize = 32.sp,
fontWeight = FontWeight.SemiBold,
letterSpacing = DEFAULT_LETTER_SPACING
)
val h2 = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.SemiBold,
letterSpacing = DEFAULT_LETTER_SPACING
)
val b1 = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = DEFAULT_LINE_HEIGHT,
letterSpacing = DEFAULT_LETTER_SPACING
)
val p1 = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Normal,
lineHeight = DEFAULT_LINE_HEIGHT,
letterSpacing = DEFAULT_LETTER_SPACING
)
val b2 = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = LINE_HEIGHT_22,
letterSpacing = DEFAULT_LETTER_SPACING
)
val p2 = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
lineHeight = LINE_HEIGHT_22,
letterSpacing = DEFAULT_LETTER_SPACING
)
val b3 = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = LINE_HEIGHT_18,
letterSpacing = DEFAULT_LETTER_SPACING
)
val p3 = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
lineHeight = LINE_HEIGHT_18,
letterSpacing = DEFAULT_LETTER_SPACING
)
val button = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold,
letterSpacing = DEFAULT_LETTER_SPACING
)
val sm2 = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = LINE_HEIGHT_22,
letterSpacing = DEFAULT_LETTER_SPACING
)
}
@Composable
fun colors(
primary: Color = MaterialTheme.colorScheme.primary,
onPrimary: Color = MaterialTheme.colorScheme.onPrimary,
secondary: Color = MaterialTheme.colorScheme.secondary,
onSecondary: Color = MaterialTheme.colorScheme.surfaceVariant,
tertiary: Color = MaterialTheme.colorScheme.onBackground,
onSurfaceVariant: Color = MaterialTheme.colorScheme.onBackground,
onSurface: Color = MaterialTheme.colorScheme.onBackground,
outline: Color = Color(
ColorUtils.blendARGB(
onPrimary.toArgb(),
primary.toArgb(),
OUTLINE_COLOR_BLEND_RATIO,
)
),
error: Color = MaterialTheme.colorScheme.error,
surfaceHighest: Color = MaterialTheme.colorScheme.surfaceContainerHighest,
surfaceHigh: Color = MaterialTheme.colorScheme.surfaceContainerHigh,
surfaceMiddle: Color = MaterialTheme.colorScheme.surfaceContainer,
success: Color = Color(0xFF479B3F),
): ConsentiumColors = ConsentiumColors(
primary = primary,
onPrimary = onPrimary,
secondary = secondary,
onSecondary = onSecondary,
tertiary = tertiary,
onSurfaceVariant = onSurfaceVariant,
onSurface = onSurface,
outline = outline,
error = error,
surfaceHighest = surfaceHighest,
surfaceHigh = surfaceHigh,
surfaceMiddle = surfaceMiddle,
success = success,
)
@Composable
fun typography(
h1: TextStyle = LocalTextStyle.current.merge(Typography.h1),
h2: TextStyle = LocalTextStyle.current.merge(Typography.h2),
b1: TextStyle = LocalTextStyle.current.merge(Typography.b1),
p1: TextStyle = LocalTextStyle.current.merge(Typography.p1),
b2: TextStyle = LocalTextStyle.current.merge(Typography.b2),
p2: TextStyle = LocalTextStyle.current.merge(Typography.p2),
b3: TextStyle = LocalTextStyle.current.merge(Typography.b3),
p3: TextStyle = LocalTextStyle.current.merge(Typography.p3),
button: TextStyle = LocalTextStyle.current.merge(Typography.button),
sm2: TextStyle = LocalTextStyle.current.merge(Typography.sm2),
): ConsentiumTypography = ConsentiumTypography(
h1 = h1,
h2 = h2,
b1 = b1,
p1 = p1,
b2 = b2,
p2 = p2,
b3 = b3,
p3 = p3,
button = button,
sm2 = sm2,
)
}
data class ConsentiumColors internal constructor(
val primary: Color,
val onPrimary: Color,
val secondary: Color,
val onSecondary: Color,
val tertiary: Color,
val onSurfaceVariant: Color,
val onSurface: Color,
val outline: Color,
val error: Color,
val surfaceHighest: Color,
val surfaceHigh: Color,
val surfaceMiddle: Color,
val success: Color,
)
data class ConsentiumTypography internal constructor(
val h1: TextStyle,
val h2: TextStyle,
val b1: TextStyle,
val p1: TextStyle,
val b2: TextStyle,
val p2: TextStyle,
val b3: TextStyle,
val p3: TextStyle,
val button: TextStyle,
val sm2: TextStyle,
)

View File

@ -1,52 +0,0 @@
package fr.openium.consentium_ui.ui.components.style
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
internal val LocalColors = staticCompositionLocalOf {
ConsentiumColors(
primary = Color.Unspecified,
onPrimary = Color.Unspecified,
secondary = Color.Unspecified,
onSecondary = Color.Unspecified,
tertiary = Color.Unspecified,
onSurfaceVariant = Color.Unspecified,
onSurface = Color.Unspecified,
outline = Color.Unspecified,
error = Color.Unspecified,
surfaceHighest = Color.Unspecified,
surfaceHigh = Color.Unspecified,
surfaceMiddle = Color.Unspecified,
success = Color.Unspecified,
)
}
internal val LocalTypography = staticCompositionLocalOf {
ConsentiumTypography(
h1 = TextStyle.Default,
h2 = TextStyle.Default,
b1 = TextStyle.Default,
p1 = TextStyle.Default,
b2 = TextStyle.Default,
p2 = TextStyle.Default,
b3 = TextStyle.Default,
p3 = TextStyle.Default,
button = TextStyle.Default,
sm2 = TextStyle.Default,
)
}
internal object ConsentiumUITheme {
val colors: ConsentiumColors
@Composable
@ReadOnlyComposable
get() = LocalColors.current
val typography: ConsentiumTypography
@Composable
@ReadOnlyComposable
get() = LocalTypography.current
}

View File

@ -1,7 +0,0 @@
package fr.openium.consentium_ui.ui.model
enum class ConsentiumPageUI {
GENERAL_CONSENT,
DETAILS_CONSENT,
COOKIES_POLICY,
}

View File

@ -1,7 +0,0 @@
package fr.openium.consentium_ui.ui.model
internal data class DetailConsentUI(
val cookiePolicyUrl: String,
val conservationDurationText: String,
val purposes: List<PurposeUI>,
)

View File

@ -1,8 +0,0 @@
package fr.openium.consentium_ui.ui.model
internal data class GeneralConsentUI(
val applicationName: String,
val iconUrl: String?,
val mainConsentText: String,
val consentPageUrl: String,
)

View File

@ -1,7 +0,0 @@
package fr.openium.consentium_ui.ui.model
internal enum class LoadingElement {
BUTTON,
LINK,
NONE,
}

View File

@ -1,7 +0,0 @@
package fr.openium.consentium_ui.ui.model
internal enum class PurposeStatusUI {
ACCEPTED,
REJECTED,
PARTIAL,
}

View File

@ -1,10 +0,0 @@
package fr.openium.consentium_ui.ui.model
internal data class PurposeUI(
val id: String,
val isRequired: Boolean,
var choice: PurposeStatusUI,
val title: String,
val description: String,
val vendors: List<VendorUI>,
)

View File

@ -1,6 +0,0 @@
package fr.openium.consentium_ui.ui.model
internal data class VendorUI(
val id: String,
var isAccepted: Boolean,
)

Some files were not shown because too many files have changed in this diff Show More