Compare commits

...

3 Commits

15 changed files with 317 additions and 8 deletions

View File

@ -7,6 +7,9 @@ plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.hilt)
alias(libs.plugins.kotlin.serialization)
}
// Keystore
@ -88,6 +91,7 @@ dependencies {
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
implementation(libs.hilt.navigation.compose)
implementation(libs.matomo)
// Compose
implementation(platform(libs.compose.bom))
@ -96,9 +100,16 @@ dependencies {
// 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

@ -3,6 +3,7 @@
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"

View File

@ -0,0 +1,13 @@
package fr.openium.consentium
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class DemoApplication : Application() {
override fun onCreate() {
super.onCreate()
}
}

View File

@ -1,9 +1,11 @@
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
@ -11,19 +13,28 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
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() ) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
Scaffold( modifier = Modifier.fillMaxSize() ) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
DemoNavGraph(navHostController = navHostController)
}
}
}
}

View File

@ -0,0 +1,48 @@
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.runtime.Composable
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.navigation
import fr.openium.consentium.ui.screens.main.MainScreen
import fr.openium.consentium.ui.screens.splash.SplashScreen
private const val NAV_ANIMATION_TIME = 100
@Composable
fun DemoNavGraph(navHostController: NavHostController) {
NavHost(
navController = navHostController,
startDestination = Destination.Splash,
enterTransition = {
fadeIn(animationSpec = tween(NAV_ANIMATION_TIME))
},
exitTransition = {
fadeOut(animationSpec = tween(NAV_ANIMATION_TIME))
},
) {
composable<Destination.Splash> {
SplashScreen(
navigateToMain = { ->
navHostController.navigate(Destination.Main) {
popUpTo(navHostController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
})
}
composable<Destination.Main> {
MainScreen()
}
}
}

View File

@ -0,0 +1,13 @@
import kotlinx.serialization.Serializable
sealed interface Destination {
@Serializable
data object Splash : Destination
@Serializable
data object Main : Destination
}

View File

@ -0,0 +1,14 @@
package fr.openium.consentium.ui.screens.main
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.hilt.navigation.compose.hiltViewModel
@Composable
fun MainScreen(
viewModel: MainScreenViewModel = hiltViewModel()
) {
Text("Main")
}

View File

@ -0,0 +1,11 @@
package fr.openium.consentium.ui.screens.main
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class MainScreenViewModel @Inject constructor(
) : ViewModel() {
}

View File

@ -0,0 +1,50 @@
package fr.openium.consentium.ui.screens.splash
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
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.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@Composable
fun SplashScreen(
viewModel: SplashScreenViewModel = hiltViewModel(),
navigateToMain: () -> Unit,
) {
val state by viewModel.state.collectAsState()
Text("Splash")
LaunchedEffect(Unit) {
viewModel.initMain()
}
when (val _state = state) {
is SplashScreenViewModel.State.Loading -> Box(
modifier = Modifier.fillMaxSize()
) {
CircularProgressIndicator(
modifier = Modifier
.size(200.dp)
.align(Alignment.Center)
)
//tracking matomo
}
is SplashScreenViewModel.State.Loaded -> {
navigateToMain()
}
}
}

View File

@ -0,0 +1,39 @@
package fr.openium.consentium.ui.screens.splash
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class SplashScreenViewModel @Inject constructor(
) : ViewModel() {
private val _state = MutableStateFlow<State>(State.Loading)
val state: StateFlow<State> = _state
fun initMain() {
val delay = viewModelScope.launch {
delay(1500L)
}
viewModelScope.launch {
_state.value = State.Loading
try {
delay.join()
_state.value = State.Loaded
} catch (e: Exception) {
e.printStackTrace()
}
}
}
sealed interface State {
data object Loading : State
data object Loaded : State
}
}

View File

@ -14,4 +14,13 @@ plugins {
// Agp
alias(libs.plugins.android.library) apply false
//Hilt
alias(libs.plugins.hilt) apply false
//Kotlin serialization
alias(libs.plugins.kotlin.serialization) apply false
}

View File

@ -1,6 +1,8 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.hilt)
alias(libs.plugins.ksp)
}
android {
@ -30,11 +32,17 @@ android {
}
dependencies {
// AndroidX
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
// Preferences data store
implementation(libs.preferencesDataStore)
// Hilt
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
// Test
testImplementation(libs.test.junit)
androidTestImplementation(libs.androidx.junit)

View File

@ -0,0 +1,48 @@
package fr.openium.consentium.data.local
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import java.util.UUID
import javax.inject.Inject
private val DATASTORE_NAME = "consentium_datastore"
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = DATASTORE_NAME)
internal class ConsentiumDataStore @Inject constructor(@ApplicationContext context: Context) {
companion object {
// Installation unique id
private val INSTALLATION_UNIQUE_ID = stringPreferencesKey("INSTALLATION_UNIQUE_ID")
}
private val _dataStore = context.dataStore
fun getInstallationUniqueId(): String =
runBlocking {
val uuid : String ? = _dataStore.data.map { prefs ->
prefs[INSTALLATION_UNIQUE_ID]
}.firstOrNull()
if (uuid == null) {
val newUuid = UUID.randomUUID().toString()
setInstallationUniqueId(newUuid)
newUuid
} else {
uuid
}
}
private suspend fun setInstallationUniqueId(installationUniqueId: String) {
_dataStore.edit { prefs ->
prefs[INSTALLATION_UNIQUE_ID] = installationUniqueId
}
}
}

View File

@ -14,7 +14,7 @@ appcompat = "1.7.0"
lifecycle = "2.8.7"
# Hilt
hilt = "2.50"
hilt = "2.51.1"
hiltNavigation = "1.2.0"
# Compose
@ -31,12 +31,25 @@ junit = "4.13.2"
espressoCore = "3.6.1"
junitExtVersion = "1.2.1"
# Preferences DataStore
preferencesDataStore = "1.1.1"
# Plugins
agp = "8.7.3"
kotlin = "2.0.0"
ksp = "2.0.0-1.0.23"
junitVersion = "1.2.1"
# Matomo
matomo = "4.3"
# Serialization
serialization = "1.7.1"
navigationCompose = "2.8.2"
[libraries]
# AndroidX
@ -51,7 +64,7 @@ androidx-lifecycle-compose = { group = "androidx.lifecycle", name = "lifecycle-r
# Hilt
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" }
hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigation" }
# Timber
@ -67,6 +80,20 @@ compose-material3 = { group = "androidx.compose.material3", name = "material3" }
# Material
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
# Matomo
matomo = { module = "com.github.matomo-org:matomo-sdk-android", version.ref = "matomo" }
# Kotlin serizalization
kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
# 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" }
# Preferences DataStore
preferencesDataStore = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "preferencesDataStore" }
# Test
test-junit = { group = "junit", name = "junit", version.ref = "junit" }
test-androidx-junit = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "junitExtVersion" }
@ -79,6 +106,10 @@ kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
android-library = { id = "com.android.library", version.ref = "agp" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
[bundles]
androidx = ["androidx-core-ktx", "androidx-activity-compose"]

View File

@ -16,6 +16,8 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io/") }
}
}