diff --git a/.github/ci-gradle.properties b/.github/ci-gradle.properties
index 530b0ce9..ce1bed66 100644
--- a/.github/ci-gradle.properties
+++ b/.github/ci-gradle.properties
@@ -1,5 +1,5 @@
#
-# Copyright 2023 Roberto Leinardi.
+# Copyright 2024 Roberto Leinardi.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/.idea/detekt.xml b/.idea/detekt.xml
index 2c14157e..ba7c3b95 100644
--- a/.idea/detekt.xml
+++ b/.idea/detekt.xml
@@ -11,6 +11,8 @@
+
+
diff --git a/apps/forlago/build.gradle.kts b/apps/forlago/build.gradle.kts
index cc7c7c73..47f99e1f 100644
--- a/apps/forlago/build.gradle.kts
+++ b/apps/forlago/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,14 @@
* limitations under the License.
*/
import com.github.triplet.gradle.androidpublisher.ReleaseStatus
+import io.github.reactivecircus.appversioning.SemVer
+import java.time.Instant
+import kotlin.math.log10
+import kotlin.math.pow
plugins {
id("forlago.android-app-conventions")
- id("forlago.app-versioning-conventions")
+ alias(libs.plugins.appversioning)
alias(libs.plugins.tripletplay)
}
@@ -27,7 +31,10 @@ println("Release keystore ${if (useReleaseKeystore) "" else "NOT "}found!")
android {
defaultConfig {
applicationId = config.apps.forlago.applicationId.get()
- setProperty("archivesBaseName", "forlago")
+ setProperty("archivesBaseName", config.apps.forlago.baseName.get())
+
+ manifestPlaceholders["deepLinkScheme"] = config.apps.forlago.deepLinkScheme.get()
+ buildConfigField("String", "DEEP_LINK_SCHEME", "\"${config.apps.forlago.deepLinkScheme.get()}\"")
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" // https://github.com/google/dagger/issues/2033
@@ -80,6 +87,7 @@ android {
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules-benchmark.pro")
}
}
+ buildFeatures.buildConfig = true
}
val serviceAccountCredentialsFile: File = rootProject.file("release/play-account.json")
@@ -93,23 +101,40 @@ if (serviceAccountCredentialsFile.exists()) {
}
println("play-account.json ${if (serviceAccountCredentialsFile.exists()) "" else "NOT "}found!")
+appVersioning {
+ overrideVersionCode { gitTag, _, _ ->
+ val semVer = SemVer.fromGitTag(gitTag)
+ val version = semVer.major * 10000 + semVer.minor * 100 + semVer.patch
+ val versionLength = (log10(version.toDouble()) + 1).toInt()
+ var epoch = Instant.now().epochSecond.toInt()
+ epoch -= epoch % 10.0.pow(versionLength.toDouble()).toInt()
+ version + epoch
+ }
+
+ overrideVersionName { gitTag, _, variantInfo ->
+ "${gitTag.rawTagName}${if (variantInfo.buildType == "debug") "-dev" else ""} (${gitTag.commitHash})"
+ }
+}
+
dependencies {
// Modules
implementation(projects.modules.featureAccount)
implementation(projects.modules.featureBar)
implementation(projects.modules.featureDebug)
implementation(projects.modules.featureFoo)
+ implementation(projects.modules.featureLogin)
+ implementation(projects.modules.featureLogout)
implementation(projects.modules.libraryAndroid)
implementation(projects.modules.libraryI18n)
implementation(projects.modules.libraryLogging)
implementation(projects.modules.libraryNavigation)
implementation(projects.modules.libraryNetwork)
implementation(projects.modules.libraryPreferences)
+ implementation(projects.modules.libraryRemoteConfig)
implementation(projects.modules.libraryUi)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.lifecycle.process)
- implementation(libs.androidx.lifecycle.runtime)
implementation(libs.androidx.profileinstaller) // Need this to side load a Baseline Profile when Benchmarking
implementation(libs.androidx.startup)
diff --git a/apps/forlago/src/androidTest/kotlin/com/leinardi/forlago/ExampleInstrumentedTest.kt b/apps/forlago/src/androidTest/kotlin/com/leinardi/forlago/ExampleInstrumentedTest.kt
index 7df9efd4..697391fb 100644
--- a/apps/forlago/src/androidTest/kotlin/com/leinardi/forlago/ExampleInstrumentedTest.kt
+++ b/apps/forlago/src/androidTest/kotlin/com/leinardi/forlago/ExampleInstrumentedTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/apps/forlago/src/debug/AndroidManifest.xml b/apps/forlago/src/debug/AndroidManifest.xml
index b71d3c9f..97221070 100644
--- a/apps/forlago/src/debug/AndroidManifest.xml
+++ b/apps/forlago/src/debug/AndroidManifest.xml
@@ -1,5 +1,5 @@
@@ -51,7 +46,7 @@
-
+
diff --git a/apps/forlago/src/main/kotlin/com/leinardi/forlago/Forlago.kt b/apps/forlago/src/main/kotlin/com/leinardi/forlago/Forlago.kt
index 13823ce8..38b3dd2c 100644
--- a/apps/forlago/src/main/kotlin/com/leinardi/forlago/Forlago.kt
+++ b/apps/forlago/src/main/kotlin/com/leinardi/forlago/Forlago.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@
package com.leinardi.forlago
import android.app.Application
-import android.os.Build
-import android.os.StrictMode
import android.os.SystemClock
import android.util.Log
import coil.ImageLoader
@@ -26,8 +24,10 @@ import coil.ImageLoaderFactory
import coil.disk.DiskCache
import coil.memory.MemoryCache
import coil.util.DebugLogger
+import com.leinardi.forlago.library.android.api.strictmode.configureStrictMode
import com.leinardi.forlago.library.feature.Feature
import com.leinardi.forlago.library.feature.FeatureManager
+import com.leinardi.forlago.library.navigation.api.destination.NavigationDestination
import com.leinardi.forlago.library.network.api.interactor.ReadCertificatePinningEnabledInteractor
import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.runBlocking
@@ -46,52 +46,12 @@ class Forlago : Application(), ImageLoaderFactory {
override fun onCreate() {
super.onCreate()
- configureStrictMode()
+ NavigationDestination.DEEP_LINK_SCHEME = BuildConfig.DEEP_LINK_SCHEME
+ configureStrictMode(runBlocking { readCertificatePinningEnabledInteractor() })
registerFeatures()
simulateHeavyLoad()
}
- private fun configureStrictMode() {
- // This can't be initialized using `androidx.startup.Initializer` or it will cause crashes in 3rd party libs using Content Providers
- // and writing data on the main thread (e.g. LeakCanary and AndroidTestRunner).
- if (BuildConfig.DEBUG) {
- val builderThread = StrictMode.ThreadPolicy.Builder()
- .detectAll()
- .permitDiskReads()
- .permitCustomSlowCalls()
- .penaltyLog()
- .penaltyDeath()
- .detectResourceMismatches()
- StrictMode.setThreadPolicy(builderThread.build())
-
- val builderVM = StrictMode.VmPolicy.Builder()
- .detectActivityLeaks()
- .detectLeakedSqlLiteObjects()
- .detectLeakedRegistrationObjects()
- .detectFileUriExposure()
- .penaltyLog()
- .penaltyDeath()
- .detectContentUriWithoutPermission()
- // .detectUntaggedSockets() // https://github.com/square/okhttp/issues/3537#issuecomment-974861679
- .apply {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- detectCredentialProtectedWhileLocked()
- detectImplicitDirectBoot()
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- detectIncorrectContextUse()
- detectUnsafeIntentLaunch()
- }
- runBlocking {
- if (readCertificatePinningEnabledInteractor()) {
- detectCleartextNetwork()
- }
- }
- }
- StrictMode.setVmPolicy(builderVM.build())
- }
- }
-
@Suppress("MagicNumber")
private fun simulateHeavyLoad() {
SystemClock.sleep(1500)
diff --git a/apps/forlago/src/main/kotlin/com/leinardi/forlago/di/AppEntryPoints.kt b/apps/forlago/src/main/kotlin/com/leinardi/forlago/di/AppEntryPoints.kt
index e9707603..3c954286 100644
--- a/apps/forlago/src/main/kotlin/com/leinardi/forlago/di/AppEntryPoints.kt
+++ b/apps/forlago/src/main/kotlin/com/leinardi/forlago/di/AppEntryPoints.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/apps/forlago/src/main/kotlin/com/leinardi/forlago/di/AppModule.kt b/apps/forlago/src/main/kotlin/com/leinardi/forlago/di/AppModule.kt
index e59d1df9..8429d5b6 100644
--- a/apps/forlago/src/main/kotlin/com/leinardi/forlago/di/AppModule.kt
+++ b/apps/forlago/src/main/kotlin/com/leinardi/forlago/di/AppModule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,12 +18,7 @@ package com.leinardi.forlago.di
import android.app.Application
import com.leinardi.forlago.feature.account.AccountFeature
-import com.leinardi.forlago.feature.account.api.interactor.account.RemoveAccountsInteractor
-import com.leinardi.forlago.feature.account.api.interactor.token.InvalidateAccessTokenInteractor
-import com.leinardi.forlago.feature.account.api.interactor.token.InvalidateRefreshTokenInteractor
-import com.leinardi.forlago.library.android.api.interactor.android.DeleteWebViewDataInteractor
import com.leinardi.forlago.library.feature.Feature
-import com.leinardi.forlago.library.navigation.api.navigator.ForlagoNavigator
import com.leinardi.forlago.ui.MainActivity
import dagger.Module
import dagger.Provides
@@ -40,17 +35,8 @@ object AppModule {
@IntoSet
fun provideAccountFeature(
application: Application,
- deleteWebViewDataInteractor: DeleteWebViewDataInteractor,
- invalidateAccessTokenInteractor: InvalidateAccessTokenInteractor,
- invalidateRefreshTokenInteractor: InvalidateRefreshTokenInteractor,
- navigator: ForlagoNavigator,
- removeAccountsInteractor: RemoveAccountsInteractor,
- ): Feature = AccountFeature(
- deleteWebViewDataInteractor = deleteWebViewDataInteractor,
- invalidateAccessTokenInteractor = invalidateAccessTokenInteractor,
- invalidateRefreshTokenInteractor = invalidateRefreshTokenInteractor,
+ accountFeatureFactory: AccountFeature.Factory,
+ ): Feature = accountFeatureFactory.create(
mainActivityIntent = MainActivity.createIntent(application),
- navigator = navigator,
- removeAccountsInteractor = removeAccountsInteractor,
)
}
diff --git a/apps/forlago/src/main/kotlin/com/leinardi/forlago/navigation/GraphDestinations.kt b/apps/forlago/src/main/kotlin/com/leinardi/forlago/navigation/GraphDestinations.kt
index 1dd98c86..0df9f753 100644
--- a/apps/forlago/src/main/kotlin/com/leinardi/forlago/navigation/GraphDestinations.kt
+++ b/apps/forlago/src/main/kotlin/com/leinardi/forlago/navigation/GraphDestinations.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainActivity.kt b/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainActivity.kt
index 952837bd..5ef17991 100644
--- a/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainActivity.kt
+++ b/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainActivity.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,6 +40,8 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.compose.LifecycleEventEffect
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
@@ -95,8 +97,9 @@ class MainActivity : AppCompatActivity() { // AppCompatActivity is needed to be
ForlagoTheme(dynamicColor = viewModel.viewState.value.dynamicColors) {
ForlagoMainScreen(
effectFlow = viewModel.effect,
- startDestination = viewModel.viewState.value.startDestination,
forlagoNavigator = forlagoNavigator,
+ sendEvent = { viewModel.onUiEvent(it) },
+ startDestination = viewModel.viewState.value.startDestination,
)
}
}
@@ -109,11 +112,6 @@ class MainActivity : AppCompatActivity() { // AppCompatActivity is needed to be
viewModel.onUiEvent(Event.OnIntentReceived(intent, true))
}
- override fun onResume() {
- super.onResume()
- viewModel.onUiEvent(Event.OnShown)
- }
-
override fun onDestroy() {
// Workaround to prevent executing a deep link again on activity recreated (e.g. theme change)
if (intent.action == Intent.ACTION_VIEW) {
@@ -157,14 +155,18 @@ class MainActivity : AppCompatActivity() { // AppCompatActivity is needed to be
}
@OptIn(ExperimentalMaterialNavigationApi::class)
-@Suppress("ReusedModifierInstance")
+@Suppress("ReusedModifierInstance", "ModifierNotUsedAtRoot")
@Composable
fun ForlagoMainScreen(
effectFlow: Flow,
forlagoNavigator: ForlagoNavigator,
+ sendEvent: (event: Event) -> Unit,
startDestination: String,
modifier: Modifier = Modifier,
) {
+ LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
+ sendEvent(Event.OnActivityResumed)
+ }
val bottomSheetNavigator = rememberBottomSheetNavigator(skipHalfExpanded = true)
val navHostController = rememberNavController(bottomSheetNavigator)
val activity = LocalContext.current.requireActivity() as MainActivity
@@ -178,8 +180,14 @@ fun ForlagoMainScreen(
).also { Timber.d("Navigate to ${event.destination}") }
is NavigatorEvent.HandleDeepLink -> navHostController.handleDeepLink(event.intent)
- is NavigatorEvent.NavigateUp -> navHostController.navigateUp().also { Timber.d("Navigate Up successful = $it") }
is NavigatorEvent.NavigateBack -> navHostController.popBackStack().also { Timber.d("NavigateBack successful = $it") }
+ is NavigatorEvent.NavigateBackOrHome -> if (navHostController.previousBackStackEntry != null) {
+ navHostController.popBackStack()
+ } else {
+ forlagoNavigator.navigateHome()
+ }
+
+ is NavigatorEvent.NavigateUp -> navHostController.navigateUp().also { Timber.d("Navigate Up successful = $it") }
}
}.launchIn(this)
}
diff --git a/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainContract.kt b/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainContract.kt
index f331ac3c..ca1a1d3c 100644
--- a/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainContract.kt
+++ b/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainContract.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,12 +18,14 @@ package com.leinardi.forlago.ui
import android.content.Intent
import androidx.annotation.StringRes
+import androidx.compose.runtime.Immutable
import com.google.android.play.core.appupdate.AppUpdateInfo
import com.google.android.play.core.install.model.AppUpdateType
import com.leinardi.forlago.library.ui.base.ViewEffect
import com.leinardi.forlago.library.ui.base.ViewEvent
import com.leinardi.forlago.library.ui.base.ViewState
+@Immutable
object MainContract {
data class State(
val startDestination: String,
@@ -32,9 +34,9 @@ object MainContract {
sealed class Event : ViewEvent {
data class OnIntentReceived(val intent: Intent, val isNewIntent: Boolean = false) : Event()
- object OnInAppUpdateCancelled : Event()
- object OnInAppUpdateFailed : Event()
- object OnShown : Event()
+ data object OnActivityResumed : Event()
+ data object OnInAppUpdateCancelled : Event()
+ data object OnInAppUpdateFailed : Event()
}
sealed class Effect : ViewEffect {
@@ -44,7 +46,7 @@ object MainContract {
) : Effect()
data class StartUpdateFlowForResult(val appUpdateInfo: AppUpdateInfo, @AppUpdateType val appUpdateType: Int) : Effect()
- object FinishActivity : Effect()
- object ShowSnackbarForCompleteUpdate : Effect()
+ data object FinishActivity : Effect()
+ data object ShowSnackbarForCompleteUpdate : Effect()
}
}
diff --git a/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainViewModel.kt b/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainViewModel.kt
index 6970ff8e..d9a6a518 100644
--- a/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainViewModel.kt
+++ b/apps/forlago/src/main/kotlin/com/leinardi/forlago/ui/MainViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,13 +17,11 @@
package com.leinardi.forlago.ui
import android.app.Application
-import androidx.lifecycle.DefaultLifecycleObserver
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.viewModelScope
import com.google.android.play.core.appupdate.AppUpdateInfo
import com.google.android.play.core.install.model.AppUpdateType
-import com.leinardi.forlago.feature.account.api.interactor.account.LogOutInteractor
+import com.leinardi.forlago.feature.debug.api.interactor.DebugShakeDetectorInteractor
+import com.leinardi.forlago.feature.logout.api.interactor.LogOutInteractor
import com.leinardi.forlago.library.android.api.ext.ifTrue
import com.leinardi.forlago.library.android.api.interactor.android.GetAppUpdateInfoInteractor
import com.leinardi.forlago.library.android.api.interactor.android.GetAppUpdateInfoInteractor.Result.DeveloperTriggeredUpdateInProgress
@@ -31,11 +29,11 @@ import com.leinardi.forlago.library.android.api.interactor.android.GetAppUpdateI
import com.leinardi.forlago.library.android.api.interactor.android.GetAppUpdateInfoInteractor.Result.ImmediateUpdateAvailable
import com.leinardi.forlago.library.android.api.interactor.android.GetAppUpdateInfoInteractor.Result.LowPriorityUpdateAvailable
import com.leinardi.forlago.library.android.api.interactor.android.GetAppUpdateInfoInteractor.Result.UpdateNotAvailable
-import com.leinardi.forlago.library.android.api.interactor.android.GetInstallStateUpdateFlowInteractor
+import com.leinardi.forlago.library.android.api.interactor.android.GetInstallStateUpdateStreamInteractor
import com.leinardi.forlago.library.feature.interactor.GetFeaturesInteractor
import com.leinardi.forlago.library.navigation.api.navigator.ForlagoNavigator
-import com.leinardi.forlago.library.ui.api.interactor.GetMaterialYouFlowInteractor
-import com.leinardi.forlago.library.ui.api.interactor.GetThemeFlowInteractor
+import com.leinardi.forlago.library.ui.api.interactor.GetMaterialYouStreamInteractor
+import com.leinardi.forlago.library.ui.api.interactor.GetThemeStreamInteractor
import com.leinardi.forlago.library.ui.base.BaseViewModel
import com.leinardi.forlago.library.ui.interactor.SetNightModeInteractor
import com.leinardi.forlago.ui.MainContract.Effect
@@ -50,49 +48,51 @@ import javax.inject.Inject
@HiltViewModel
class MainViewModel @Inject constructor(
- getInstallStateUpdateFlowInteractor: GetInstallStateUpdateFlowInteractor,
- getMaterialYouFlowInteractor: GetMaterialYouFlowInteractor,
- getThemeFlowInteractor: GetThemeFlowInteractor,
+ getInstallStateUpdateStreamInteractor: GetInstallStateUpdateStreamInteractor,
+ getMaterialYouStreamInteractor: GetMaterialYouStreamInteractor,
+ getThemeStreamInteractor: GetThemeStreamInteractor,
private val app: Application,
+ private val debugShakeDetectorInteractor: DebugShakeDetectorInteractor,
private val forlagoNavigator: ForlagoNavigator,
private val getAppUpdateInfoInteractor: GetAppUpdateInfoInteractor,
private val getFeaturesInteractor: GetFeaturesInteractor,
private val logOutInteractor: LogOutInteractor,
setNightModeInteractor: SetNightModeInteractor,
-) : BaseViewModel(), DefaultLifecycleObserver {
+) : BaseViewModel() {
@AppUpdateType private var appUpdateType: Int? = null
init {
- getThemeFlowInteractor()
+ getThemeStreamInteractor()
.onEach { nightMode ->
- if (!logOutInteractor.isSignOutInProgress()) {
+ if (!logOutInteractor.isLogOutInProgress()) {
setNightModeInteractor(nightMode)
}
}
.launchIn(viewModelScope)
- getMaterialYouFlowInteractor()
+ getMaterialYouStreamInteractor()
.onEach { updateState { copy(dynamicColors = it) } }
.launchIn(viewModelScope)
- getInstallStateUpdateFlowInteractor()
+ getInstallStateUpdateStreamInteractor()
.onEach { result ->
- if (result is GetInstallStateUpdateFlowInteractor.Result.Downloaded) {
+ if (result is GetInstallStateUpdateStreamInteractor.Result.Downloaded) {
sendEffect { Effect.ShowSnackbarForCompleteUpdate }
}
}
.launchIn(viewModelScope)
checkForUpdates()
- ProcessLifecycleOwner.get().lifecycle.addObserver(this)
+ debugShakeDetectorInteractor.startObserving()
}
- override fun onResume(owner: LifecycleOwner) {
- super.onResume(owner)
- checkForUpdates(true)
+ override fun onCleared() {
+ super.onCleared()
+ debugShakeDetectorInteractor.stopObserving()
}
override fun provideInitialState() = State(forlagoNavigator.homeDestination)
override fun handleEvent(event: Event) {
when (event) {
+ is Event.OnActivityResumed -> checkForUpdates(true)
is Event.OnInAppUpdateCancelled -> {
Timber.d("In-App update cancelled")
if (appUpdateType == AppUpdateType.IMMEDIATE) {
@@ -101,14 +101,17 @@ class MainViewModel @Inject constructor(
}
}
- is Event.OnInAppUpdateFailed -> sendEffect {
- Effect.ShowErrorSnackbar(
- app.getString(com.leinardi.forlago.library.i18n.R.string.i18n_app_update_error),
- )
+ is Event.OnInAppUpdateFailed -> viewModelScope.launch {
+ debounceError {
+ sendEffect {
+ Effect.ShowErrorSnackbar(
+ app.getString(com.leinardi.forlago.library.i18n.R.string.i18n_app_update_error),
+ )
+ }
+ }
}
is Event.OnIntentReceived -> handleOnIntentReceived(event)
- is Event.OnShown -> checkForUpdates(true)
}
}
@@ -117,7 +120,7 @@ class MainViewModel @Inject constructor(
var handled = false
getFeaturesInteractor().forEach { feature ->
if (!handled) {
- feature.handleIntent(event.intent).ifTrue { handled = true }
+ feature.handleIntent(event.intent, forlagoNavigator).ifTrue { handled = true }
}
}
if (!handled && event.isNewIntent) {
diff --git a/apps/forlago/src/main/res/drawable/ui_ic_launcher_foreground.xml b/apps/forlago/src/main/res/drawable/ui_ic_launcher_foreground.xml
index 1fcd05d7..170302e2 100644
--- a/apps/forlago/src/main/res/drawable/ui_ic_launcher_foreground.xml
+++ b/apps/forlago/src/main/res/drawable/ui_ic_launcher_foreground.xml
@@ -1,5 +1,5 @@
@@ -99,13 +94,13 @@
android:authorities="com.leinardi.forlago.androidx-startup"
android:exported="false" >
+
+
+
@@ -159,6 +163,9 @@
+
diff --git a/apps/forlago/versions/mergedManifests/benchmark/AndroidManifest.xml b/apps/forlago/versions/mergedManifests/benchmark/AndroidManifest.xml
deleted file mode 100644
index d5798c35..00000000
--- a/apps/forlago/versions/mergedManifests/benchmark/AndroidManifest.xml
+++ /dev/null
@@ -1,266 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/apps/forlago/versions/mergedManifests/debug/AndroidManifest.xml b/apps/forlago/versions/mergedManifests/debug/AndroidManifest.xml
deleted file mode 100644
index bbc75afa..00000000
--- a/apps/forlago/versions/mergedManifests/debug/AndroidManifest.xml
+++ /dev/null
@@ -1,351 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/art/project.dot.png b/art/project.dot.png
index 47d9dddf..faa31cda 100644
Binary files a/art/project.dot.png and b/art/project.dot.png differ
diff --git a/build-conventions/build.gradle.kts b/build-conventions/build.gradle.kts
index de729704..99ac62f8 100644
--- a/build-conventions/build.gradle.kts
+++ b/build-conventions/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,16 +23,18 @@ plugins {
dependencies {
+ implementation(libs.kotlinx.serialization)
implementation(libs.plugin.aboutlibraries)
implementation(libs.plugin.android.gradle)
implementation(libs.plugin.androidcachefix)
- implementation(libs.plugin.appversioning)
+ implementation(libs.plugin.apollo)
+ implementation(libs.plugin.apollo.external)
+ implementation(libs.plugin.dagger.hilt)
implementation(libs.plugin.detekt)
implementation(libs.plugin.easylauncher)
implementation(libs.plugin.firebase.crashlytics)
implementation(libs.plugin.firebase.perf)
implementation(libs.plugin.google.services)
- implementation(libs.plugin.dagger.hilt)
implementation(libs.plugin.kotlin)
implementation(libs.plugin.ksp)
implementation(libs.plugin.ruler)
diff --git a/build-conventions/settings.gradle.kts b/build-conventions/settings.gradle.kts
index e2a6c121..f7d74d7a 100644
--- a/build-conventions/settings.gradle.kts
+++ b/build-conventions/settings.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ pluginManagement {
// https://docs.gradle.org/7.0/userguide/declaring_dependencies.html#sec:type-safe-project-accessors
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
+@Suppress("UnstableApiUsage")
dependencyResolutionManagement {
repositories {
gradlePluginPortal()
diff --git a/build-conventions/src/main/kotlin/com/leinardi/forlago/apollo/ChangeAdaptersModifierToPublicHooks.kt b/build-conventions/src/main/kotlin/com/leinardi/forlago/apollo/ChangeAdaptersModifierToPublicHooks.kt
new file mode 100644
index 00000000..33bb4c34
--- /dev/null
+++ b/build-conventions/src/main/kotlin/com/leinardi/forlago/apollo/ChangeAdaptersModifierToPublicHooks.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.apollo
+
+import com.apollographql.apollo3.compiler.hooks.ApolloCompilerKotlinHooks.FileInfo
+import com.apollographql.apollo3.compiler.hooks.DefaultApolloCompilerKotlinHooks
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.ParameterizedTypeName
+import com.squareup.kotlinpoet.TypeSpec
+
+/**
+ * Change the Adapters visibility from private to public.
+ *
+ * https://github.com/apollographql/apollo-kotlin/issues/5585
+ */
+class ChangeAdaptersModifierToPublicHooks : DefaultApolloCompilerKotlinHooks() {
+ override val version = "ChangeAdaptersModifierToPublicHooks.0"
+
+ override fun postProcessFiles(files: Collection): Collection = files.map {
+ it.copy(
+ fileSpec = it.fileSpec
+ .toBuilder()
+ .apply { members.replaceAll { member -> if (member is TypeSpec) member.changeAdaptersModifierToPublic() else member } }
+ .build(),
+ )
+ }
+
+ private fun TypeSpec.changeAdaptersModifierToPublic(): TypeSpec = toBuilder()
+ .apply {
+ if (superinterfaces
+ .keys
+ .filterIsInstance()
+ .any { it.rawType == ClassName("com.apollographql.apollo3.api", "Adapter") }
+ ) {
+ modifiers.removeIf { it == KModifier.PRIVATE }
+ addModifiers(KModifier.PUBLIC)
+ }
+
+ // Recurse on nested types
+ typeSpecs.replaceAll { it.changeAdaptersModifierToPublic() }
+ }
+ .build()
+}
diff --git a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/AndroidConfigExt.kt b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/AndroidConfigExt.kt
index f1320de9..b8340644 100644
--- a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/AndroidConfigExt.kt
+++ b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/AndroidConfigExt.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/AppsConfigExt.kt b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/AppsConfigExt.kt
index 0587a1af..b4034f23 100644
--- a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/AppsConfigExt.kt
+++ b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/AppsConfigExt.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,11 +17,8 @@
package com.leinardi.forlago.ext
import org.gradle.api.plugins.ExtensionAware
-import org.gradle.api.provider.Property
import org.gradle.kotlin.dsl.getByType
-interface AppsConfigExt : ExtensionAware {
- val deepLinkSchema: Property
-}
+interface AppsConfigExt : ExtensionAware
internal inline val ConfigExt.apps: AppsConfigExt get() = extensions.getByType()
diff --git a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ConfigExt.kt b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ConfigExt.kt
index db386709..7bf7cf78 100644
--- a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ConfigExt.kt
+++ b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ConfigExt.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ExtraPropertiesExtensionExt.kt b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ExtraPropertiesExtensionExt.kt
index f193b5d3..688934e2 100644
--- a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ExtraPropertiesExtensionExt.kt
+++ b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ExtraPropertiesExtensionExt.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ForlagoAppsConfigExt.kt b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ForlagoAppsConfigExt.kt
index c0a449a3..50f8c02a 100644
--- a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ForlagoAppsConfigExt.kt
+++ b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ForlagoAppsConfigExt.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,8 @@ import org.gradle.kotlin.dsl.getByType
interface ForlagoAppsConfigExt : ExtensionAware {
val applicationId: Property
+ val baseName: Property
+ val deepLinkScheme: Property
}
internal inline val AppsConfigExt.forlago: ForlagoAppsConfigExt get() = extensions.getByType()
diff --git a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ParamsConfigExt.kt b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ParamsConfigExt.kt
index da5a2e5c..8a67e8d9 100644
--- a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ParamsConfigExt.kt
+++ b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/ParamsConfigExt.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/StringExt.kt b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/StringExt.kt
index d0a3a94d..1ce2408d 100644
--- a/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/StringExt.kt
+++ b/build-conventions/src/main/kotlin/com/leinardi/forlago/ext/StringExt.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build-conventions/src/main/kotlin/forlago.android-app-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.android-app-conventions.gradle.kts
index c09047d7..3ed09da8 100644
--- a/build-conventions/src/main/kotlin/forlago.android-app-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.android-app-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,8 +31,9 @@ plugins {
id("com.starter.easylauncher")
id("forlago.ruler-conventions")
}
+
val libs = the()
-val applyGsmServicesPlugins = rootProject.file("apps/forlago/google-services.json").exists()
+val applyGsmServicesPlugins = project.file("google-services.json").exists()
if (applyGsmServicesPlugins) {
plugins.apply("com.google.gms.google-services")
plugins.apply("com.google.firebase.crashlytics")
@@ -78,12 +79,12 @@ dependencies {
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime)
+ implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.navigation.fragment)
implementation(libs.androidx.navigation.ui.ktx)
implementation(libs.coroutines.android)
implementation(libs.coroutines.core)
implementation(libs.dagger.hilt.android)
- implementation(libs.hilt.navigation.compose)
implementation(libs.kotlin.result)
implementation(libs.kotlinx.collections.immutable)
implementation(libs.timber)
@@ -101,5 +102,5 @@ tasks.register("installGitHooks") {
}
afterEvaluate {
- tasks.named("preBuild").dependsOn("installGitHooks")
+ tasks.named("build").dependsOn("installGitHooks")
}
diff --git a/build-conventions/src/main/kotlin/forlago.android-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.android-conventions.gradle.kts
index 9939c3ed..b3a9e315 100644
--- a/build-conventions/src/main/kotlin/forlago.android-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.android-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@ import com.android.build.gradle.api.AndroidBasePlugin
import com.leinardi.forlago.ext.android
import com.leinardi.forlago.ext.apps
import com.leinardi.forlago.ext.config
-import org.gradle.accessors.dm.LibrariesForLibs
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -29,8 +28,6 @@ plugins {
id("forlago.config-conventions")
}
-val libs = the()
-
plugins.withType().configureEach {
extensions.configure {
compileSdkVersion(config.android.compileSdk.get())
@@ -39,9 +36,6 @@ plugins.withType().configureEach {
minSdk = config.android.minSdk.get()
targetSdk = config.android.targetSdk.get()
- manifestPlaceholders["deepLinkSchema"] = config.apps.deepLinkSchema.get()
- buildConfigField("String", "DEEP_LINK_SCHEMA", "\"${config.apps.deepLinkSchema.get()}\"")
-
testInstrumentationRunner = "com.leinardi.forlago.library.test.runner.HiltTestRunner"
// The following argument makes the Android Test Orchestrator run its
// "pm clear" command after each test invocation. This command ensures
@@ -49,8 +43,6 @@ plugins.withType().configureEach {
setTestInstrumentationRunnerArguments(mutableMapOf("clearPackageData" to "true"))
}
- buildFeatures.buildConfig = true
-
compileOptions {
sourceCompatibility = config.android.javaVersion.get()
targetCompatibility = config.android.javaVersion.get()
@@ -64,7 +56,7 @@ plugins.withType().configureEach {
}
}
- if (this is CommonExtension<*, *, *, *, *>) {
+ if (this is CommonExtension<*, *, *, *, *, *>) {
lint {
abortOnError = true
checkAllWarnings = false
@@ -89,14 +81,15 @@ plugins.withType().configureEach {
packagingOptions {
resources {
// Use this block to exclude conflicting files that breaks your APK assemble task
- excludes.add("META-INF/LICENSE.md")
excludes.add("META-INF/LICENSE-notice.md")
+ excludes.add("META-INF/LICENSE.md")
+ excludes.add("META-INF/NOTICE.md")
}
}
}
}
-fun CommonExtension<*, *, *, *, *>.kotlinOptions(block: KotlinJvmOptions.() -> Unit) {
+fun CommonExtension<*, *, *, *, *, *>.kotlinOptions(block: KotlinJvmOptions.() -> Unit) {
(this as ExtensionAware).extensions.configure("kotlinOptions", block)
}
diff --git a/build-conventions/src/main/kotlin/forlago.android-feature-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.android-feature-conventions.gradle.kts
index 56705e77..ad3418d6 100644
--- a/build-conventions/src/main/kotlin/forlago.android-feature-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.android-feature-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,6 +54,7 @@ dependencies {
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime)
+ implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.navigation.fragment)
implementation(libs.androidx.navigation.ui.ktx)
diff --git a/build-conventions/src/main/kotlin/forlago.android-library-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.android-library-conventions.gradle.kts
index 7fa0ac7c..adab85e7 100644
--- a/build-conventions/src/main/kotlin/forlago.android-library-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.android-library-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,4 +54,3 @@ dependencies {
testImplementation(project(":modules:library-test"))
}
-
diff --git a/build-conventions/src/main/kotlin/forlago.app-versioning-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.app-versioning-conventions.gradle.kts
deleted file mode 100644
index 77015061..00000000
--- a/build-conventions/src/main/kotlin/forlago.app-versioning-conventions.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2023 Roberto Leinardi.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import io.github.reactivecircus.appversioning.SemVer
-import java.time.Instant
-import kotlin.math.log10
-import kotlin.math.pow
-
-plugins {
- id("io.github.reactivecircus.app-versioning")
-}
-
-appVersioning {
- overrideVersionCode { gitTag, _, _ ->
- val semVer = SemVer.fromGitTag(gitTag)
- val version = semVer.major * 10000 + semVer.minor * 100 + semVer.patch
- val versionLength = (log10(version.toDouble()) + 1).toInt()
- var epoch = Instant.now().epochSecond.toInt()
- epoch -= epoch % 10.0.pow(versionLength.toDouble()).toInt()
- version + epoch
- }
- overrideVersionName { gitTag, _, variantInfo ->
- "${gitTag.rawTagName}${if (variantInfo.buildType == "debug") "-dev" else ""} (${gitTag.commitHash})"
- }
-}
diff --git a/build-conventions/src/main/kotlin/forlago.buildlog-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.buildlog-conventions.gradle.kts
index bd0f4717..d9fcd49c 100644
--- a/build-conventions/src/main/kotlin/forlago.buildlog-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.buildlog-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,6 @@ if (config.params.saveBuildLogToFile.get()) {
val buildLogDir = layout.buildDirectory.dir("logs")
mkdir(buildLogDir)
val buildLog = buildLogDir.get().file("buildlog-${datetime}.txt").asFile
-
System.setProperty("org.gradle.color.error", "RED")
val outputListener = StandardOutputListener { output -> buildLog.appendText(output.toString()) }
diff --git a/build-conventions/src/main/kotlin/forlago.config-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.config-conventions.gradle.kts
index e68e0858..e3d62a2c 100644
--- a/build-conventions/src/main/kotlin/forlago.config-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.config-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,10 +32,10 @@ val config = extensions.create("config").apply {
}
extensions.create("apps").apply {
- deepLinkSchema.convention("forlago")
-
extensions.create("forlago").apply {
applicationId.convention("com.leinardi.forlago")
+ baseName.convention("forlago")
+ deepLinkScheme.convention("forlago")
}
}
diff --git a/build-conventions/src/main/kotlin/forlago.dependencies-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.dependencies-conventions.gradle.kts
index 7474d6cf..7d86854c 100644
--- a/build-conventions/src/main/kotlin/forlago.dependencies-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.dependencies-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,11 @@ afterEvaluate {
val compileDependencyReportTask = tasks.register("generateRuntimeDependenciesReport") {
description = "Generates a text file containing the Runtime classpath dependencies."
}
- project.configurations.filter { it.name.contains("RuntimeClasspath") }.forEach { configuration ->
+ project.configurations.filter { config ->
+ !config.name.contains("TestRuntimeClasspath") &&
+ !config.name.startsWith("benchmark") &&
+ config.name.contains("RuntimeClasspath")
+ }.forEach { configuration ->
val configurationTask = tasks.register(
"generate${configuration.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString() }}DependenciesReport",
DependencyReportTask::class.java,
diff --git a/build-conventions/src/main/kotlin/forlago.dependency-graph-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.dependency-graph-conventions.gradle.kts
index 52ccb487..ed50c47b 100644
--- a/build-conventions/src/main/kotlin/forlago.dependency-graph-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.dependency-graph-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build-conventions/src/main/kotlin/forlago.detekt-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.detekt-conventions.gradle.kts
index 78d58bdb..1266e671 100644
--- a/build-conventions/src/main/kotlin/forlago.detekt-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.detekt-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build-conventions/src/main/kotlin/forlago.generate-module-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.generate-module-conventions.gradle.kts
index f26c8752..f3f18e88 100644
--- a/build-conventions/src/main/kotlin/forlago.generate-module-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.generate-module-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build-conventions/src/main/kotlin/forlago.kotlin-library-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.kotlin-library-conventions.gradle.kts
index 20021238..a0cffc67 100644
--- a/build-conventions/src/main/kotlin/forlago.kotlin-library-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.kotlin-library-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build-conventions/src/main/kotlin/forlago.merged-manifests-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.merged-manifests-conventions.gradle.kts
index a695538a..cc39be18 100644
--- a/build-conventions/src/main/kotlin/forlago.merged-manifests-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.merged-manifests-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,8 @@ tasks {
dependsOn(tasks.matching { it.name matches "^process.*Manifest$".toRegex() })
mustRunAfter(tasks.matching { it.name matches "^process.*Manifest$".toRegex() })
mkdir("versions/mergedManifests")
- from(layout.buildDirectory.dir("intermediates/merged_manifests")) {
- include("**/*.xml")
+ from(layout.buildDirectory.dir("intermediates/merged_manifests/release/processReleaseManifest/")) {
+ include("AndroidManifest.xml")
}
into("versions/mergedManifests")
filter { line -> line.replace("(android:version.*=\".*\")|(android:testOnly=\".*\")".toRegex(), "") }
diff --git a/build-conventions/src/main/kotlin/forlago.ruler-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.ruler-conventions.gradle.kts
index 0a63c66d..7e23cb86 100644
--- a/build-conventions/src/main/kotlin/forlago.ruler-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.ruler-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build-conventions/src/main/kotlin/forlago.spotless-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.spotless-conventions.gradle.kts
index 7a1fa28c..daa6ca4c 100644
--- a/build-conventions/src/main/kotlin/forlago.spotless-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.spotless-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import com.diffplug.gradle.spotless.SpotlessTask
-import com.diffplug.spotless.LineEnding
import org.gradle.accessors.dm.LibrariesForLibs
plugins {
diff --git a/build-conventions/src/main/kotlin/forlago.versions-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.versions-conventions.gradle.kts
index 6cc0b029..63166622 100644
--- a/build-conventions/src/main/kotlin/forlago.versions-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.versions-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build-conventions/src/main/kotlin/forlago.violation-comments-to-github-conventions.gradle.kts b/build-conventions/src/main/kotlin/forlago.violation-comments-to-github-conventions.gradle.kts
index 2a8b4abb..49e6843b 100644
--- a/build-conventions/src/main/kotlin/forlago.violation-comments-to-github-conventions.gradle.kts
+++ b/build-conventions/src/main/kotlin/forlago.violation-comments-to-github-conventions.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/build.gradle.kts b/build.gradle.kts
index f232410e..ecc5563c 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,10 @@ plugins {
alias(libs.plugins.gradledoctor)
}
+doctor {
+ allowBuildingAllAndroidAppsSimultaneously.set(true)
+}
+
subprojects {
gradle.projectsEvaluated {
tasks.withType {
diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml
index 4cdf00bf..70e1ad9d 100644
--- a/config/detekt/detekt.yml
+++ b/config/detekt/detekt.yml
@@ -418,8 +418,8 @@ complexity:
includePrivateDeclarations: false
ignoreOverloaded: false
CyclomaticComplexMethod:
- active: true
- threshold: 20 # Custom
+ active: false
+ threshold: 30 # Custom
ignoreSingleWhenExpression: true # Custom
ignoreSimpleWhenEntries: false
ignoreNestingFunctions: false
@@ -905,7 +905,7 @@ potential-bugs:
NullCheckOnMutableProperty:
active: true # Custom
NullableToStringCall:
- active: true # Custom
+ active: false
PropertyUsedBeforeDeclaration:
active: true # Custom
UnconditionalJumpStatementInLoop:
@@ -1310,16 +1310,17 @@ Compose:
active: true
PreviewPublic:
active: true
- RememberContentMissing:
- active: true
RememberMissing:
active: true
+ RememberContentMissing:
+ active: true
UnstableCollections:
active: true
ViewModelForwarding:
- active: true
- # You can optionally use this rule on things other than types ending in "ViewModel" or "Presenter" (which are the defaults). You can add your own via a regex here:
- # allowedStateHolderNames: .*ViewModel,.*Presenter
+ active:
+ true
+ # You can optionally use this rule on things other than types ending in "ViewModel" or "Presenter" (which are the defaults). You can add your own via a regex here:
+ # allowedStateHolderNames: .*ViewModel,.*Presenter
ViewModelInjection:
active: true
# You can optionally add your own ViewModel factories here
diff --git a/config/diktat/diktat-analysis.yml b/config/diktat/diktat-analysis.yml
index 924fe26a..5dce8b30 100644
--- a/config/diktat/diktat-analysis.yml
+++ b/config/diktat/diktat-analysis.yml
@@ -290,7 +290,7 @@
enabled: false # Custom
# Checks that backticks (``) are not used in the identifier name, except the case when it is test method (marked with @Test annotation)
- name: BACKTICKS_PROHIBITED
- enabled: true
+ enabled: false # Custom
# all code blocks annotated with @Nested, @ParameterizedTest (JUnit 5) will
# be ignored and not checked.
ignoreAnnotated: [Nested, ParameterizedTest]
@@ -443,7 +443,7 @@
maxLambdaLength: 15 # max length of lambda without parameters # Custom
# Checks that using unnecessary, custom label
- name: CUSTOM_LABEL
- enabled: true
+ enabled: false # Custom
# Checks that outer lambda has explicit parameter name
- name: PARAMETER_NAME_IN_OUTER_LAMBDA
enabled: false
diff --git a/config/lint/lint.xml b/config/lint/lint.xml
index 0fd29ba8..6ba5e8a7 100644
--- a/config/lint/lint.xml
+++ b/config/lint/lint.xml
@@ -1,5 +1,5 @@
-
+
+
+
+
+
+
diff --git a/gradle.properties b/gradle.properties
index 6707c1f8..0934add5 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
#
-# Copyright 2023 Roberto Leinardi.
+# Copyright 2024 Roberto Leinardi.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
# Gradle
org.gradle.caching=true
org.gradle.configuration-cache=true
-org.gradle.jvmargs=-Xms2G -Xmx6G -XX:+UseParallelGC
+org.gradle.jvmargs=-XX:MaxHeapSize=8g -Dfile.encoding=UTF-8 -XX:+UseParallelGC
org.gradle.parallel=true
org.gradle.warning.mode=all
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 4524678f..5c30eefe 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,27 +1,30 @@
# Sharing dependency versions between projects
# https://docs.gradle.org/current/userguide/platforms.html
[versions]
-aboutlibraries = "10.9.1"
+aboutlibraries = "10.10.0"
accompanist = "0.33.1-alpha"
-androidx-compose = "1.6.0-beta01"
-androidx-compose-compiler = "1.5.4"
+androidx-compose = "1.6.2"
+androidx-compose-compiler = "1.5.8"
androidx-datastore = "1.0.0"
-androidx-lifecycle = "2.6.2"
-androidx-navigation = "2.7.5"
-androidx-room = "2.6.0"
-apollo = "4.0.0-beta.2"
+androidx-lifecycle = "2.7.0"
+androidx-navigation = "2.7.7"
+androidx-room = "2.6.1"
+apollo = "4.0.0-beta.4"
+chucker = "4.0.0"
coroutines = "1.7.3"
-detekt = "1.23.1"
+dagger = "2.50"
+detekt = "1.23.4"
diktat = "1.2.5"
-gradle = "8.3"
+gradle = "8.5"
greclipse = "4.19"
-dagger = "2.48.1"
-kotlin = "1.9.20"
+kotlin = "1.9.22"
kotlin-result = "1.1.18"
-kotlinpoet = "1.15.1"
-ksp = "1.9.20-1.0.14"
+kotlinpoet = "1.15.3"
+ksp = "1.9.22-1.0.17"
ktlint = "0.43.0"
-material3 = "1.2.0-alpha11"
+maps-compose = "4.3.0"
+maps-ktx = "5.0.0"
+material3 = "1.2.0"
mockk = "1.13.8"
okhttp = "4.12.0"
play-app-update = "2.1.0"
@@ -29,22 +32,17 @@ prettier = "2.7.1"
retrofit = "2.9.0"
[libraries]
-aboutlibraries = { module = "com.mikepenz:aboutlibraries-compose", version.ref = "aboutlibraries" }
+aboutlibraries = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlibraries" }
aboutlibraries-core = { module = "com.mikepenz:aboutlibraries-core", version.ref = "aboutlibraries" }
accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version.ref = "accompanist" }
-accompanist-insets-ui = { module = "com.google.accompanist:accompanist-insets-ui", version.ref = "accompanist" }
accompanist-navigation-material = { module = "com.google.accompanist:accompanist-navigation-material", version.ref = "accompanist" }
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
-accompanist-placeholder = { module = "com.google.accompanist:accompanist-placeholder-material3", version.ref = "accompanist" }
-accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" }
-android-maps-utils = "com.google.maps.android:android-maps-utils:3.5.3"
-androidx-activity-compose = "androidx.activity:activity-compose:1.8.1"
+android-mail = "com.sun.mail:android-mail:1.6.7"
+androidx-activity-compose = "androidx.activity:activity-compose:1.8.2"
androidx-appcompat = "androidx.appcompat:appcompat:1.6.1"
-androidx-benchmark-macro-junit4 = "androidx.benchmark:benchmark-macro-junit4:1.2.1"
+androidx-benchmark-macro-junit4 = "androidx.benchmark:benchmark-macro-junit4:1.2.3"
androidx-biometric = "androidx.biometric:biometric:1.2.0-alpha05"
androidx-browser = "androidx.browser:browser:1.7.0"
-androidx-compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "androidx-compose" }
-androidx-compose-layout = { module = "androidx.compose.foundation:foundation-layout", version.ref = "androidx-compose" }
androidx-compose-material = { module = "androidx.compose.material:material", version.ref = "androidx-compose" }
androidx-compose-material-android = { module = "androidx.compose.material:material-android", version.ref = "androidx-compose" }
androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "androidx-compose" }
@@ -57,17 +55,19 @@ androidx-compose-tooling = { module = "androidx.compose.ui:ui-tooling", version.
androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "androidx-compose" }
androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx-compose" }
androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-compose" }
-androidx-constraintlayout-compose = "androidx.constraintlayout:constraintlayout-compose:1.0.1"
+androidx-compose-ui-util = { module = "androidx.compose.ui:ui-util", version.ref = "androidx-compose" }
+androidx-constraintlayout-compose = "androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha13"
androidx-core-ktx = "androidx.core:core-ktx:1.12.0"
androidx-core-splashscreen = "androidx.core:core-splashscreen:1.0.1"
androidx-customview = "androidx.customview:customview:1.2.0-alpha02"
androidx-customview-poolingcontainer = "androidx.customview:customview-poolingcontainer:1.0.0"
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "androidx-datastore" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" }
-androidx-hilt-navigation-compose = "androidx.hilt:hilt-navigation-compose:1.1.0"
+androidx-hilt-navigation-compose = "androidx.hilt:hilt-navigation-compose:1.2.0"
androidx-lifecycle-livedata = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidx-lifecycle" }
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" }
+androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
androidx-lifecycle-savedstate = { module = "androidx.lifecycle:lifecycle-viewmodel-savedstate", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
@@ -89,15 +89,17 @@ androidx-test-ext-junit = "androidx.test.ext:junit-ktx:1.1.5"
androidx-test-orchestrator = "androidx.test:orchestrator:1.4.2"
androidx-test-rules = "androidx.test:rules:1.5.0"
androidx-test-runner = "androidx.test:runner:1.5.2"
-androidx-test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-alpha05"
-androidx-tracing = "androidx.tracing:tracing:1.1.0"
+androidx-test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0"
+androidx-tracing = "androidx.tracing:tracing:1.2.0"
apollo = { module = "com.apollographql.apollo3:apollo-runtime", version.ref = "apollo" }
apollo-adapters = { module = "com.apollographql.apollo3:apollo-adapters", version.ref = "apollo" }
apollo-cache = { module = "com.apollographql.apollo3:apollo-normalized-cache", version.ref = "apollo" }
apollo-cache-sqlite = { module = "com.apollographql.apollo3:apollo-normalized-cache-sqlite", version.ref = "apollo" }
apollo-idling-resource = { module = "com.apollographql.apollo3:apollo-idling-resource", version.ref = "apollo" }
apollo-testing-support = { module = "com.apollographql.apollo3:apollo-testing-support", version.ref = "apollo" }
-coil-compose = "io.coil-kt:coil-compose:2.4.0"
+chucker = { module = "com.github.chuckerteam.chucker:library", version.ref = "chucker" }
+chucker-noop = { module = "com.github.chuckerteam.chucker:library-no-op", version.ref = "chucker" }
+coil-compose = "io.coil-kt:coil-compose:2.5.0"
composecharts = "io.github.bytebeats:compose-charts:0.1.2"
coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
@@ -108,33 +110,34 @@ dagger-hilt-android-testing = { module = "com.google.dagger:hilt-android-testing
dagger-hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "dagger" }
desugar = "com.android.tools:desugar_jdk_libs:2.0.4"
detekt = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
+detekt-nlopez-compose-rules = "io.nlopez.compose.rules:detekt:0.3.8"
detekt-rules-compose = "ru.kode:detekt-rules-compose:1.3.0"
-detekt-nlopez-compose-rules = "io.nlopez.compose.rules:detekt:0.3.3"
firebase-analytics = { module = "com.google.firebase:firebase-analytics-ktx" }
-firebase-bom = "com.google.firebase:firebase-bom:32.6.0"
-firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics-ktx" }
-firebase-perf = { module = "com.google.firebase:firebase-perf-ktx" }
-hilt-navigation-compose = "androidx.hilt:hilt-navigation-compose:1.1.0"
+firebase-bom = "com.google.firebase:firebase-bom:32.7.1"
+firebase-config = { module = "com.google.firebase:firebase-config" }
+firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" }
+firebase-perf = { module = "com.google.firebase:firebase-perf" }
junit = "junit:junit:4.13.2"
konfetti = "nl.dionsegijn:konfetti-compose:2.0.3"
-kotlin-extensions = { module = "org.jetbrains.kotlin:kotlin-android-extensions", version.ref = "kotlin" }
kotlin-faker = "io.github.serpro69:kotlin-faker:1.14.0"
+kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
kotlin-result = { module = "com.michael-bull.kotlin-result:kotlin-result", version.ref = "kotlin-result" }
kotlin-result-coroutines = { module = "com.michael-bull.kotlin-result:kotlin-result-coroutines", version.ref = "kotlin-result" }
-kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinpoet" }
kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinpoet" }
-kotlinx-collections-immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5"
-kotlinx-serialization = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.1"
+kotlinx-collections-immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7"
+kotlinx-serialization = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2"
ksp-symbol-processing-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
leakcanary = "com.squareup.leakcanary:leakcanary-android:2.12"
lottie-compose = "com.airbnb.android:lottie-compose:6.1.0"
-maps = "com.google.maps.android:maps-ktx:4.0.0"
-maps-compose = "com.google.maps.android:maps-compose:3.0.0"
-maps-utils-ktx = "com.google.maps.android:maps-utils-ktx:3.4.0"
-material = "com.google.android.material:material:1.10.0"
+maps-compose = { module = "com.google.maps.android:maps-compose", version.ref = "maps-compose" }
+maps-compose-utils = { module = "com.google.maps.android:maps-compose-utils", version.ref = "maps-compose" }
+maps-ktx = { module = "com.google.maps.android:maps-ktx", version.ref = "maps-ktx" }
+maps-utils = "com.google.maps.android:android-maps-utils:3.8.0"
+maps-utils-ktx = { module = "com.google.maps.android:maps-utils-ktx", version.ref = "maps-ktx" }
+material = "com.google.android.material:material:1.11.0"
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" }
okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
@@ -156,10 +159,11 @@ zoomable = "net.engawapg.lib:zoomable:1.5.1"
# plugins
plugin-aboutlibraries = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlibraries" }
-plugin-android-gradle = "com.android.tools.build:gradle:8.2.0-rc03"
+plugin-android-gradle = "com.android.tools.build:gradle:8.3.0-rc02"
plugin-androidcachefix = "gradle.plugin.org.gradle.android:android-cache-fix-gradle-plugin:3.0"
plugin-androidx-navigation-safeargs = { module = "androidx.navigation:navigation-safe-args-gradle-plugin", version.ref = "androidx-navigation" }
-plugin-appversioning = "io.github.reactivecircus.appversioning:app-versioning-gradle-plugin:1.3.1"
+plugin-apollo = { module = "com.apollographql.apollo3:apollo-gradle-plugin", version.ref = "apollo" }
+plugin-apollo-external = { module = "com.apollographql.apollo3:apollo-gradle-plugin-external", version.ref = "apollo" }
plugin-dagger-hilt = { module = "com.google.dagger:hilt-android-gradle-plugin", version.ref = "dagger" }
plugin-detekt = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
plugin-easylauncher = "com.project.starter:easylauncher:6.2.0"
@@ -168,15 +172,23 @@ plugin-firebase-perf = "com.google.firebase:perf-plugin:1.4.2"
plugin-google-services = "com.google.gms:google-services:4.4.0"
plugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
plugin-ksp = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
-plugin-ruler = "com.spotify.ruler:ruler-gradle-plugin:1.3.0"
-plugin-spotless = "com.diffplug.spotless:spotless-plugin-gradle:6.22.0"
+plugin-ruler = "com.spotify.ruler:ruler-gradle-plugin:1.4.0"
+plugin-spotless = "com.diffplug.spotless:spotless-plugin-gradle:6.23.2"
plugin-versions = "com.github.ben-manes:gradle-versions-plugin:0.50.0"
plugin-versions-update = "nl.littlerobots.vcu:plugin:0.8.1"
plugin-violation = "se.bjurr.violations:violation-comments-to-github-gradle-plugin:1.70.0"
+[bundles]
+maps = [
+ "maps-compose",
+ "maps-compose-utils",
+ "maps-ktx",
+ "maps-utils",
+ "maps-utils-ktx",
+]
+
[plugins]
-apollo = { id = "com.apollographql.apollo3", version.ref = "apollo" }
-apollo-external = { id = "com.apollographql.apollo3.external", version.ref = "apollo" }
+appversioning = "io.github.reactivecircus.app-versioning:1.3.1"
google-secrets = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:2.0.1"
gradledoctor = "com.osacky.doctor:0.8.1"
kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index c1962a79..d64cd491 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 9b0a13f0..e6aba251 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index aeb74cbb..1aa94a42 100755
--- a/gradlew
+++ b/gradlew
@@ -83,7 +83,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -130,10 +131,13 @@ location of your Java installation."
fi
else
JAVACMD=java
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
@@ -141,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC3045
+ # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -149,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC3045
+ # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -198,11 +202,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-# Collect all arguments for the java command;
-# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-# shell script including quotes and variable substitutions, so put them in
-# double quotes to make sure that they get re-expanded; and
-# * put everything else in single quotes, so that it's not re-expanded.
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
diff --git a/macrobenchmark/forlago/build.gradle.kts b/macrobenchmark/forlago/build.gradle.kts
index 4e05d6b3..4b34f43a 100644
--- a/macrobenchmark/forlago/build.gradle.kts
+++ b/macrobenchmark/forlago/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,7 +52,9 @@ android {
matchingFallbacks.add("release")
}
}
+ buildFeatures.buildConfig = true
}
+
androidComponents {
beforeVariants(selector().all()) {
// enable only the benchmark buildType, since we only want to measure close to release performance
diff --git a/macrobenchmark/forlago/src/main/AndroidManifest.xml b/macrobenchmark/forlago/src/main/AndroidManifest.xml
index 0593361c..aff0af39 100644
--- a/macrobenchmark/forlago/src/main/AndroidManifest.xml
+++ b/macrobenchmark/forlago/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
+
+
+
+
+
+
diff --git a/modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/bar/Bar.kt b/modules/feature-bar-api/src/main/kotlin/com/leinardi/forlago/feature/bar/api/destination/Bar.kt
similarity index 87%
rename from modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/bar/Bar.kt
rename to modules/feature-bar-api/src/main/kotlin/com/leinardi/forlago/feature/bar/api/destination/Bar.kt
index 5e5ea6d2..026857c1 100644
--- a/modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/bar/Bar.kt
+++ b/modules/feature-bar-api/src/main/kotlin/com/leinardi/forlago/feature/bar/api/destination/Bar.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.leinardi.forlago.library.navigation.api.destination.bar
+package com.leinardi.forlago.feature.bar.api.destination
import com.leinardi.forlago.library.navigation.annotation.NavGraphDestination
diff --git a/modules/feature-bar/build.gradle.kts b/modules/feature-bar/build.gradle.kts
index fed3aa38..f8c9bbcb 100644
--- a/modules/feature-bar/build.gradle.kts
+++ b/modules/feature-bar/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,3 +25,7 @@ android {
consumerProguardFiles("$projectDir/proguard-bar-consumer-rules.pro")
}
}
+
+dependencies {
+ api(projects.modules.featureBarApi)
+}
diff --git a/modules/feature-bar/src/main/AndroidManifest.xml b/modules/feature-bar/src/main/AndroidManifest.xml
index d3b4b53b..e14617db 100644
--- a/modules/feature-bar/src/main/AndroidManifest.xml
+++ b/modules/feature-bar/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
+
+
+
+
+
+
diff --git a/modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/debug/Debug.kt b/modules/feature-debug-api/src/main/kotlin/com/leinardi/forlago/feature/debug/api/destination/Debug.kt
similarity index 77%
rename from modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/debug/Debug.kt
rename to modules/feature-debug-api/src/main/kotlin/com/leinardi/forlago/feature/debug/api/destination/Debug.kt
index 43e00f2a..5acc12d9 100644
--- a/modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/debug/Debug.kt
+++ b/modules/feature-debug-api/src/main/kotlin/com/leinardi/forlago/feature/debug/api/destination/Debug.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.leinardi.forlago.library.navigation.api.destination.debug
+package com.leinardi.forlago.feature.debug.api.destination
import com.leinardi.forlago.library.navigation.annotation.NavGraphDestination
-@NavGraphDestination(deepLink = true) // Disable the deeplink for release builds
+@NavGraphDestination(deepLink = true)
interface Debug
diff --git a/modules/feature-debug-api/src/main/kotlin/com/leinardi/forlago/feature/debug/api/interactor/DebugShakeDetectorInteractor.kt b/modules/feature-debug-api/src/main/kotlin/com/leinardi/forlago/feature/debug/api/interactor/DebugShakeDetectorInteractor.kt
new file mode 100644
index 00000000..b3b6736d
--- /dev/null
+++ b/modules/feature-debug-api/src/main/kotlin/com/leinardi/forlago/feature/debug/api/interactor/DebugShakeDetectorInteractor.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.feature.debug.api.interactor
+
+interface DebugShakeDetectorInteractor {
+ fun startObserving()
+
+ fun stopObserving()
+}
diff --git a/modules/feature-debug-api/src/main/kotlin/com/leinardi/forlago/feature/debug/api/interactor/GetDebugInfoInteractor.kt b/modules/feature-debug-api/src/main/kotlin/com/leinardi/forlago/feature/debug/api/interactor/GetDebugInfoInteractor.kt
new file mode 100644
index 00000000..3b055162
--- /dev/null
+++ b/modules/feature-debug-api/src/main/kotlin/com/leinardi/forlago/feature/debug/api/interactor/GetDebugInfoInteractor.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.feature.debug.api.interactor
+
+interface GetDebugInfoInteractor {
+ operator fun invoke(): DebugInfo
+ data class DebugInfo(
+ val app: App,
+ val device: Device,
+ ) {
+ data class App(
+ val name: String,
+ val versionName: String,
+ val versionCode: Long,
+ val packageName: String,
+ )
+
+ data class Device(
+ val manufacturer: String,
+ val model: String,
+ val resolutionPx: String,
+ val resolutionDp: String,
+ val density: Float,
+ val scaledDensity: Float,
+ val densityDpi: Int,
+ val apiLevel: Int,
+ )
+ }
+}
diff --git a/modules/feature-debug/build.gradle.kts b/modules/feature-debug/build.gradle.kts
index 99dcb3b8..67ca1773 100644
--- a/modules/feature-debug/build.gradle.kts
+++ b/modules/feature-debug/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,12 +24,16 @@ android {
defaultConfig {
consumerProguardFiles("$projectDir/proguard-debug-consumer-rules.pro")
}
+ buildFeatures.buildConfig = true
}
dependencies {
+ api(projects.modules.featureDebugApi)
implementation(projects.modules.featureAccountApi)
+ implementation(projects.modules.featureLogoutApi)
implementation(projects.modules.libraryNetworkApi)
implementation(projects.modules.libraryPreferencesApi)
+ implementation(projects.modules.libraryRemoteConfigApi)
implementation(libs.androidx.lifecycle.process)
implementation(libs.androidx.startup)
diff --git a/modules/feature-debug/src/main/AndroidManifest.xml b/modules/feature-debug/src/main/AndroidManifest.xml
index 3f41d4c3..e14617db 100644
--- a/modules/feature-debug/src/main/AndroidManifest.xml
+++ b/modules/feature-debug/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
-
+
-
-
-
-
-
-
+
diff --git a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/DebugFeature.kt b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/DebugFeature.kt
index 792d645b..b849cc67 100644
--- a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/DebugFeature.kt
+++ b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/DebugFeature.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,10 +17,10 @@
package com.leinardi.forlago.feature.debug
import androidx.compose.runtime.Composable
+import com.leinardi.forlago.feature.debug.api.destination.DebugDestination
import com.leinardi.forlago.feature.debug.ui.DebugScreen
import com.leinardi.forlago.library.feature.Feature
import com.leinardi.forlago.library.navigation.api.destination.NavigationDestination
-import com.leinardi.forlago.library.navigation.api.destination.debug.DebugDestination
class DebugFeature : Feature() {
override val id = "Debug"
diff --git a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/di/DebugInitializerEntryPoint.kt b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/di/DebugInitializerEntryPoint.kt
deleted file mode 100644
index cb39ce09..00000000
--- a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/di/DebugInitializerEntryPoint.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2023 Roberto Leinardi.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.leinardi.forlago.feature.debug.di
-
-import android.content.Context
-import com.leinardi.forlago.feature.debug.initializer.DebugInitializer
-import dagger.hilt.EntryPoint
-import dagger.hilt.InstallIn
-import dagger.hilt.android.EntryPointAccessors
-import dagger.hilt.components.SingletonComponent
-
-@EntryPoint
-@InstallIn(SingletonComponent::class)
-interface DebugInitializerEntryPoint {
- fun inject(initializer: DebugInitializer)
-
- companion object {
- // a helper method to resolve the InitializerEntryPoint from the context
- fun resolve(context: Context): DebugInitializerEntryPoint {
- val appContext = context.applicationContext ?: error("Application context is null")
- return EntryPointAccessors.fromApplication(appContext, DebugInitializerEntryPoint::class.java)
- }
- }
-}
diff --git a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/di/DebugModule.kt b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/di/DebugModule.kt
index f59787ed..12b26c89 100644
--- a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/di/DebugModule.kt
+++ b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/di/DebugModule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,19 +17,39 @@
package com.leinardi.forlago.feature.debug.di
import com.leinardi.forlago.feature.debug.DebugFeature
+import com.leinardi.forlago.feature.debug.api.interactor.DebugShakeDetectorInteractor
+import com.leinardi.forlago.feature.debug.api.interactor.GetDebugInfoInteractor
+import com.leinardi.forlago.feature.debug.interactor.DebugShakeDetectorInteractorImpl
+import com.leinardi.forlago.feature.debug.interactor.GetDebugInfoInteractorImpl
import com.leinardi.forlago.library.feature.Feature
+import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityRetainedComponent
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.IntoSet
import javax.inject.Singleton
-@Module
+@Module(includes = [DebugModule.BindModule::class])
@InstallIn(SingletonComponent::class)
object DebugModule {
@Provides
@Singleton
@IntoSet
fun provideDebugFeature(): Feature = DebugFeature()
+
+ @Module
+ @InstallIn(SingletonComponent::class)
+ internal interface BindModule {
+ @Binds
+ fun bindGetDebugInfoInteractor(bind: GetDebugInfoInteractorImpl): GetDebugInfoInteractor
+ }
+}
+
+@Module
+@InstallIn(ActivityRetainedComponent::class)
+internal interface DebugActivityRetainedModule {
+ @Binds
+ fun bindDebugShakeDetectorInteractor(bind: DebugShakeDetectorInteractorImpl): DebugShakeDetectorInteractor
}
diff --git a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/initializer/DebugInitializer.kt b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/interactor/DebugShakeDetectorInteractorImpl.kt
similarity index 52%
rename from modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/initializer/DebugInitializer.kt
rename to modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/interactor/DebugShakeDetectorInteractorImpl.kt
index c5bcf7b7..0c8353ec 100644
--- a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/initializer/DebugInitializer.kt
+++ b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/interactor/DebugShakeDetectorInteractorImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,45 +14,45 @@
* limitations under the License.
*/
-package com.leinardi.forlago.feature.debug.initializer
+package com.leinardi.forlago.feature.debug.interactor
-import android.content.Context
+import android.app.Application
import android.hardware.SensorManager
import androidx.core.content.ContextCompat
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
-import androidx.startup.Initializer
-import com.leinardi.forlago.feature.debug.BuildConfig
-import com.leinardi.forlago.feature.debug.di.DebugInitializerEntryPoint
-import com.leinardi.forlago.library.navigation.api.destination.debug.DebugDestination
+import androidx.lifecycle.lifecycleScope
+import com.leinardi.forlago.feature.debug.api.destination.DebugDestination
+import com.leinardi.forlago.feature.debug.api.interactor.DebugShakeDetectorInteractor
import com.leinardi.forlago.library.navigation.api.navigator.ForlagoNavigator
import com.squareup.seismic.ShakeDetector
+import dagger.hilt.android.scopes.ActivityRetainedScoped
+import kotlinx.coroutines.launch
import javax.inject.Inject
-/**
- * Initializes a shake detector that will show the Debug screen when the device is shaken with the app on foreground
- */
-class DebugInitializer : Initializer, DefaultLifecycleObserver {
- @Inject lateinit var navigator: ForlagoNavigator
- private var sensorManager: SensorManager? = null
+@ActivityRetainedScoped
+internal class DebugShakeDetectorInteractorImpl @Inject constructor(
+ application: Application,
+ private val forlagoNavigator: ForlagoNavigator,
+) : DebugShakeDetectorInteractor, DefaultLifecycleObserver {
+ private val sensorManager = ContextCompat.getSystemService(application, SensorManager::class.java)
private val shakeDetector by lazy {
ShakeDetector {
- navigator.navigate(DebugDestination.get())
+ ProcessLifecycleOwner.get().lifecycleScope.launch {
+ forlagoNavigator.navigate(DebugDestination.get())
+ }
}
}
- override fun create(context: Context): ShakeDetector {
- DebugInitializerEntryPoint.resolve(context).inject(this)
- if (BuildConfig.DEBUG) {
- sensorManager = ContextCompat.getSystemService(context, SensorManager::class.java)
- ProcessLifecycleOwner.get().lifecycle.addObserver(this)
- }
- return shakeDetector
+ override fun startObserving() {
+ ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}
- override fun dependencies(): List>> = emptyList()
+ override fun stopObserving() {
+ ProcessLifecycleOwner.get().lifecycle.removeObserver(this)
+ }
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
diff --git a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/interactor/GetDebugInfoInteractor.kt b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/interactor/GetDebugInfoInteractorImpl.kt
similarity index 79%
rename from modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/interactor/GetDebugInfoInteractor.kt
rename to modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/interactor/GetDebugInfoInteractorImpl.kt
index c6bd451f..e74e342e 100644
--- a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/interactor/GetDebugInfoInteractor.kt
+++ b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/interactor/GetDebugInfoInteractorImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,13 +20,15 @@ import android.app.Application
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.content.pm.PackageInfoCompat
+import com.leinardi.forlago.feature.debug.api.interactor.GetDebugInfoInteractor
+import com.leinardi.forlago.feature.debug.api.interactor.GetDebugInfoInteractor.DebugInfo
import javax.inject.Inject
-class GetDebugInfoInteractor @Inject constructor(
+internal class GetDebugInfoInteractorImpl @Inject constructor(
private val context: Application,
-) {
+) : GetDebugInfoInteractor {
@Suppress("DEPRECATION")
- operator fun invoke(): DebugInfo {
+ override operator fun invoke(): DebugInfo {
val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager.getPackageInfo(context.packageName, PackageManager.PackageInfoFlags.of(0))
} else {
@@ -67,27 +69,4 @@ class GetDebugInfoInteractor @Inject constructor(
),
)
}
-
- data class DebugInfo(
- val app: App,
- val device: Device,
- ) {
- data class App(
- val name: String,
- val versionName: String,
- val versionCode: Long,
- val packageName: String,
- )
-
- data class Device(
- val manufacturer: String,
- val model: String,
- val resolutionPx: String,
- val resolutionDp: String,
- val density: Float,
- val scaledDensity: Float,
- val densityDpi: Int,
- val apiLevel: Int,
- )
- }
}
diff --git a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugContract.kt b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugContract.kt
index 8efbe669..3eb76e7a 100644
--- a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugContract.kt
+++ b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugContract.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@ package com.leinardi.forlago.feature.debug.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
-import com.leinardi.forlago.feature.debug.interactor.GetDebugInfoInteractor
+import com.leinardi.forlago.feature.debug.api.interactor.GetDebugInfoInteractor
import com.leinardi.forlago.feature.debug.ui.DebugViewModel.DebugNavigationBarItem.Features
import com.leinardi.forlago.feature.debug.ui.DebugViewModel.DebugNavigationBarItem.Info
import com.leinardi.forlago.feature.debug.ui.DebugViewModel.DebugNavigationBarItem.Options
@@ -26,6 +26,7 @@ import com.leinardi.forlago.library.network.api.interactor.ReadEnvironmentIntera
import com.leinardi.forlago.library.ui.base.ViewEffect
import com.leinardi.forlago.library.ui.base.ViewEvent
import com.leinardi.forlago.library.ui.base.ViewState
+import kotlin.enums.EnumEntries
@Immutable
object DebugContract {
@@ -39,9 +40,13 @@ object DebugContract {
Options,
Features,
),
- val environments: Array = ReadEnvironmentInteractor.Environment.values(),
- val selectedNavigationItem: DebugViewModel.DebugNavigationBarItem = bottomNavigationItems[0],
val certificatePinningEnabled: Boolean = true,
+ val environments: EnumEntries = ReadEnvironmentInteractor.Environment.entries,
+ val selectedNavigationItem: DebugViewModel.DebugNavigationBarItem = bottomNavigationItems.first(),
+ val testBoolean: Boolean? = null,
+ val testDouble: Double? = null,
+ val testLong: Long? = null,
+ val testString: String? = null,
) : ViewState {
data class Feature(
val composable: @Composable () -> Unit,
@@ -50,12 +55,12 @@ object DebugContract {
}
sealed class Event : ViewEvent {
+ data class OnEnableCertificatePinningChanged(val boolean: Boolean) : Event()
data class OnNavigationBarItemSelected(val selectedNavigationItem: DebugViewModel.DebugNavigationBarItem) : Event()
data class OnEnvironmentSelected(val environment: ReadEnvironmentInteractor.Environment) : Event()
- data class OnEnableCertificatePinning(val boolean: Boolean) : Event()
- object OnClearApolloCacheClicked : Event()
- object OnForceCrashClicked : Event()
- object OnUpButtonClicked : Event()
+ data object OnClearApolloCacheClicked : Event()
+ data object OnForceCrashClicked : Event()
+ data object OnUpButtonClicked : Event()
}
sealed class Effect : ViewEffect
diff --git a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugScreen.kt b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugScreen.kt
index 9b7c1628..879be4b8 100644
--- a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugScreen.kt
+++ b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugScreen.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,8 +39,8 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.PrimaryScrollableTabRow
import androidx.compose.material3.ProvideTextStyle
+import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Tab
@@ -60,7 +60,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import com.leinardi.forlago.feature.debug.R
-import com.leinardi.forlago.feature.debug.interactor.GetDebugInfoInteractor
+import com.leinardi.forlago.feature.debug.api.interactor.GetDebugInfoInteractor.DebugInfo
import com.leinardi.forlago.feature.debug.ui.DebugContract.Event
import com.leinardi.forlago.feature.debug.ui.DebugContract.State
import com.leinardi.forlago.feature.debug.ui.DebugViewModel.DebugNavigationBarItem.Features
@@ -69,7 +69,7 @@ import com.leinardi.forlago.feature.debug.ui.DebugViewModel.DebugNavigationBarIt
import com.leinardi.forlago.library.network.api.interactor.ReadEnvironmentInteractor.Environment
import com.leinardi.forlago.library.ui.component.LocalMainScaffoldPadding
import com.leinardi.forlago.library.ui.component.LocalSnackbarHostState
-import com.leinardi.forlago.library.ui.component.MainNavigationBarItem
+import com.leinardi.forlago.library.ui.component.NavigationBarItemWithBadge
import com.leinardi.forlago.library.ui.component.PreviewFeature
import com.leinardi.forlago.library.ui.component.Scaffold
import com.leinardi.forlago.library.ui.component.SettingsGroup
@@ -114,14 +114,16 @@ private fun DebugScreen(
},
snackbarHost = { SnackbarHost(snackbarHostState) },
bottomBar = {
- NavigationBar {
- state.bottomNavigationItems.forEachIndexed { index, screen ->
- MainNavigationBarItem(
- icon = screen.icon,
- label = screen.label,
- selected = state.selectedNavigationItem == state.bottomNavigationItems[index],
- onClick = { sendEvent(Event.OnNavigationBarItemSelected(state.bottomNavigationItems[index])) },
- )
+ if (state.bottomNavigationItems.isNotEmpty()) {
+ NavigationBar {
+ state.bottomNavigationItems.forEachIndexed { index, screen ->
+ NavigationBarItemWithBadge(
+ icon = screen.icon,
+ label = screen.label,
+ selected = state.selectedNavigationItem == state.bottomNavigationItems[index],
+ onClick = { sendEvent(Event.OnNavigationBarItemSelected(state.bottomNavigationItems[index])) },
+ )
+ }
}
}
},
@@ -242,9 +244,7 @@ private fun Options(
SettingsMenuSwitch(
title = { Text("Certificate Pinning") },
checked = state.certificatePinningEnabled,
- onCheckedChange = {
- sendEvent(Event.OnEnableCertificatePinning(it))
- },
+ onCheckedChange = { sendEvent(Event.OnEnableCertificatePinningChanged(it)) },
subtitle = { Text("Changing this value will restart the app") },
)
}
@@ -260,6 +260,20 @@ private fun Options(
Text("Clear Apollo cache")
}
}
+ SettingsGroup(
+ title = { Text(text = "Remote config") },
+ ) {
+ Column(Modifier.padding(horizontal = Spacing.x02)) {
+ ProvideTextStyle(value = MaterialTheme.typography.bodyMedium) {
+ CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
+ Text("testBoolean = ${state.testBoolean}")
+ Text("testDouble = ${state.testDouble}")
+ Text("testLong = ${state.testLong}")
+ Text("testString = ${state.testString}")
+ }
+ }
+ }
+ }
SettingsGroup(
title = { Text(text = "Crashlytics") },
) {
@@ -332,10 +346,10 @@ private fun Features(
Column(
modifier = modifier,
) {
- PrimaryScrollableTabRow(
+ ScrollableTabRow(
selectedTabIndex = pagerState.currentPage,
indicator = { tabPositions ->
- TabRowDefaults.PrimaryIndicator(
+ TabRowDefaults.SecondaryIndicator(
Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]),
)
},
@@ -356,14 +370,14 @@ private fun Features(
}
}
-private val previewDebugInfo = GetDebugInfoInteractor.DebugInfo(
- GetDebugInfoInteractor.DebugInfo.App(
+private val previewDebugInfo = DebugInfo(
+ DebugInfo.App(
name = "App name",
versionName = "versionName",
versionCode = 123L,
packageName = "packageName",
),
- GetDebugInfoInteractor.DebugInfo.Device(
+ DebugInfo.Device(
manufacturer = Build.MANUFACTURER,
model = Build.MODEL,
resolutionPx = "resolutionPx",
diff --git a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugViewModel.kt b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugViewModel.kt
index f99321ab..16423342 100644
--- a/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugViewModel.kt
+++ b/modules/feature-debug/src/main/kotlin/com/leinardi/forlago/feature/debug/ui/DebugViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,11 +22,11 @@ import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.Star
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.lifecycle.viewModelScope
-import com.leinardi.forlago.feature.account.api.interactor.account.LogOutInteractor
-import com.leinardi.forlago.feature.debug.interactor.GetDebugInfoInteractor
+import com.leinardi.forlago.feature.debug.api.interactor.GetDebugInfoInteractor
import com.leinardi.forlago.feature.debug.ui.DebugContract.Effect
import com.leinardi.forlago.feature.debug.ui.DebugContract.Event
import com.leinardi.forlago.feature.debug.ui.DebugContract.State
+import com.leinardi.forlago.feature.logout.api.interactor.LogOutInteractor
import com.leinardi.forlago.library.android.api.interactor.android.GetAppUpdateInfoInteractor
import com.leinardi.forlago.library.android.api.interactor.android.GetAppUpdateInfoInteractor.Result
import com.leinardi.forlago.library.android.api.interactor.android.RestartApplicationInteractor
@@ -37,8 +37,11 @@ import com.leinardi.forlago.library.network.api.interactor.ReadCertificatePinnin
import com.leinardi.forlago.library.network.api.interactor.ReadEnvironmentInteractor
import com.leinardi.forlago.library.network.api.interactor.StoreCertificatePinningEnabledInteractor
import com.leinardi.forlago.library.network.api.interactor.StoreEnvironmentInteractor
+import com.leinardi.forlago.library.remoteconfig.api.interactor.GetKillSwitchStreamInteractor
import com.leinardi.forlago.library.ui.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@@ -50,6 +53,7 @@ class DebugViewModel @Inject constructor(
private val forlagoNavigator: ForlagoNavigator,
private val getDebugInfoInteractor: GetDebugInfoInteractor,
private val getFeaturesInteractor: GetFeaturesInteractor,
+ private val getKillSwitchStreamInteractor: GetKillSwitchStreamInteractor,
private val logOutInteractor: LogOutInteractor,
private val readCertificatePinningEnabledInteractor: ReadCertificatePinningEnabledInteractor,
private val readEnvironmentInteractor: ReadEnvironmentInteractor,
@@ -80,6 +84,18 @@ class DebugViewModel @Inject constructor(
},
)
}
+ getKillSwitchStreamInteractor.getBoolean("testBoolean")
+ .onEach { updateState { copy(testBoolean = it.value) } }
+ .launchIn(this)
+ getKillSwitchStreamInteractor.getDouble("testDouble")
+ .onEach { updateState { copy(testDouble = it.value) } }
+ .launchIn(this)
+ getKillSwitchStreamInteractor.getLong("testLong")
+ .onEach { updateState { copy(testLong = it.value) } }
+ .launchIn(this)
+ getKillSwitchStreamInteractor.getString("testString")
+ .onEach { updateState { copy(testString = it.value) } }
+ .launchIn(this)
}
}
@@ -96,19 +112,19 @@ class DebugViewModel @Inject constructor(
override fun handleEvent(event: Event) {
when (event) {
- is Event.OnNavigationBarItemSelected -> updateState { copy(selectedNavigationItem = event.selectedNavigationItem) }
is Event.OnClearApolloCacheClicked -> viewModelScope.launch { clearApolloCacheInteractor() }
+ is Event.OnEnableCertificatePinningChanged -> handleOnEnableCertificatePinning(event.boolean)
is Event.OnEnvironmentSelected -> handleOnEnvironmentSelected(event.environment)
- is Event.OnEnableCertificatePinning -> handleOnEnableCertificatePinning(event.boolean)
is Event.OnForceCrashClicked -> throw DebugScreenTestCrashException()
+ is Event.OnNavigationBarItemSelected -> updateState { copy(selectedNavigationItem = event.selectedNavigationItem) }
is Event.OnUpButtonClicked -> forlagoNavigator.navigateUp()
}
}
- private fun handleOnEnableCertificatePinning(isEnableCertificatePinning: Boolean) {
+ private fun handleOnEnableCertificatePinning(isCertificatePinningEnable: Boolean) {
viewModelScope.launch {
- storeCertificatePinningEnabledInteractor(isEnableCertificatePinning)
- updateState { copy(certificatePinningEnabled = isEnableCertificatePinning) }
+ storeCertificatePinningEnabledInteractor(isCertificatePinningEnable)
+ updateState { copy(certificatePinningEnabled = isCertificatePinningEnable) }
restartApplicationInteractor()
}
}
@@ -125,9 +141,9 @@ class DebugViewModel @Inject constructor(
}
sealed class DebugNavigationBarItem(val label: String, val icon: ImageVector) {
- object Info : DebugNavigationBarItem("Info", Icons.Filled.Info)
- object Options : DebugNavigationBarItem("Options", Icons.Filled.BugReport)
- object Features : DebugNavigationBarItem("Features", Icons.Filled.Star)
+ data object Info : DebugNavigationBarItem("Info", Icons.Filled.Info)
+ data object Options : DebugNavigationBarItem("Options", Icons.Filled.BugReport)
+ data object Features : DebugNavigationBarItem("Features", Icons.Filled.Star)
}
}
diff --git a/modules/feature-debug/src/main/res/values/strings.xml b/modules/feature-debug/src/main/res/values/strings.xml
index 9d8ec5b7..4e0e1bbb 100644
--- a/modules/feature-debug/src/main/res/values/strings.xml
+++ b/modules/feature-debug/src/main/res/values/strings.xml
@@ -1,5 +1,5 @@
+
+
+
+
+
+
diff --git a/modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/foo/Foo.kt b/modules/feature-foo-api/src/main/kotlin/com/leinardi/forlago/feature/foo/api/destination/Foo.kt
similarity index 86%
rename from modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/foo/Foo.kt
rename to modules/feature-foo-api/src/main/kotlin/com/leinardi/forlago/feature/foo/api/destination/Foo.kt
index b75ed669..1e9e4a83 100644
--- a/modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/foo/Foo.kt
+++ b/modules/feature-foo-api/src/main/kotlin/com/leinardi/forlago/feature/foo/api/destination/Foo.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.leinardi.forlago.library.navigation.api.destination.foo
+package com.leinardi.forlago.feature.foo.api.destination
import com.leinardi.forlago.library.navigation.annotation.NavGraphDestination
diff --git a/modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/foo/FooDialog.kt b/modules/feature-foo-api/src/main/kotlin/com/leinardi/forlago/feature/foo/api/destination/FooDialog.kt
similarity index 86%
rename from modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/foo/FooDialog.kt
rename to modules/feature-foo-api/src/main/kotlin/com/leinardi/forlago/feature/foo/api/destination/FooDialog.kt
index b798b314..49dbaf99 100644
--- a/modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/foo/FooDialog.kt
+++ b/modules/feature-foo-api/src/main/kotlin/com/leinardi/forlago/feature/foo/api/destination/FooDialog.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.leinardi.forlago.library.navigation.api.destination.foo
+package com.leinardi.forlago.feature.foo.api.destination
import com.leinardi.forlago.library.navigation.annotation.NavGraphDestination
diff --git a/modules/feature-foo/build.gradle.kts b/modules/feature-foo/build.gradle.kts
index 3da8f8d2..d2d98dc1 100644
--- a/modules/feature-foo/build.gradle.kts
+++ b/modules/feature-foo/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,3 +25,8 @@ android {
consumerProguardFiles("$projectDir/proguard-foo-consumer-rules.pro")
}
}
+
+dependencies {
+ api(projects.modules.featureFooApi)
+ implementation(projects.modules.featureBarApi)
+}
diff --git a/modules/feature-foo/src/main/AndroidManifest.xml b/modules/feature-foo/src/main/AndroidManifest.xml
index d3b4b53b..e14617db 100644
--- a/modules/feature-foo/src/main/AndroidManifest.xml
+++ b/modules/feature-foo/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
+
+
+
+
+
+
diff --git a/modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/account/SignIn.kt b/modules/feature-login-api/src/main/kotlin/com/leinardi/forlago/feature/login/api/destination/LogIn.kt
similarity index 75%
rename from modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/account/SignIn.kt
rename to modules/feature-login-api/src/main/kotlin/com/leinardi/forlago/feature/login/api/destination/LogIn.kt
index 3506f1a6..4c91a3bf 100644
--- a/modules/library-navigation-api/src/main/kotlin/com/leinardi/forlago/library/navigation/api/destination/account/SignIn.kt
+++ b/modules/feature-login-api/src/main/kotlin/com/leinardi/forlago/feature/login/api/destination/LogIn.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-package com.leinardi.forlago.library.navigation.api.destination.account
+package com.leinardi.forlago.feature.login.api.destination
import com.leinardi.forlago.library.navigation.annotation.NavGraphDestination
-@NavGraphDestination(deepLink = true)
-data class SignIn(
- val reauthenticate: Boolean = true,
-)
+@NavGraphDestination
+interface LogIn
diff --git a/modules/feature-login-api/src/main/kotlin/com/leinardi/forlago/feature/login/api/interactor/IsLogInInProgressInteractor.kt b/modules/feature-login-api/src/main/kotlin/com/leinardi/forlago/feature/login/api/interactor/IsLogInInProgressInteractor.kt
new file mode 100644
index 00000000..8de2c85e
--- /dev/null
+++ b/modules/feature-login-api/src/main/kotlin/com/leinardi/forlago/feature/login/api/interactor/IsLogInInProgressInteractor.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.feature.login.api.interactor
+
+interface IsLogInInProgressInteractor {
+ operator fun invoke(): Boolean
+
+ fun setLogInInProgress(inProgress: Boolean)
+}
diff --git a/modules/feature-account-api/src/main/kotlin/com/leinardi/forlago/feature/account/api/interactor/account/SignInInteractor.kt b/modules/feature-login-api/src/main/kotlin/com/leinardi/forlago/feature/login/api/interactor/LogInInteractor.kt
similarity index 83%
rename from modules/feature-account-api/src/main/kotlin/com/leinardi/forlago/feature/account/api/interactor/account/SignInInteractor.kt
rename to modules/feature-login-api/src/main/kotlin/com/leinardi/forlago/feature/login/api/interactor/LogInInteractor.kt
index fd982538..199972a2 100644
--- a/modules/feature-account-api/src/main/kotlin/com/leinardi/forlago/feature/account/api/interactor/account/SignInInteractor.kt
+++ b/modules/feature-login-api/src/main/kotlin/com/leinardi/forlago/feature/login/api/interactor/LogInInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.leinardi.forlago.feature.account.api.interactor.account
+package com.leinardi.forlago.feature.login.api.interactor
import com.github.michaelbull.result.Result
-import com.leinardi.forlago.feature.account.api.model.AuthErrResult
+import com.leinardi.forlago.library.network.api.model.AuthErrResult
import kotlin.random.Random
-interface SignInInteractor {
+interface LogInInteractor {
suspend operator fun invoke(
username: String,
password: String,
diff --git a/modules/feature-login/build.gradle.kts b/modules/feature-login/build.gradle.kts
new file mode 100644
index 00000000..385a5d0e
--- /dev/null
+++ b/modules/feature-login/build.gradle.kts
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id("forlago.android-feature-conventions")
+}
+
+android {
+ namespace = "com.leinardi.forlago.feature.login"
+ resourcePrefix = "login_"
+ defaultConfig {
+ consumerProguardFiles("$projectDir/proguard-login-consumer-rules.pro")
+ }
+ buildFeatures.buildConfig = true
+}
+
+dependencies {
+ api(projects.modules.featureLoginApi)
+ implementation(projects.modules.featureAccountApi)
+ implementation(projects.modules.featureLogoutApi)
+ implementation(projects.modules.libraryNetworkApi)
+ implementation(projects.modules.libraryPreferencesApi)
+
+ implementation(libs.lottie.compose)
+
+ testImplementation(libs.robolectric)
+}
diff --git a/modules/feature-login/proguard-login-consumer-rules.pro b/modules/feature-login/proguard-login-consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/feature-login/src/main/AndroidManifest.xml b/modules/feature-login/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..f7e2fb5b
--- /dev/null
+++ b/modules/feature-login/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/LoginFeature.kt b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/LoginFeature.kt
new file mode 100644
index 00000000..a4f3378e
--- /dev/null
+++ b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/LoginFeature.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.feature.login
+
+import android.accounts.AccountManager
+import android.content.Intent
+import androidx.compose.runtime.Composable
+import com.leinardi.forlago.feature.login.api.destination.LogInDestination
+import com.leinardi.forlago.feature.login.ui.LogInScreen
+import com.leinardi.forlago.library.feature.Feature
+import com.leinardi.forlago.library.navigation.api.destination.NavigationDestination
+import com.leinardi.forlago.library.navigation.api.navigator.ForlagoNavigator
+
+class LoginFeature : Feature() {
+ override val id = "Login"
+
+ override val composableDestinations: Map Unit> = mapOf(
+ LogInDestination to { LogInScreen() },
+ )
+
+ override val handleIntent: suspend (Intent, ForlagoNavigator) -> Boolean = { intent, navigator ->
+ if (intent.isNewAccount()) {
+ handleNewAccountAdded(navigator)
+ } else {
+ false
+ }
+ }
+
+ private fun Intent.isNewAccount(): Boolean = hasExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE)
+
+ private fun handleNewAccountAdded(navigator: ForlagoNavigator): Boolean {
+ navigator.navigateToLogin {
+ launchSingleTop = true
+ popUpTo(0) { inclusive = true }
+ }
+ return true
+ }
+}
diff --git a/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/di/LoginModule.kt b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/di/LoginModule.kt
new file mode 100644
index 00000000..4174f7dc
--- /dev/null
+++ b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/di/LoginModule.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.feature.login.di
+
+import com.leinardi.forlago.feature.login.LoginFeature
+import com.leinardi.forlago.feature.login.api.interactor.IsLogInInProgressInteractor
+import com.leinardi.forlago.feature.login.api.interactor.LogInInteractor
+import com.leinardi.forlago.feature.login.interactor.IsLogInInProgressInteractorImpl
+import com.leinardi.forlago.feature.login.interactor.LogInInteractorImpl
+import com.leinardi.forlago.library.feature.Feature
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import dagger.multibindings.IntoSet
+import javax.inject.Singleton
+
+@Module(includes = [LoginModule.BindModule::class])
+@InstallIn(SingletonComponent::class)
+object LoginModule {
+ @Provides
+ @Singleton
+ @IntoSet
+ fun provideLoginFeature(): Feature = LoginFeature()
+
+ @Module
+ @InstallIn(SingletonComponent::class)
+ internal interface BindModule {
+ @Binds
+ fun bindLogInInteractor(bind: LogInInteractorImpl): LogInInteractor
+
+ @Binds
+ fun bindIsLogInInProgressInteractor(bind: IsLogInInProgressInteractorImpl): IsLogInInProgressInteractor
+ }
+}
diff --git a/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/interactor/IsLogInInProgressInteractorImpl.kt b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/interactor/IsLogInInProgressInteractorImpl.kt
new file mode 100644
index 00000000..20248b3a
--- /dev/null
+++ b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/interactor/IsLogInInProgressInteractorImpl.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.feature.login.interactor
+
+import com.leinardi.forlago.feature.login.api.interactor.IsLogInInProgressInteractor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+internal class IsLogInInProgressInteractorImpl @Inject constructor() : IsLogInInProgressInteractor {
+ private var inProgress = false
+ override fun invoke(): Boolean = inProgress
+
+ override fun setLogInInProgress(inProgress: Boolean) {
+ this.inProgress = inProgress
+ }
+}
diff --git a/modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/interactor/account/SignInInteractorImpl.kt b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/interactor/LogInInteractorImpl.kt
similarity index 80%
rename from modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/interactor/account/SignInInteractorImpl.kt
rename to modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/interactor/LogInInteractorImpl.kt
index 344e3b87..24db0955 100644
--- a/modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/interactor/account/SignInInteractorImpl.kt
+++ b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/interactor/LogInInteractorImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,30 +14,30 @@
* limitations under the License.
*/
-package com.leinardi.forlago.feature.account.interactor.account
+package com.leinardi.forlago.feature.login.interactor
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
-import com.leinardi.forlago.feature.account.api.interactor.account.SignInInteractor
-import com.leinardi.forlago.feature.account.api.interactor.account.SignInInteractor.OkResult
-import com.leinardi.forlago.feature.account.api.model.AuthErrResult
+import com.leinardi.forlago.feature.login.api.interactor.LogInInteractor
+import com.leinardi.forlago.feature.login.api.interactor.LogInInteractor.OkResult
import com.leinardi.forlago.library.feature.interactor.GetFeaturesInteractor
+import com.leinardi.forlago.library.network.api.model.AuthErrResult
import kotlinx.coroutines.delay
import java.net.HttpURLConnection
import java.util.UUID
import java.util.concurrent.TimeUnit
import javax.inject.Inject
-internal class SignInInteractorImpl @Inject constructor(
+internal class LogInInteractorImpl @Inject constructor(
private val getFeaturesInteractor: GetFeaturesInteractor,
-) : SignInInteractor {
+) : LogInInteractor {
@Suppress("TooGenericExceptionCaught", "MagicNumber")
override suspend operator fun invoke(username: String, password: String, success: Boolean): Result {
// This simulates fetching a new refresh token. The access token won't be valid 20% of the time.
delay(TimeUnit.SECONDS.toMillis(2))
return if (success) {
- getFeaturesInteractor().forEach { it.featureLifecycle.onSignIn() }
+ getFeaturesInteractor().forEach { it.featureLifecycle.onLogin() }
Ok(
OkResult(
accessToken = UUID.randomUUID().toString(),
diff --git a/modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/ui/SignInContract.kt b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/ui/LogInContract.kt
similarity index 81%
rename from modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/ui/SignInContract.kt
rename to modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/ui/LogInContract.kt
index 498463fd..9d22781b 100644
--- a/modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/ui/SignInContract.kt
+++ b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/ui/LogInContract.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-package com.leinardi.forlago.feature.account.ui
+package com.leinardi.forlago.feature.login.ui
+import androidx.compose.runtime.Immutable
import com.leinardi.forlago.library.ui.base.ViewEffect
import com.leinardi.forlago.library.ui.base.ViewEvent
import com.leinardi.forlago.library.ui.base.ViewState
-object SignInContract {
+@Immutable
+object LogInContract {
data class State(
val isReauthenticate: Boolean,
val username: String,
@@ -29,7 +31,7 @@ object SignInContract {
) : ViewState
sealed class Event : ViewEvent {
- data class OnSignInButtonClicked(val username: String, val password: String) : Event()
+ data class OnLogInButtonClicked(val username: String, val password: String) : Event()
}
sealed class Effect : ViewEffect {
diff --git a/modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/ui/SignInScreen.kt b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/ui/LogInScreen.kt
similarity index 90%
rename from modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/ui/SignInScreen.kt
rename to modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/ui/LogInScreen.kt
index d091744d..1dea0762 100644
--- a/modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/ui/SignInScreen.kt
+++ b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/ui/LogInScreen.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.leinardi.forlago.feature.account.ui
+package com.leinardi.forlago.feature.login.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -51,9 +51,9 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
-import com.leinardi.forlago.feature.account.ui.SignInContract.Effect
-import com.leinardi.forlago.feature.account.ui.SignInContract.Event
-import com.leinardi.forlago.feature.account.ui.SignInContract.State
+import com.leinardi.forlago.feature.login.ui.LogInContract.Effect
+import com.leinardi.forlago.feature.login.ui.LogInContract.Event
+import com.leinardi.forlago.feature.login.ui.LogInContract.State
import com.leinardi.forlago.library.ui.component.LocalMainScaffoldPadding
import com.leinardi.forlago.library.ui.component.OutlinedTextField
import com.leinardi.forlago.library.ui.component.PreviewFeature
@@ -70,8 +70,8 @@ import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
@Composable
-fun SignInScreen(viewModel: SignInViewModel = hiltViewModel()) {
- SignInScreen(
+fun LogInScreen(viewModel: LogInViewModel = hiltViewModel()) {
+ LogInScreen(
state = viewModel.viewState.value,
sendEvent = { viewModel.onUiEvent(it) },
effectFlow = viewModel.effect,
@@ -79,7 +79,7 @@ fun SignInScreen(viewModel: SignInViewModel = hiltViewModel()) {
}
@Composable
-private fun SignInScreen(
+private fun LogInScreen(
state: State,
effectFlow: Flow,
sendEvent: (event: Event) -> Unit,
@@ -159,19 +159,20 @@ private fun SignInScreen(
keyboardActions = KeyboardActions(
onDone = {
localFocusManager.clearFocus()
- sendEvent(Event.OnSignInButtonClicked(username, password))
+ sendEvent(Event.OnLogInButtonClicked(username, password))
},
),
)
ProgressButton(
onClick = {
localFocusManager.clearFocus()
- sendEvent(Event.OnSignInButtonClicked(username, password))
+ sendEvent(Event.OnLogInButtonClicked(username, password))
},
- loading = state.isLoading,
modifier = Modifier.fillMaxWidth(),
+ loading = state.isLoading,
+ enabled = username.isNotBlank() && password.isNotBlank(),
) {
- Text("Sign in")
+ Text("Log in")
}
}
}
@@ -181,6 +182,6 @@ private fun SignInScreen(
@Composable
private fun PreviewAccountScreen() {
PreviewFeature {
- SignInScreen(State(false, "", ""), Channel().receiveAsFlow()) {}
+ LogInScreen(State(false, "", ""), Channel().receiveAsFlow()) {}
}
}
diff --git a/modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/ui/SignInViewModel.kt b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/ui/LogInViewModel.kt
similarity index 67%
rename from modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/ui/SignInViewModel.kt
rename to modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/ui/LogInViewModel.kt
index 801e55e4..ce09ae62 100644
--- a/modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/ui/SignInViewModel.kt
+++ b/modules/feature-login/src/main/kotlin/com/leinardi/forlago/feature/login/ui/LogInViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,43 +14,39 @@
* limitations under the License.
*/
-package com.leinardi.forlago.feature.account.ui
+package com.leinardi.forlago.feature.login.ui
-import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.leinardi.forlago.feature.account.api.interactor.account.AddAccountInteractor
import com.leinardi.forlago.feature.account.api.interactor.account.GetAccountInteractor
-import com.leinardi.forlago.feature.account.api.interactor.account.SignInInteractor
import com.leinardi.forlago.feature.account.api.interactor.token.GetAccessTokenInteractor
import com.leinardi.forlago.feature.account.api.interactor.token.SetRefreshTokenInteractor
-import com.leinardi.forlago.feature.account.api.model.AuthErrResult
-import com.leinardi.forlago.feature.account.ui.SignInContract.Effect
-import com.leinardi.forlago.feature.account.ui.SignInContract.Event
-import com.leinardi.forlago.feature.account.ui.SignInContract.State
-import com.leinardi.forlago.library.navigation.api.destination.account.SignInDestination
+import com.leinardi.forlago.feature.login.api.interactor.LogInInteractor
+import com.leinardi.forlago.feature.login.ui.LogInContract.Effect
+import com.leinardi.forlago.feature.login.ui.LogInContract.Event
+import com.leinardi.forlago.feature.login.ui.LogInContract.State
import com.leinardi.forlago.library.navigation.api.navigator.ForlagoNavigator
+import com.leinardi.forlago.library.network.api.model.AuthErrResult
import com.leinardi.forlago.library.ui.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
-class SignInViewModel @Inject constructor(
+class LogInViewModel @Inject constructor(
private val addAccountInteractor: AddAccountInteractor,
- private val getAccountInteractor: GetAccountInteractor,
+ private val forlagoNavigator: ForlagoNavigator,
private val getAccessTokenInteractor: GetAccessTokenInteractor,
+ private val getAccountInteractor: GetAccountInteractor,
+ private val logInInteractor: LogInInteractor,
private val setRefreshTokenInteractor: SetRefreshTokenInteractor,
- private val savedStateHandle: SavedStateHandle,
- private val signInInteractor: SignInInteractor,
- private val forlagoNavigator: ForlagoNavigator,
) : BaseViewModel() {
override fun provideInitialState(): State {
val username = getAccountInteractor()?.name
- val reauthenticate: Boolean = SignInDestination.Arguments.getReauthenticate(savedStateHandle)
return State(
- isReauthenticate = reauthenticate && username != null,
+ isReauthenticate = username != null,
username = username.orEmpty(),
password = "",
)
@@ -58,33 +54,33 @@ class SignInViewModel @Inject constructor(
override fun handleEvent(event: Event) {
when (event) {
- is Event.OnSignInButtonClicked -> signIn(event.username, event.password)
+ is Event.OnLogInButtonClicked -> logIn(event.username, event.password)
}
}
- private fun signIn(username: String, password: String) {
+ private fun logIn(username: String, password: String) {
viewModelScope.launch {
updateState { copy(isLoading = true) }
- when (val result = signInInteractor(username, password)) {
- is Ok -> handleSuccessfulSignIn(result.value.refreshToken, username)
+ when (val result = logInInteractor(username, password)) {
+ is Ok -> handleSuccessfulLogIn(result.value.refreshToken, username)
is Err -> when (result.error) {
is AuthErrResult.BadAuthentication -> sendEffect {
Effect.ShowErrorSnackbar(
- "SignInInteractor.Result.Failure.BadAuthentication",
+ "BadAuthentication",
"OK",
)
}
is AuthErrResult.NetworkError -> sendEffect {
Effect.ShowErrorSnackbar(
- "SignInInteractor.Result.Failure.NetworkError",
+ "NetworkError",
"OK",
)
}
is AuthErrResult.UnexpectedError -> sendEffect {
Effect.ShowErrorSnackbar(
- "SignInInteractor.Result.Failure.UnexpectedError",
+ "UnexpectedError",
"OK",
)
}
@@ -94,14 +90,14 @@ class SignInViewModel @Inject constructor(
}
}
- private suspend fun handleSuccessfulSignIn(
+ private suspend fun handleSuccessfulLogIn(
refreshToken: String,
username: String,
) {
if (viewState.value.isReauthenticate) {
if (setRefreshTokenInteractor(refreshToken)) {
getAccessTokenInteractor()
- forlagoNavigator.navigateBack()
+ forlagoNavigator.navigateBackOrHome()
}
} else {
addAccountInteractor(username, refreshToken)
diff --git a/modules/feature-account/src/test/kotlin/com/leinardi/forlago/feature/account/interactor/account/SignInInteractorImplTest.kt b/modules/feature-login/src/test/kotlin/com/leinardi/forlago/feature/login/interactor/LogInInteractorImplTest.kt
similarity index 88%
rename from modules/feature-account/src/test/kotlin/com/leinardi/forlago/feature/account/interactor/account/SignInInteractorImplTest.kt
rename to modules/feature-login/src/test/kotlin/com/leinardi/forlago/feature/login/interactor/LogInInteractorImplTest.kt
index bb707103..a08fa0a3 100644
--- a/modules/feature-account/src/test/kotlin/com/leinardi/forlago/feature/account/interactor/account/SignInInteractorImplTest.kt
+++ b/modules/feature-login/src/test/kotlin/com/leinardi/forlago/feature/login/interactor/LogInInteractorImplTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.leinardi.forlago.feature.account.interactor.account
+package com.leinardi.forlago.feature.login.interactor
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.unwrap
-import com.leinardi.forlago.feature.account.api.model.AuthErrResult
import com.leinardi.forlago.library.feature.interactor.GetFeaturesInteractor
+import com.leinardi.forlago.library.network.api.model.AuthErrResult
import com.leinardi.forlago.library.test.coroutine.MainDispatcherRule
import io.mockk.every
import io.mockk.mockk
@@ -30,10 +30,10 @@ import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
-class SignInInteractorImplTest {
+class LogInInteractorImplTest {
@get:Rule val mainDispatcherRule = MainDispatcherRule()
private val getFeaturesInteractor: GetFeaturesInteractor = mockk()
- private val signInInteractor = SignInInteractorImpl(getFeaturesInteractor)
+ private val signInInteractor = LogInInteractorImpl(getFeaturesInteractor)
@Test
fun `GIVEN remote success WHEN call logInInteractor with password THEN return Success `(): TestResult = runTest {
diff --git a/modules/feature-logout-api/build.gradle.kts b/modules/feature-logout-api/build.gradle.kts
new file mode 100644
index 00000000..25de4507
--- /dev/null
+++ b/modules/feature-logout-api/build.gradle.kts
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id("forlago.android-library-conventions")
+}
+
+android {
+ namespace = "com.leinardi.forlago.feature.logout.api"
+ resourcePrefix = "logout_api_"
+ defaultConfig {
+ consumerProguardFiles("$projectDir/proguard-logout-api-consumer-rules.pro")
+ }
+}
+
+dependencies {
+ implementation(projects.modules.libraryNavigationApi)
+ ksp(projects.modules.libraryNavigationKsp)
+}
+
+// Workaround for https://github.com/detekt/detekt/issues/4743
+tasks.withType().configureEach {
+ exclude("com/leinardi/forlago/feature/logout/api/destination/*Destination.kt")
+}
diff --git a/modules/feature-logout-api/proguard-logout-api-consumer-rules.pro b/modules/feature-logout-api/proguard-logout-api-consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/feature-logout-api/src/main/AndroidManifest.xml b/modules/feature-logout-api/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..02bcfb64
--- /dev/null
+++ b/modules/feature-logout-api/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/modules/feature-logout-api/src/main/kotlin/com/leinardi/forlago/feature/logout/api/destination/LogOutDialog.kt b/modules/feature-logout-api/src/main/kotlin/com/leinardi/forlago/feature/logout/api/destination/LogOutDialog.kt
new file mode 100644
index 00000000..1d0931f1
--- /dev/null
+++ b/modules/feature-logout-api/src/main/kotlin/com/leinardi/forlago/feature/logout/api/destination/LogOutDialog.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.feature.logout.api.destination
+
+import com.leinardi.forlago.library.navigation.annotation.NavGraphDestination
+
+@NavGraphDestination(name = "logout")
+interface LogOutDialog
diff --git a/modules/feature-account-api/src/main/kotlin/com/leinardi/forlago/feature/account/api/interactor/account/LogOutInteractor.kt b/modules/feature-logout-api/src/main/kotlin/com/leinardi/forlago/feature/logout/api/interactor/LogOutInteractor.kt
similarity index 82%
rename from modules/feature-account-api/src/main/kotlin/com/leinardi/forlago/feature/account/api/interactor/account/LogOutInteractor.kt
rename to modules/feature-logout-api/src/main/kotlin/com/leinardi/forlago/feature/logout/api/interactor/LogOutInteractor.kt
index 55bc123e..0fc7d5ed 100644
--- a/modules/feature-account-api/src/main/kotlin/com/leinardi/forlago/feature/account/api/interactor/account/LogOutInteractor.kt
+++ b/modules/feature-logout-api/src/main/kotlin/com/leinardi/forlago/feature/logout/api/interactor/LogOutInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.leinardi.forlago.feature.account.api.interactor.account
+package com.leinardi.forlago.feature.logout.api.interactor
interface LogOutInteractor {
suspend operator fun invoke(navigateToLogin: Boolean = true)
-
- fun isSignOutInProgress(): Boolean
+ fun isLogOutInProgress(): Boolean
}
diff --git a/modules/feature-logout/build.gradle.kts b/modules/feature-logout/build.gradle.kts
new file mode 100644
index 00000000..0151cbb5
--- /dev/null
+++ b/modules/feature-logout/build.gradle.kts
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id("forlago.android-feature-conventions")
+}
+
+android {
+ namespace = "com.leinardi.forlago.feature.logout"
+ resourcePrefix = "logout_"
+ defaultConfig {
+ consumerProguardFiles("$projectDir/proguard-logout-consumer-rules.pro")
+ }
+}
+
+dependencies {
+ api(projects.modules.featureLogoutApi)
+ implementation(projects.modules.featureAccountApi)
+ implementation(projects.modules.featureLoginApi)
+ implementation(projects.modules.libraryNetworkApi)
+ testImplementation(libs.robolectric)
+}
diff --git a/modules/feature-logout/proguard-logout-consumer-rules.pro b/modules/feature-logout/proguard-logout-consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/feature-logout/src/main/AndroidManifest.xml b/modules/feature-logout/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..f7e2fb5b
--- /dev/null
+++ b/modules/feature-logout/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/modules/feature-logout/src/main/kotlin/com/leinardi/forlago/feature/logout/LogoutFeature.kt b/modules/feature-logout/src/main/kotlin/com/leinardi/forlago/feature/logout/LogoutFeature.kt
new file mode 100644
index 00000000..574ee277
--- /dev/null
+++ b/modules/feature-logout/src/main/kotlin/com/leinardi/forlago/feature/logout/LogoutFeature.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.feature.logout
+
+import com.leinardi.forlago.library.feature.Feature
+
+class LogoutFeature : Feature() {
+ override val id = "Logout"
+}
diff --git a/modules/feature-logout/src/main/kotlin/com/leinardi/forlago/feature/logout/di/LogoutModule.kt b/modules/feature-logout/src/main/kotlin/com/leinardi/forlago/feature/logout/di/LogoutModule.kt
new file mode 100644
index 00000000..49950830
--- /dev/null
+++ b/modules/feature-logout/src/main/kotlin/com/leinardi/forlago/feature/logout/di/LogoutModule.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.feature.logout.di
+
+import com.leinardi.forlago.feature.logout.LogoutFeature
+import com.leinardi.forlago.feature.logout.api.interactor.LogOutInteractor
+import com.leinardi.forlago.feature.logout.interactor.LogOutInteractorImpl
+import com.leinardi.forlago.library.feature.Feature
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityRetainedComponent
+import dagger.hilt.components.SingletonComponent
+import dagger.multibindings.IntoSet
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object LogoutModule {
+ @Provides
+ @Singleton
+ @IntoSet
+ fun provideLogoutFeature(): Feature = LogoutFeature()
+}
+
+@Module
+@InstallIn(ActivityRetainedComponent::class)
+internal interface LogoutActivityRetainedModule {
+ @Binds
+ fun bindLogOutInteractor(bind: LogOutInteractorImpl): LogOutInteractor
+}
diff --git a/modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/interactor/account/LogOutInteractorImpl.kt b/modules/feature-logout/src/main/kotlin/com/leinardi/forlago/feature/logout/interactor/LogOutInteractorImpl.kt
similarity index 53%
rename from modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/interactor/account/LogOutInteractorImpl.kt
rename to modules/feature-logout/src/main/kotlin/com/leinardi/forlago/feature/logout/interactor/LogOutInteractorImpl.kt
index 28831636..c7fdb2b7 100644
--- a/modules/feature-account/src/main/kotlin/com/leinardi/forlago/feature/account/interactor/account/LogOutInteractorImpl.kt
+++ b/modules/feature-logout/src/main/kotlin/com/leinardi/forlago/feature/logout/interactor/LogOutInteractorImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,40 +14,43 @@
* limitations under the License.
*/
-package com.leinardi.forlago.feature.account.interactor.account
+package com.leinardi.forlago.feature.logout.interactor
-import com.leinardi.forlago.feature.account.api.interactor.account.LogOutInteractor
+import com.leinardi.forlago.feature.login.api.interactor.IsLogInInProgressInteractor
+import com.leinardi.forlago.feature.logout.api.interactor.LogOutInteractor
import com.leinardi.forlago.library.feature.interactor.GetFeaturesInteractor
-import com.leinardi.forlago.library.navigation.api.destination.account.SignInDestination
import com.leinardi.forlago.library.navigation.api.navigator.ForlagoNavigator
import com.leinardi.forlago.library.network.api.interactor.ClearApolloCacheInteractor
-import com.leinardi.forlago.library.preferences.api.di.User
-import com.leinardi.forlago.library.preferences.api.repository.DataStoreRepository
+import dagger.hilt.android.scopes.ActivityRetainedScoped
import timber.log.Timber
import javax.inject.Inject
+@ActivityRetainedScoped
internal class LogOutInteractorImpl @Inject constructor(
private val clearApolloCacheInteractor: ClearApolloCacheInteractor,
private val forlagoNavigator: ForlagoNavigator,
private val getFeaturesInteractor: GetFeaturesInteractor,
- @User private val userDataStoreRepository: DataStoreRepository,
+ private val isLogInInProgressInteractor: IsLogInInProgressInteractor,
) : LogOutInteractor {
- private var signOutInProgress: Boolean = false
+ private var logOutInProgress: Boolean = false
override suspend operator fun invoke(navigateToLogin: Boolean) {
- Timber.d("LogOut")
- signOutInProgress = true
- clearApolloCacheInteractor()
- getFeaturesInteractor().forEach { it.featureLifecycle.onSignOut }
- userDataStoreRepository.clearPreferencesStorage()
- if (navigateToLogin) {
- forlagoNavigator.navigate(SignInDestination.get()) {
- launchSingleTop = true
- popUpTo(0) { inclusive = true }
+ if (!isLogInInProgressInteractor()) {
+ Timber.d("LogOut")
+ logOutInProgress = true
+ clearApolloCacheInteractor()
+ getFeaturesInteractor().forEach { it.featureLifecycle.onLogout() }
+ if (navigateToLogin) {
+ forlagoNavigator.navigateToLogin {
+ launchSingleTop = true
+ popUpTo(0) { inclusive = true }
+ }
}
+ logOutInProgress = false
+ } else {
+ Timber.w("LogIn in progress, ignoring LogOut request!")
}
- signOutInProgress = false
}
- override fun isSignOutInProgress() = signOutInProgress
+ override fun isLogOutInProgress() = logOutInProgress
}
diff --git a/modules/feature-account/src/test/kotlin/com/leinardi/forlago/feature/account/interactor/account/LogOutInteractorImplTest.kt b/modules/feature-logout/src/test/kotlin/com/leinardi/forlago/feature/logout/interactor/LogOutInteractorImplTest.kt
similarity index 83%
rename from modules/feature-account/src/test/kotlin/com/leinardi/forlago/feature/account/interactor/account/LogOutInteractorImplTest.kt
rename to modules/feature-logout/src/test/kotlin/com/leinardi/forlago/feature/logout/interactor/LogOutInteractorImplTest.kt
index aa13f830..8b941a15 100644
--- a/modules/feature-account/src/test/kotlin/com/leinardi/forlago/feature/account/interactor/account/LogOutInteractorImplTest.kt
+++ b/modules/feature-logout/src/test/kotlin/com/leinardi/forlago/feature/logout/interactor/LogOutInteractorImplTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,20 +14,18 @@
* limitations under the License.
*/
-package com.leinardi.forlago.feature.account.interactor.account
+package com.leinardi.forlago.feature.logout.interactor
import android.os.Build
-import com.leinardi.forlago.feature.account.api.interactor.account.LogOutInteractor
+import com.leinardi.forlago.feature.login.api.interactor.IsLogInInProgressInteractor
+import com.leinardi.forlago.feature.logout.api.interactor.LogOutInteractor
import com.leinardi.forlago.library.feature.interactor.GetFeaturesInteractor
import com.leinardi.forlago.library.navigation.api.navigator.ForlagoNavigator
import com.leinardi.forlago.library.network.api.interactor.ClearApolloCacheInteractor
-import com.leinardi.forlago.library.preferences.api.repository.DataStoreRepository
-import io.mockk.Runs
import io.mockk.clearAllMocks
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
-import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.test.TestResult
@@ -45,7 +43,7 @@ class LogOutInteractorImplTest {
private val clearApolloCacheInteractor: ClearApolloCacheInteractor = mockk()
private val forlagoNavigator: ForlagoNavigator = mockk()
private val getFeaturesInteractor: GetFeaturesInteractor = mockk()
- private val userDataStoreRepository: DataStoreRepository = mockk()
+ private val isLogInInProgressInteractor: IsLogInInProgressInteractor = mockk()
private lateinit var logOutInteractor: LogOutInteractor
@Before
@@ -54,9 +52,9 @@ class LogOutInteractorImplTest {
clearApolloCacheInteractor,
forlagoNavigator,
getFeaturesInteractor,
- userDataStoreRepository,
+ isLogInInProgressInteractor,
)
- coEvery { userDataStoreRepository.clearPreferencesStorage() } just Runs
+ every { isLogInInProgressInteractor.invoke() } returns false
}
@After
diff --git a/modules/library-android-api/build.gradle.kts b/modules/library-android-api/build.gradle.kts
index 2e5f941b..5a40f0b8 100644
--- a/modules/library-android-api/build.gradle.kts
+++ b/modules/library-android-api/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ android {
defaultConfig {
consumerProguardFiles("$projectDir/proguard-android-api-consumer-rules.pro")
}
+ buildFeatures.buildConfig = true
}
dependencies {
diff --git a/modules/library-android-api/src/main/AndroidManifest.xml b/modules/library-android-api/src/main/AndroidManifest.xml
index d3b4b53b..e14617db 100644
--- a/modules/library-android-api/src/main/AndroidManifest.xml
+++ b/modules/library-android-api/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
-
+
-
+
+
+
+
+
+
+
+
diff --git a/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/CrashlyticsTree.kt b/modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/CrashlyticsTree.kt
similarity index 93%
rename from modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/CrashlyticsTree.kt
rename to modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/CrashlyticsTree.kt
index 03a16b0b..0ff9daa7 100644
--- a/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/CrashlyticsTree.kt
+++ b/modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/CrashlyticsTree.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.leinardi.forlago.library.logging
+package com.leinardi.forlago.library.logging.api
import android.annotation.SuppressLint
import android.util.Log
import com.google.firebase.crashlytics.FirebaseCrashlytics
import timber.log.Timber
-class CrashlyticsTree : Timber.Tree() {
+internal class CrashlyticsTree : Timber.Tree() {
@SuppressLint("LogNotTimber")
@Suppress("IDENTIFIER_LENGTH") // The identifier name is coming from Timber
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
diff --git a/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/DebugTree.kt b/modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/DebugTree.kt
similarity index 84%
rename from modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/DebugTree.kt
rename to modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/DebugTree.kt
index f7307290..5d772b8a 100644
--- a/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/DebugTree.kt
+++ b/modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/DebugTree.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.leinardi.forlago.library.logging
+package com.leinardi.forlago.library.logging.api
import timber.log.Timber
-class DebugTree : Timber.DebugTree() {
+internal class DebugTree : Timber.DebugTree() {
override fun createStackElementTag(element: StackTraceElement) =
"(${element.fileName.orEmpty()}:${element.lineNumber})"
}
diff --git a/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/initializer/TimberInitializer.kt b/modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/initializer/TimberInitializer.kt
similarity index 77%
rename from modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/initializer/TimberInitializer.kt
rename to modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/initializer/TimberInitializer.kt
index 5abd038f..947443ef 100644
--- a/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/initializer/TimberInitializer.kt
+++ b/modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/initializer/TimberInitializer.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.leinardi.forlago.library.logging.initializer
+package com.leinardi.forlago.library.logging.api.initializer
import android.content.Context
import androidx.startup.Initializer
-import com.leinardi.forlago.library.logging.BuildConfig
-import com.leinardi.forlago.library.logging.CrashlyticsTree
-import com.leinardi.forlago.library.logging.DebugTree
+import com.leinardi.forlago.library.logging.api.BuildConfig
+import com.leinardi.forlago.library.logging.api.CrashlyticsTree
+import com.leinardi.forlago.library.logging.api.DebugTree
import timber.log.Timber
class TimberInitializer : Initializer {
diff --git a/modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/interactor/LogScreenViewInteractor.kt b/modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/interactor/LogScreenViewInteractor.kt
index f769c7b5..2c2d6669 100644
--- a/modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/interactor/LogScreenViewInteractor.kt
+++ b/modules/library-logging-api/src/main/kotlin/com/leinardi/forlago/library/logging/api/interactor/LogScreenViewInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/modules/library-logging/build.gradle.kts b/modules/library-logging/build.gradle.kts
index 362a2a32..a10a9758 100644
--- a/modules/library-logging/build.gradle.kts
+++ b/modules/library-logging/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,18 +25,14 @@ android {
defaultConfig {
consumerProguardFiles("$projectDir/proguard-logging-consumer-rules.pro")
}
+ buildFeatures.buildConfig = true
}
dependencies {
api(projects.modules.libraryLoggingApi)
- api(libs.timber)
- implementation(libs.androidx.startup)
implementation(libs.firebase.analytics)
- implementation(libs.firebase.crashlytics)
- if (rootProject.file("apps/forlago/google-services.json").exists()) {
- implementation(libs.firebase.perf)
- }
implementation(libs.dagger.hilt.android)
implementation(platform(libs.firebase.bom))
+
ksp(libs.dagger.hilt.compiler)
}
diff --git a/modules/library-logging/src/main/AndroidManifest.xml b/modules/library-logging/src/main/AndroidManifest.xml
index 989e3d8d..e7e27789 100644
--- a/modules/library-logging/src/main/AndroidManifest.xml
+++ b/modules/library-logging/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
-
+
-
-
-
-
-
-
-
diff --git a/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/di/LoggingModule.kt b/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/di/LoggingModule.kt
index 176cf5bc..af8a3952 100644
--- a/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/di/LoggingModule.kt
+++ b/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/di/LoggingModule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/interactor/LogScreenViewInteractorImpl.kt b/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/interactor/LogScreenViewInteractorImpl.kt
index e159857a..facafa8c 100644
--- a/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/interactor/LogScreenViewInteractorImpl.kt
+++ b/modules/library-logging/src/main/kotlin/com/leinardi/forlago/library/logging/interactor/LogScreenViewInteractorImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,10 +27,10 @@ class LogScreenViewInteractorImpl @Inject constructor(
override operator fun invoke(screenClass: String, screenName: String) {
if (!BuildConfig.DEBUG) {
Timber.e("Default FirebaseApp must be initialized for the log to work")
-// firebaseAnalytics.logEvent(FirebaseAnalytics.Event.SCREEN_VIEW) {
-// param(FirebaseAnalytics.Param.SCREEN_CLASS, screenClass)
-// param(FirebaseAnalytics.Param.SCREEN_NAME, screenName)
-// }
+ // firebaseAnalytics.logEvent(FirebaseAnalytics.Event.SCREEN_VIEW) {
+ // param(FirebaseAnalytics.Param.SCREEN_CLASS, screenClass)
+ // param(FirebaseAnalytics.Param.SCREEN_NAME, screenName)
+ // }
}
}
}
diff --git a/modules/library-navigation-annotation/build.gradle.kts b/modules/library-navigation-annotation/build.gradle.kts
index b98a7166..2744e6ed 100644
--- a/modules/library-navigation-annotation/build.gradle.kts
+++ b/modules/library-navigation-annotation/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/modules/library-navigation-annotation/src/main/kotlin/com/leinardi/forlago/library/navigation/annotation/NavGraphDestination.kt b/modules/library-navigation-annotation/src/main/kotlin/com/leinardi/forlago/library/navigation/annotation/NavGraphDestination.kt
index 7e3008e3..9639c196 100644
--- a/modules/library-navigation-annotation/src/main/kotlin/com/leinardi/forlago/library/navigation/annotation/NavGraphDestination.kt
+++ b/modules/library-navigation-annotation/src/main/kotlin/com/leinardi/forlago/library/navigation/annotation/NavGraphDestination.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/modules/library-navigation-api/build.gradle.kts b/modules/library-navigation-api/build.gradle.kts
index 7bb33445..6404c14e 100644
--- a/modules/library-navigation-api/build.gradle.kts
+++ b/modules/library-navigation-api/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Roberto Leinardi.
+ * Copyright 2024 Roberto Leinardi.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ import io.gitlab.arturbosch.detekt.Detekt
plugins {
id("forlago.android-library-conventions")
+ id("com.google.dagger.hilt.android")
}
android {
@@ -26,20 +27,17 @@ android {
defaultConfig {
consumerProguardFiles("$projectDir/proguard-navigation-api-consumer-rules.pro")
}
+ buildFeatures.buildConfig = true
}
dependencies {
- implementation(projects.modules.libraryNavigationAnnotation)
- ksp(projects.modules.libraryNavigationKsp)
-
+ api(projects.modules.libraryNavigationAnnotation)
api(libs.androidx.navigation.compose)
+ implementation(libs.dagger.hilt.android)
+ ksp(libs.dagger.hilt.compiler)
}
// Workaround for https://github.com/detekt/detekt/issues/4743
tasks.withType().configureEach {
exclude("com/leinardi/forlago/library/navigation/api/destination/**/*Destination.kt")
}
-
-afterEvaluate {
- tasks.named("compileDebugKotlin").configure { shouldRunAfter(tasks.named("kspDebugKotlin")) }
-}
diff --git a/modules/library-navigation-api/src/main/AndroidManifest.xml b/modules/library-navigation-api/src/main/AndroidManifest.xml
index d3b4b53b..e14617db 100644
--- a/modules/library-navigation-api/src/main/AndroidManifest.xml
+++ b/modules/library-navigation-api/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
+
+
+
+
+
+
diff --git a/modules/library-remote-config-api/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/api/interactor/GetFeatureFlagInteractor.kt b/modules/library-remote-config-api/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/api/interactor/GetFeatureFlagInteractor.kt
new file mode 100644
index 00000000..0041f759
--- /dev/null
+++ b/modules/library-remote-config-api/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/api/interactor/GetFeatureFlagInteractor.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.library.remoteconfig.api.interactor
+
+import com.leinardi.forlago.library.remoteconfig.api.model.RemoteConfigValue
+
+interface GetFeatureFlagInteractor {
+ fun getBoolean(key: String): RemoteConfigValue.Boolean
+ fun getByteArray(key: String): RemoteConfigValue.ByteArray
+ fun getDouble(key: String): RemoteConfigValue.Double
+ fun getLong(key: String): RemoteConfigValue.Long
+ fun getString(key: String): RemoteConfigValue.String
+}
diff --git a/modules/library-remote-config-api/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/api/interactor/GetKillSwitchStreamInteractor.kt b/modules/library-remote-config-api/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/api/interactor/GetKillSwitchStreamInteractor.kt
new file mode 100644
index 00000000..07da4585
--- /dev/null
+++ b/modules/library-remote-config-api/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/api/interactor/GetKillSwitchStreamInteractor.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.library.remoteconfig.api.interactor
+
+import com.leinardi.forlago.library.remoteconfig.api.model.RemoteConfigValue
+import kotlinx.coroutines.flow.Flow
+
+interface GetKillSwitchStreamInteractor {
+ fun getBoolean(key: String): Flow
+ fun getByteArray(key: String): Flow
+ fun getDouble(key: String): Flow
+ fun getLong(key: String): Flow
+ fun getString(key: String): Flow
+}
diff --git a/modules/library-remote-config-api/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/api/model/RemoteConfigValue.kt b/modules/library-remote-config-api/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/api/model/RemoteConfigValue.kt
new file mode 100644
index 00000000..2d3fab35
--- /dev/null
+++ b/modules/library-remote-config-api/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/api/model/RemoteConfigValue.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.library.remoteconfig.api.model
+
+sealed class RemoteConfigValue {
+ abstract val valueSource: ValueSource
+ abstract val lastFetchStatus: LastFetchStatus?
+
+ data class Long(
+ val value: kotlin.Long,
+ override val valueSource: ValueSource,
+ override val lastFetchStatus: LastFetchStatus?,
+ ) : RemoteConfigValue()
+
+ data class Double(
+ val value: kotlin.Double,
+ override val valueSource: ValueSource,
+ override val lastFetchStatus: LastFetchStatus?,
+ ) : RemoteConfigValue()
+
+ data class String(
+ val value: kotlin.String,
+ override val valueSource: ValueSource,
+ override val lastFetchStatus: LastFetchStatus?,
+ ) : RemoteConfigValue()
+
+ data class ByteArray(
+ val value: kotlin.ByteArray,
+ override val valueSource: ValueSource,
+ override val lastFetchStatus: LastFetchStatus?,
+ ) : RemoteConfigValue() {
+ override fun equals(other: Any?): kotlin.Boolean {
+ if (this === other) {
+ return true
+ }
+ if (javaClass != other?.javaClass) {
+ return false
+ }
+
+ other as ByteArray
+
+ if (!value.contentEquals(other.value)) {
+ return false
+ }
+ if (valueSource != other.valueSource) {
+ return false
+ }
+ return lastFetchStatus == other.lastFetchStatus
+ }
+
+ @Suppress("MagicNumber")
+ override fun hashCode(): Int {
+ var result = value.contentHashCode()
+ result = 31 * result + valueSource.hashCode()
+ result = 31 * result + lastFetchStatus.hashCode()
+ return result
+ }
+ }
+
+ data class Boolean(
+ val value: kotlin.Boolean,
+ override val valueSource: ValueSource,
+ override val lastFetchStatus: LastFetchStatus?,
+ ) : RemoteConfigValue()
+
+ enum class ValueSource {
+ DEFAULT,
+ REMOTE,
+ STATIC,
+ }
+
+ enum class LastFetchStatus {
+ FAILURE,
+ NO_FETCH_YET,
+ SUCCESS,
+ THROTTLED
+ }
+}
diff --git a/modules/library-remote-config/build.gradle.kts b/modules/library-remote-config/build.gradle.kts
new file mode 100644
index 00000000..fe7c047f
--- /dev/null
+++ b/modules/library-remote-config/build.gradle.kts
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id("forlago.android-library-conventions")
+ id("com.google.dagger.hilt.android")
+ alias(libs.plugins.kotlinx.serialization)
+}
+
+android {
+ namespace = "com.leinardi.forlago.library.remoteconfig"
+ resourcePrefix = "remote_config_"
+ defaultConfig {
+ consumerProguardFiles("$projectDir/proguard-remoteconfig-consumer-rules.pro")
+ }
+ buildFeatures.buildConfig = true
+}
+dependencies {
+ api(projects.modules.libraryRemoteConfigApi)
+ implementation(projects.modules.libraryLoggingApi)
+
+ implementation(libs.androidx.lifecycle.process)
+ implementation(libs.androidx.startup)
+ implementation(libs.dagger.hilt.android)
+ implementation(libs.firebase.config)
+ implementation(libs.kotlinx.serialization)
+ implementation(libs.timber)
+ implementation(platform(libs.firebase.bom))
+ ksp(libs.dagger.hilt.compiler)
+
+ testImplementation(libs.robolectric)
+}
diff --git a/modules/library-remote-config/proguard-remoteconfig-consumer-rules.pro b/modules/library-remote-config/proguard-remoteconfig-consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/library-remote-config/src/main/AndroidManifest.xml b/modules/library-remote-config/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..3649bf1d
--- /dev/null
+++ b/modules/library-remote-config/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/library-remote-config/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/RemoteConfigDefaults.kt b/modules/library-remote-config/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/RemoteConfigDefaults.kt
new file mode 100644
index 00000000..74311761
--- /dev/null
+++ b/modules/library-remote-config/src/main/kotlin/com/leinardi/forlago/library/remoteconfig/RemoteConfigDefaults.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 Roberto Leinardi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.leinardi.forlago.library.remoteconfig
+
+import android.content.Context
+import kotlinx.serialization.json.Json
+
+fun getRemoteConfigDefaults(context: Context): Map {
+ val rawResourceId = R.raw.remote_config_defaults
+ val inputStream = context.resources.openRawResource(rawResourceId)
+ val jsonText = inputStream.bufferedReader().use { it.readText() }
+ return Json.decodeFromString