From 811c821c65a7659db741bc8ce2944987aa40a964 Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Sun, 10 Dec 2023 01:52:55 +0100 Subject: [PATCH 01/24] Add configuration as a way to interact with CP. WIP --- build.gradle.kts | 2 +- gradle/libs.versions.toml | 4 +- .../database/WrenchConfigurationDao.kt | 13 ++ .../database/WrenchConfigurationValueDao.kt | 1 - .../se/eelde/toggles/database/WrenchScope.kt | 3 +- .../toggles/database/tables/ScopeTable.kt | 10 +- .../eelde/toggles/provider/TogglesProvider.kt | 45 +++-- .../toggles/provider/TogglesUriMatcher.kt | 63 ++++--- toggles-app/build.gradle.kts | 29 +-- ...gglesProviderMatcherConfigurationIdTest.kt | 101 +++++++++++ ...glesProviderMatcherConfigurationKeyTest.kt | 101 +++++++++++ ...TogglesProviderMatcherConfigurationTest.kt | 97 ++++++++++ ...oviderMatcherCurrentConfigurationIdTest.kt | 104 +++++++++++ ...viderMatcherCurrentConfigurationKeyTest.kt | 170 ++++++++++++++++++ ...roviderMatcherCurrentConfigurationTest.kt} | 127 ++++--------- ...oviderPredefinedConfigurationValuesTest.kt | 151 ++++++++++++++++ .../toggles/core/TogglesProviderContract.kt | 60 +++++-- .../toggles/prefs/TogglesPreferencesImpl.kt | 3 +- 18 files changed, 912 insertions(+), 172 deletions(-) create mode 100644 toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationIdTest.kt create mode 100644 toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt create mode 100644 toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt create mode 100644 toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherCurrentConfigurationIdTest.kt create mode 100644 toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherCurrentConfigurationKeyTest.kt rename toggles-app/src/test/java/se/eelde/toggles/provider/{TogglesProviderTest.kt => TogglesProviderMatcherCurrentConfigurationTest.kt} (61%) create mode 100644 toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderPredefinedConfigurationValuesTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 84720ee4..a027d101 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -44,5 +44,5 @@ tasks.named("dependencyUpdates", DependencyUpdatesTask::class.java).configure { } task("clean") { - delete(rootProject.buildDir) + delete(rootProject.layout.buildDirectory) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 847daf37..9c9b4041 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -44,7 +44,6 @@ androidx-compose-material-material-icons-extended = { module = "androidx.compose androidx-compose-material-material-ripple = { module = "androidx.compose.material:material-ripple" } androidx-compose-material3 = { module = "androidx.compose.material3:material3" } androidx-compose-runtime = { module = "androidx.compose.runtime:runtime" } -androidx-compose-runtime-runtime-saveable = { module = "androidx.compose.runtime:runtime-saveable" } androidx-compose-ui = { module = "androidx.compose.ui:ui" } androidx-compose-ui-ui-geometry = { module = "androidx.compose.ui:ui-geometry" } androidx-compose-ui-ui-graphics = { module = "androidx.compose.ui:ui-graphics" } @@ -127,7 +126,9 @@ org-jetbrains-kotlin-kotlin-stdlib-jdk8 = "org.jetbrains.kotlin:kotlin-stdlib-jd org-jetbrains-kotlinx-kotlinx-collections-immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5" org-jetbrains-kotlinx-kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" } org-jetbrains-kotlinx-kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "org-jetbrains-kotlinx" } +org-jetbrains-kotlinx-kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test" } org-robolectric = "org.robolectric:robolectric:4.10.3" +app-cash-turbine = "app.cash.turbine:turbine:1.0.0" se-eelde-toggles-toggles-core = { module = "se.eelde.toggles:toggles-core" } se-eelde-toggles-toggles-flow = { module = "se.eelde.toggles:toggles-flow" } se-eelde-toggles-toggles-prefs = { module = "se.eelde.toggles:toggles-prefs" } @@ -141,7 +142,6 @@ com-github-ben-manes-versions = "com.github.ben-manes.versions:0.50.0" com-github-triplet-play = "com.github.triplet.play:3.8.4" com-google-dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "com-google-dagger" } com-google-devtools-ksp = "com.google.devtools.ksp:1.9.22-1.0.16" -com-google-firebase-crashlytics = "com.google.firebase.crashlytics:2.9.8" com-google-gms-google-services = "com.google.gms.google-services:4.3.15" com-vanniktech-maven-publish = "com.vanniktech.maven.publish:0.27.0" nl-littlerobots-version-catalog-update = "nl.littlerobots.version-catalog-update:0.8.3" diff --git a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt index f2a216e5..0c5430c3 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt @@ -52,6 +52,19 @@ interface WrenchConfigurationDao { ) fun getToggles(configurationKey: String, scopeId: List): Cursor + @Query( + "SELECT configuration.id, " + + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value," + + " scope.name as scope" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + + " WHERE configuration.configurationKey = (:configurationKey)" + ) + fun getToggles(configurationKey: String): Cursor + @Query( "SELECT * " + " FROM " + ConfigurationTable.TABLE_NAME + diff --git a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationValueDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationValueDao.kt index f0bfc637..061c8652 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationValueDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationValueDao.kt @@ -12,7 +12,6 @@ import se.eelde.toggles.database.tables.ConfigurationValueTable @Dao interface WrenchConfigurationValueDao { - @Query( "SELECT * FROM " + ConfigurationValueTable.TABLE_NAME + " WHERE " + ConfigurationValueTable.COL_CONFIG_ID + " = (:configurationId) AND " + ConfigurationValueTable.COL_SCOPE + " = (:scopeId)" diff --git a/modules/database/src/main/java/se/eelde/toggles/database/WrenchScope.kt b/modules/database/src/main/java/se/eelde/toggles/database/WrenchScope.kt index 5511cc25..47d03c9a 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/WrenchScope.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/WrenchScope.kt @@ -5,6 +5,7 @@ import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Index import androidx.room.PrimaryKey +import se.eelde.toggles.core.ColumnNames import se.eelde.toggles.database.tables.ApplicationTable import se.eelde.toggles.database.tables.ScopeTable import java.util.Date @@ -40,7 +41,7 @@ data class WrenchScope constructor( companion object { - const val SCOPE_DEFAULT = "wrench_default" + const val SCOPE_DEFAULT = ColumnNames.ToggleScope.DEFAULT_SCOPE const val SCOPE_USER = "Development scope" fun newWrenchScope() = WrenchScope(0, 0, SCOPE_DEFAULT, Date()) diff --git a/modules/database/src/main/java/se/eelde/toggles/database/tables/ScopeTable.kt b/modules/database/src/main/java/se/eelde/toggles/database/tables/ScopeTable.kt index 729618ae..f9166e88 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/tables/ScopeTable.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/tables/ScopeTable.kt @@ -1,11 +1,13 @@ package se.eelde.toggles.database.tables +import se.eelde.toggles.core.ColumnNames + interface ScopeTable { companion object { const val TABLE_NAME = "scope" - const val COL_ID = "id" - const val COL_APP_ID = "applicationId" - const val COL_NAME = "name" - const val COL_SELECTED_TIMESTAMP = "selectedTimestamp" + const val COL_ID = ColumnNames.ToggleScope.COL_ID + const val COL_APP_ID = ColumnNames.ToggleScope.COL_APP_ID + const val COL_NAME = ColumnNames.ToggleScope.COL_NAME + const val COL_SELECTED_TIMESTAMP = ColumnNames.ToggleScope.COL_SELECTED_TIMESTAMP } } diff --git a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt index 660bc955..76682ded 100644 --- a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt +++ b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt @@ -114,7 +114,7 @@ class TogglesProvider : ContentProvider() { var cursor: Cursor? when (togglesUriMatcher.match(uri)) { - togglesUriMatcher.currentConfigurationId -> { + UriMatch.CURRENT_CONFIGURATION_ID -> { val scope = getSelectedScope(context, scopeDao, callingApplication.id) cursor = configurationDao.getToggle( java.lang.Long.valueOf(uri.lastPathSegment!!), @@ -132,7 +132,7 @@ class TogglesProvider : ContentProvider() { } } - togglesUriMatcher.currentConfigurationKey -> { + UriMatch.CURRENT_CONFIGURATION_KEY -> { // this change is experimental and might be a way // for consumers to @Suppress("ConstantConditionIf") @@ -157,6 +157,10 @@ class TogglesProvider : ContentProvider() { } } + UriMatch.CONFIGURATION_KEY -> { + cursor = configurationDao.getToggles(uri.lastPathSegment!!) + } + else -> { throw UnsupportedOperationException("Not yet implemented $uri") } @@ -182,7 +186,7 @@ class TogglesProvider : ContentProvider() { val insertId: Long when (togglesUriMatcher.match(uri)) { - togglesUriMatcher.currentConfigurations -> { + UriMatch.CURRENT_CONFIGURATIONS -> { val toggle = Toggle.fromContentValues(values!!) var wrenchConfiguration: WrenchConfiguration? = @@ -219,7 +223,7 @@ class TogglesProvider : ContentProvider() { insertId = wrenchConfiguration.id } - togglesUriMatcher.predefinedConfigurationValues -> { + UriMatch.PREDEFINED_CONFIGURATION_VALUES -> { val fullConfig = WrenchPredefinedConfigurationValue.fromContentValues(values!!) insertId = try { predefinedConfigurationDao.insert(fullConfig) @@ -231,6 +235,10 @@ class TogglesProvider : ContentProvider() { } } + UriMatch.CONFIGURATIONS -> { + + } + else -> { throw UnsupportedOperationException("Not yet implemented $uri") } @@ -265,7 +273,7 @@ class TogglesProvider : ContentProvider() { val updatedRows: Int when (togglesUriMatcher.match(uri)) { - togglesUriMatcher.currentConfigurationId -> { + UriMatch.CURRENT_CONFIGURATION_ID -> { val toggle = Toggle.fromContentValues(values!!) val scope = getSelectedScope(context, scopeDao, callingApplication.id) updatedRows = configurationValueDao.updateConfigurationValueSync( @@ -314,25 +322,28 @@ class TogglesProvider : ContentProvider() { } return when (togglesUriMatcher.match(uri)) { - togglesUriMatcher.currentConfigurations -> { + UriMatch.CURRENT_CONFIGURATIONS -> "vnd.android.cursor.dir/vnd.${context!!.packageName}.currentConfiguration" - } - togglesUriMatcher.currentConfigurationId -> { + UriMatch.CURRENT_CONFIGURATION_ID -> "vnd.android.cursor.item/vnd.${context!!.packageName}.currentConfiguration" - } - togglesUriMatcher.currentConfigurationKey -> { - "vnd.android.cursor.dir/vnd.${context!!.packageName}.currentConfiguration" - } + UriMatch.CURRENT_CONFIGURATION_KEY -> + "vnd.android.cursor.item/vnd.${context!!.packageName}.currentConfiguration" + + UriMatch.CONFIGURATIONS -> + "vnd.android.cursor.dir/vnd.${context!!.packageName}.configuration" + + UriMatch.CONFIGURATION_ID -> + "vnd.android.cursor.dir/vnd.${context!!.packageName}.configuration" - togglesUriMatcher.predefinedConfigurationValues -> { + UriMatch.CONFIGURATION_KEY -> + "vnd.android.cursor.dir/vnd.${context!!.packageName}.configuration" + + UriMatch.PREDEFINED_CONFIGURATION_VALUES -> "vnd.android.cursor.dir/vnd.${context!!.packageName}.predefinedConfigurationValue" - } - else -> { - throw UnsupportedOperationException("Not yet implemented") - } + UriMatch.UNKNOWN -> TODO() } } diff --git a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesUriMatcher.kt b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesUriMatcher.kt index eb55604a..3787b6a1 100644 --- a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesUriMatcher.kt +++ b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesUriMatcher.kt @@ -3,51 +3,64 @@ package se.eelde.toggles.provider import android.content.UriMatcher import android.net.Uri -class TogglesUriMatcher constructor(providerAuthority: String) { - @Suppress("MagicNumber") - internal val currentConfigurationId = 1 - - @Suppress("MagicNumber") - internal val currentConfigurationKey = 2 - - @Suppress("MagicNumber") - internal val currentConfigurations = 3 - - @Suppress("MagicNumber") - internal val predefinedConfigurationValues = 5 - - @Suppress("MagicNumber") - private val applicationId = 6 +enum class UriMatch { + CURRENT_CONFIGURATION_ID, + CURRENT_CONFIGURATION_KEY, + CURRENT_CONFIGURATIONS, + CONFIGURATIONS, + CONFIGURATION_ID, + CONFIGURATION_KEY, + PREDEFINED_CONFIGURATION_VALUES, + UNKNOWN, +} +class TogglesUriMatcher(providerAuthority: String) { private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH) - fun match(uri: Uri) = uriMatcher.match(uri) + fun match(uri: Uri): UriMatch { + val match = uriMatcher.match(uri) + return if (match == -1) { + UriMatch.UNKNOWN + } else { + UriMatch.entries[match] + } + } init { - uriMatcher.addURI( - providerAuthority, - "application/#", - applicationId - ) uriMatcher.addURI( providerAuthority, "currentConfiguration/#", - currentConfigurationId + UriMatch.CURRENT_CONFIGURATION_ID.ordinal ) uriMatcher.addURI( providerAuthority, "currentConfiguration/*", - currentConfigurationKey + UriMatch.CURRENT_CONFIGURATION_KEY.ordinal ) uriMatcher.addURI( providerAuthority, "currentConfiguration", - currentConfigurations + UriMatch.CURRENT_CONFIGURATIONS.ordinal + ) + uriMatcher.addURI( + providerAuthority, + "configuration", + UriMatch.CONFIGURATIONS.ordinal + ) + uriMatcher.addURI( + providerAuthority, + "configuration/#", + UriMatch.CONFIGURATION_ID.ordinal + ) + uriMatcher.addURI( + providerAuthority, + "configuration/*", + UriMatch.CONFIGURATION_KEY.ordinal ) uriMatcher.addURI( providerAuthority, "predefinedConfigurationValue", - predefinedConfigurationValues + UriMatch.PREDEFINED_CONFIGURATION_VALUES.ordinal ) } } diff --git a/toggles-app/build.gradle.kts b/toggles-app/build.gradle.kts index 01730a1b..768e7e38 100644 --- a/toggles-app/build.gradle.kts +++ b/toggles-app/build.gradle.kts @@ -127,24 +127,11 @@ dependencies { implementation(libs.androidx.hilt.hilt.navigation.compose) implementation(libs.androidx.navigation.navigation.compose) implementation(libs.androidx.startup.startup.runtime) - testImplementation(libs.junit) - - testImplementation(libs.androidx.test.core.ktx) - testImplementation(libs.androidx.test.ext.truth) - testImplementation(libs.androidx.test.rules) - testImplementation(libs.androidx.test.runner) - testImplementation(libs.androidx.test.ext.junit) - testImplementation(libs.org.robolectric) - testImplementation(libs.androidx.test.espresso.espresso.core) - testImplementation(libs.androidx.arch.core.core.testing) implementation(libs.com.google.dagger.hilt.android) ksp(libs.com.google.dagger.hilt.android.compiler) ksp(libs.androidx.hilt.hilt.compiler) - testImplementation(libs.com.google.dagger.hilt.android.testing) - kspTest(libs.com.google.dagger.hilt.android.compiler) - implementation(libs.androidx.lifecycle.lifecycle.common.java8) implementation(libs.com.google.dagger) @@ -163,6 +150,22 @@ dependencies { debugImplementation(libs.com.squareup.leakcanary.leakcanary.android) + testImplementation(libs.junit) + + + testImplementation(libs.androidx.test.core.ktx) + testImplementation(libs.androidx.test.ext.truth) + testImplementation(libs.androidx.test.rules) + testImplementation(libs.androidx.test.runner) + testImplementation(libs.androidx.test.ext.junit) + testImplementation(libs.org.robolectric) + testImplementation(libs.androidx.test.espresso.espresso.core) + testImplementation(libs.androidx.arch.core.core.testing) + testImplementation(libs.com.google.dagger.hilt.android.testing) + testImplementation(libs.org.jetbrains.kotlinx.kotlinx.coroutines.test) + testImplementation(libs.app.cash.turbine) + kspTest(libs.com.google.dagger.hilt.android.compiler) + androidTestImplementation(libs.androidx.test.core.ktx) androidTestImplementation(libs.androidx.test.ext.junit) androidTestImplementation(platform(libs.androidx.compose.bom)) diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationIdTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationIdTest.kt new file mode 100644 index 00000000..74b9561a --- /dev/null +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationIdTest.kt @@ -0,0 +1,101 @@ +package se.eelde.toggles.provider + +import android.app.Application +import android.content.Context +import android.os.Build +import androidx.core.content.ContextCompat +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.HiltTestApplication +import dagger.hilt.android.testing.UninstallModules +import dagger.hilt.components.SingletonComponent +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config +import se.eelde.toggles.BuildConfig +import se.eelde.toggles.R +import se.eelde.toggles.core.TogglesProviderContract +import se.eelde.toggles.database.WrenchDatabase +import se.eelde.toggles.di.DatabaseModule +import javax.inject.Inject +import javax.inject.Singleton + +@HiltAndroidTest +@RunWith(AndroidJUnit4::class) +@UninstallModules(DatabaseModule::class) +@Config(application = HiltTestApplication::class, sdk = [Build.VERSION_CODES.P]) +class TogglesProviderMatcherConfigurationIdTest { + @get:Rule + var hiltRule = HiltAndroidRule(this) + + private lateinit var togglesProvider: TogglesProvider + + @Module + @InstallIn(SingletonComponent::class) + object TestModule { + @Singleton + @Provides + fun provideWrenchDb(@ApplicationContext context: Context): WrenchDatabase { + return Room.inMemoryDatabaseBuilder(context, WrenchDatabase::class.java) + .allowMainThreadQueries().build() + } + } + + @Inject + lateinit var wrenchDatabase: WrenchDatabase + + @Before + fun setUp() { + hiltRule.inject() + + val contentProviderController = + Robolectric.buildContentProvider(TogglesProvider::class.java) + .create(BuildConfig.CONFIG_AUTHORITY) + togglesProvider = contentProviderController.get() + + val context = ApplicationProvider.getApplicationContext() + val appIcon = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground) + shadowOf(context.packageManager).setApplicationIcon( + context.applicationInfo.packageName, + appIcon + ) + } + + @Test + fun testGetTypePredefinedConfigurationValue() { + val type = togglesProvider.getType(TogglesProviderContract.configurationUri(0)) + assertEquals("vnd.android.cursor.item/vnd.se.eelde.toggles.configuration", type) + } + + @Test + fun testInsert() { + TODO("To be implemented") + } + + @Test + fun testUpdate() { + TODO("To be implemented") + } + + @Test + fun testQuery() { + TODO("To be implemented") + } + + @Test + fun testDelete() { + TODO("To be implemented") + } +} diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt new file mode 100644 index 00000000..1a8d9dbf --- /dev/null +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt @@ -0,0 +1,101 @@ +package se.eelde.toggles.provider + +import android.app.Application +import android.content.Context +import android.os.Build +import androidx.core.content.ContextCompat +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.HiltTestApplication +import dagger.hilt.android.testing.UninstallModules +import dagger.hilt.components.SingletonComponent +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config +import se.eelde.toggles.BuildConfig +import se.eelde.toggles.R +import se.eelde.toggles.core.TogglesProviderContract +import se.eelde.toggles.database.WrenchDatabase +import se.eelde.toggles.di.DatabaseModule +import javax.inject.Inject +import javax.inject.Singleton + +@HiltAndroidTest +@RunWith(AndroidJUnit4::class) +@UninstallModules(DatabaseModule::class) +@Config(application = HiltTestApplication::class, sdk = [Build.VERSION_CODES.P]) +class TogglesProviderMatcherConfigurationKeyTest { + @get:Rule + var hiltRule = HiltAndroidRule(this) + + private lateinit var togglesProvider: TogglesProvider + + @Module + @InstallIn(SingletonComponent::class) + object TestModule { + @Singleton + @Provides + fun provideWrenchDb(@ApplicationContext context: Context): WrenchDatabase { + return Room.inMemoryDatabaseBuilder(context, WrenchDatabase::class.java) + .allowMainThreadQueries().build() + } + } + + @Inject + lateinit var wrenchDatabase: WrenchDatabase + + @Before + fun setUp() { + hiltRule.inject() + + val contentProviderController = + Robolectric.buildContentProvider(TogglesProvider::class.java) + .create(BuildConfig.CONFIG_AUTHORITY) + togglesProvider = contentProviderController.get() + + val context = ApplicationProvider.getApplicationContext() + val appIcon = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground) + shadowOf(context.packageManager).setApplicationIcon( + context.applicationInfo.packageName, + appIcon + ) + } + + @Test + fun testGetTypePredefinedConfigurationValue() { + val type = togglesProvider.getType(TogglesProviderContract.configurationUri("fakeKey")) + assertEquals("vnd.android.cursor.item/vnd.se.eelde.toggles.configuration", type) + } + + @Test + fun testInsert() { + TODO("To be implemented") + } + + @Test + fun testUpdate() { + TODO("To be implemented") + } + + @Test + fun testQuery() { + TODO("To be implemented") + } + + @Test + fun testDelete() { + TODO("To be implemented") + } +} diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt new file mode 100644 index 00000000..847ca130 --- /dev/null +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt @@ -0,0 +1,97 @@ +package se.eelde.toggles.provider + +import android.app.Application +import android.content.Context +import android.os.Build +import androidx.core.content.ContextCompat +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.HiltTestApplication +import dagger.hilt.android.testing.UninstallModules +import dagger.hilt.components.SingletonComponent +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config +import se.eelde.toggles.BuildConfig +import se.eelde.toggles.R +import se.eelde.toggles.core.Toggle +import se.eelde.toggles.core.TogglesProviderContract +import se.eelde.toggles.database.WrenchDatabase +import se.eelde.toggles.di.DatabaseModule +import javax.inject.Inject +import javax.inject.Singleton + +@HiltAndroidTest +@RunWith(AndroidJUnit4::class) +@UninstallModules(DatabaseModule::class) +@Config(application = HiltTestApplication::class, sdk = [Build.VERSION_CODES.P]) +class TogglesProviderMatcherConfigurationTest { + @get:Rule + var hiltRule = HiltAndroidRule(this) + + private lateinit var togglesProvider: TogglesProvider + + @Module + @InstallIn(SingletonComponent::class) + object TestModule { + @Singleton + @Provides + fun provideWrenchDb(@ApplicationContext context: Context): WrenchDatabase { + return Room.inMemoryDatabaseBuilder(context, WrenchDatabase::class.java) + .allowMainThreadQueries().build() + } + } + + @Inject + lateinit var wrenchDatabase: WrenchDatabase + + @Before + fun setUp() { + hiltRule.inject() + + val contentProviderController = + Robolectric.buildContentProvider(TogglesProvider::class.java) + .create(BuildConfig.CONFIG_AUTHORITY) + togglesProvider = contentProviderController.get() + + val context = ApplicationProvider.getApplicationContext() + val appIcon = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground) + shadowOf(context.packageManager).setApplicationIcon( + context.applicationInfo.packageName, + appIcon + ) + } + + @Test + fun testGetTypePredefinedConfigurationValue() { + val type = togglesProvider.getType(TogglesProviderContract.configurationUri()) + assertEquals("vnd.android.cursor.dir/vnd.se.eelde.toggles.configuration", type) + } + + @Test + fun testUpdate() { + TODO("To be implemented") + } + + @Test + fun testQuery() { + TODO("To be implemented") + } + + @Test + fun testDelete() { + TODO("To be implemented") + } +} diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherCurrentConfigurationIdTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherCurrentConfigurationIdTest.kt new file mode 100644 index 00000000..07c61a59 --- /dev/null +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherCurrentConfigurationIdTest.kt @@ -0,0 +1,104 @@ +package se.eelde.toggles.provider + +import android.app.Application +import android.content.Context +import android.os.Build +import androidx.core.content.ContextCompat +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.HiltTestApplication +import dagger.hilt.android.testing.UninstallModules +import dagger.hilt.components.SingletonComponent +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config +import se.eelde.toggles.BuildConfig +import se.eelde.toggles.R +import se.eelde.toggles.core.Toggle +import se.eelde.toggles.core.TogglesProviderContract +import se.eelde.toggles.database.WrenchDatabase +import se.eelde.toggles.di.DatabaseModule +import javax.inject.Inject +import javax.inject.Singleton + +@HiltAndroidTest +@RunWith(AndroidJUnit4::class) +@UninstallModules(DatabaseModule::class) +@Config(application = HiltTestApplication::class, sdk = [Build.VERSION_CODES.P]) +class TogglesProviderMatcherCurrentConfigurationIdTest { + @get:Rule + var hiltRule = HiltAndroidRule(this) + + private lateinit var togglesProvider: TogglesProvider + + @Module + @InstallIn(SingletonComponent::class) + object TestModule { + @Singleton + @Provides + fun provideWrenchDb(@ApplicationContext context: Context): WrenchDatabase { + return Room.inMemoryDatabaseBuilder(context, WrenchDatabase::class.java) + .allowMainThreadQueries().build() + } + } + + @Inject + lateinit var wrenchDatabase: WrenchDatabase + + @Before + fun setUp() { + hiltRule.inject() + + val contentProviderController = + Robolectric.buildContentProvider(TogglesProvider::class.java) + .create(BuildConfig.CONFIG_AUTHORITY) + togglesProvider = contentProviderController.get() + + val context = ApplicationProvider.getApplicationContext() + val appIcon = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground) + shadowOf(context.packageManager).setApplicationIcon( + context.applicationInfo.packageName, + appIcon + ) + } + + @Test + fun testGetTypePredefinedConfigurationValue() { + val type = togglesProvider.getType(TogglesProviderContract.toggleUri(0)) + assertEquals("vnd.android.cursor.item/vnd.se.eelde.toggles.currentConfiguration", type) + } + + @Test(expected = UnsupportedOperationException::class) + fun testMissingInsertForToggleWithId() { + togglesProvider.insert( + TogglesProviderContract.toggleUri(0), + getToggle("dummyToggle").toContentValues() + ) + } + + @Test(expected = UnsupportedOperationException::class) + fun testMissingDeleteForToggleWithId() { + togglesProvider.delete(TogglesProviderContract.toggleUri(0), null, null) + } + + private fun getToggle(key: String): Toggle { + return Toggle { + id = 0L + type = "toggletype" + this.key = key + value = "togglevalue" + } + } +} diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherCurrentConfigurationKeyTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherCurrentConfigurationKeyTest.kt new file mode 100644 index 00000000..1b30795c --- /dev/null +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherCurrentConfigurationKeyTest.kt @@ -0,0 +1,170 @@ +package se.eelde.toggles.provider + +import android.app.Application +import android.content.Context +import android.os.Build +import androidx.core.content.ContextCompat +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.HiltTestApplication +import dagger.hilt.android.testing.UninstallModules +import dagger.hilt.components.SingletonComponent +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config +import se.eelde.toggles.BuildConfig +import se.eelde.toggles.R +import se.eelde.toggles.core.Toggle +import se.eelde.toggles.core.TogglesProviderContract +import se.eelde.toggles.database.WrenchDatabase +import se.eelde.toggles.di.DatabaseModule +import javax.inject.Inject +import javax.inject.Singleton + +@HiltAndroidTest +@RunWith(AndroidJUnit4::class) +@UninstallModules(DatabaseModule::class) +@Config(application = HiltTestApplication::class, sdk = [Build.VERSION_CODES.P]) +class TogglesProviderMatcherCurrentConfigurationKeyTest { + @get:Rule + var hiltRule = HiltAndroidRule(this) + + private lateinit var togglesProvider: TogglesProvider + + @Module + @InstallIn(SingletonComponent::class) + object TestModule { + @Singleton + @Provides + fun provideWrenchDb(@ApplicationContext context: Context): WrenchDatabase { + return Room.inMemoryDatabaseBuilder(context, WrenchDatabase::class.java) + .allowMainThreadQueries().build() + } + } + + @Inject + lateinit var wrenchDatabase: WrenchDatabase + + @Before + fun setUp() { + hiltRule.inject() + + val contentProviderController = + Robolectric.buildContentProvider(TogglesProvider::class.java) + .create(BuildConfig.CONFIG_AUTHORITY) + togglesProvider = contentProviderController.get() + + val context = ApplicationProvider.getApplicationContext() + val appIcon = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground) + shadowOf(context.packageManager).setApplicationIcon( + context.applicationInfo.packageName, + appIcon + ) + } + + @Test + fun testUpdateToggle() { + val updateToggleKey = "updateToggleKey" + + val uri = TogglesProviderContract.toggleUri() + val insertToggle = getToggle(updateToggleKey) + val insertToggleUri = togglesProvider.insert(uri, insertToggle.toContentValues()) + assertNotNull(insertToggleUri) + + var cursor = togglesProvider.query( + TogglesProviderContract.toggleUri(updateToggleKey), + null, + null, + null, + null + ) + assertNotNull(cursor) + assertTrue(cursor.moveToFirst()) + + val providerToggle = Toggle.fromCursor(cursor) + assertEquals(insertToggle.key, providerToggle.key) + assertEquals(insertToggle.value, providerToggle.value) + assertEquals(insertToggle.type, providerToggle.type) + + val updateToggle = Toggle { + id = providerToggle.id + type = providerToggle.type + key = providerToggle.key + value = providerToggle.value!! + providerToggle.value!! + } + + val update = togglesProvider.update( + TogglesProviderContract.toggleUri(updateToggle.id), + updateToggle.toContentValues(), + null, + null + ) + assertEquals(1, update) + + cursor = togglesProvider.query( + TogglesProviderContract.toggleUri(updateToggleKey), + null, + null, + null, + null + ) + assertNotNull(cursor) + + assertTrue(cursor.moveToFirst()) + val updatedToggle = Toggle.fromCursor(cursor) + + assertEquals(insertToggle.value!! + insertToggle.value!!, updatedToggle.value) + } + + @Test + fun testGetTypePredefinedConfigurationValue() { + val type = togglesProvider.getType(TogglesProviderContract.toggleUri("fake")) + assertEquals("vnd.android.cursor.item/vnd.se.eelde.toggles.currentConfiguration", type) + } + + @Test(expected = UnsupportedOperationException::class) + fun testMissingInsertForToggleWithKey() { + togglesProvider.insert( + TogglesProviderContract.toggleUri("fake"), + getToggle("dummyToggle").toContentValues() + ) + } + + @Test(expected = UnsupportedOperationException::class) + fun testMissingUpdateForToggleWithKey() { + togglesProvider.update( + TogglesProviderContract.toggleUri("fake"), + getToggle("dummyToggle").toContentValues(), + null, + null + ) + } + + @Test(expected = UnsupportedOperationException::class) + fun testMissingDeleteForToggleWithKey() { + togglesProvider.delete(TogglesProviderContract.toggleUri("fake"), null, null) + } + + private fun getToggle(key: String): Toggle { + return Toggle { + id = 0L + type = "toggletype" + this.key = key + value = "togglevalue" + } + } +} diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherCurrentConfigurationTest.kt similarity index 61% rename from toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderTest.kt rename to toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherCurrentConfigurationTest.kt index 548ff19f..16494cf2 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherCurrentConfigurationTest.kt @@ -16,7 +16,9 @@ import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltTestApplication import dagger.hilt.android.testing.UninstallModules import dagger.hilt.components.SingletonComponent -import org.junit.Assert +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test @@ -27,17 +29,17 @@ import org.robolectric.annotation.Config import se.eelde.toggles.BuildConfig import se.eelde.toggles.R import se.eelde.toggles.core.Toggle -import se.eelde.toggles.core.ToggleValue import se.eelde.toggles.core.TogglesProviderContract import se.eelde.toggles.database.WrenchDatabase import se.eelde.toggles.di.DatabaseModule +import javax.inject.Inject import javax.inject.Singleton @HiltAndroidTest @RunWith(AndroidJUnit4::class) @UninstallModules(DatabaseModule::class) @Config(application = HiltTestApplication::class, sdk = [Build.VERSION_CODES.P]) -class TogglesProviderTest { +class TogglesProviderMatcherCurrentConfigurationTest { @get:Rule var hiltRule = HiltAndroidRule(this) @@ -54,6 +56,9 @@ class TogglesProviderTest { } } + @Inject + lateinit var wrenchDatabase: WrenchDatabase + @Before fun setUp() { hiltRule.inject() @@ -71,6 +76,12 @@ class TogglesProviderTest { ) } + @Test + fun testGetTypePredefinedConfigurationValue() { + val type = togglesProvider.getType(TogglesProviderContract.toggleUri()) + assertEquals("vnd.android.cursor.dir/vnd.se.eelde.toggles.currentConfiguration", type) + } + @Test fun testInsertToggle() { val insertToggleKey = "insertToggleKey" @@ -78,7 +89,7 @@ class TogglesProviderTest { val uri = TogglesProviderContract.toggleUri() val insertToggle = getToggle(insertToggleKey) val insertToggleUri = togglesProvider.insert(uri, insertToggle.toContentValues()) - Assert.assertNotNull(insertToggleUri) + assertNotNull(insertToggleUri) var cursor = togglesProvider.query( TogglesProviderContract.toggleUri(insertToggleKey), @@ -87,29 +98,29 @@ class TogglesProviderTest { null, null ) - Assert.assertNotNull(cursor) - Assert.assertEquals(1, cursor.count) + assertNotNull(cursor) + assertEquals(1, cursor.count) cursor.moveToFirst() var queryToggle = Toggle.fromCursor(cursor) - Assert.assertEquals(insertToggle.key, queryToggle.key) - Assert.assertEquals(insertToggle.value, queryToggle.value) - Assert.assertEquals(insertToggle.type, queryToggle.type) + assertEquals(insertToggle.key, queryToggle.key) + assertEquals(insertToggle.value, queryToggle.value) + assertEquals(insertToggle.type, queryToggle.type) val toggleUri = TogglesProviderContract.toggleUri( Integer.parseInt(insertToggleUri.lastPathSegment!!).toLong() ) cursor = togglesProvider.query(toggleUri, null, null, null, null) - Assert.assertNotNull(cursor) - Assert.assertEquals(1, cursor.count) + assertNotNull(cursor) + assertEquals(1, cursor.count) cursor.moveToFirst() queryToggle = Toggle.fromCursor(cursor) - Assert.assertEquals(insertToggle.key, queryToggle.key) - Assert.assertEquals(insertToggle.value, queryToggle.value) - Assert.assertEquals(insertToggle.type, queryToggle.type) + assertEquals(insertToggle.key, queryToggle.key) + assertEquals(insertToggle.value, queryToggle.value) + assertEquals(insertToggle.type, queryToggle.type) } @Test @@ -119,7 +130,7 @@ class TogglesProviderTest { val uri = TogglesProviderContract.toggleUri() val insertToggle = getToggle(updateToggleKey) val insertToggleUri = togglesProvider.insert(uri, insertToggle.toContentValues()) - Assert.assertNotNull(insertToggleUri) + assertNotNull(insertToggleUri) var cursor = togglesProvider.query( TogglesProviderContract.toggleUri(updateToggleKey), @@ -128,13 +139,13 @@ class TogglesProviderTest { null, null ) - Assert.assertNotNull(cursor) - Assert.assertTrue(cursor.moveToFirst()) + assertNotNull(cursor) + assertTrue(cursor.moveToFirst()) val providerToggle = Toggle.fromCursor(cursor) - Assert.assertEquals(insertToggle.key, providerToggle.key) - Assert.assertEquals(insertToggle.value, providerToggle.value) - Assert.assertEquals(insertToggle.type, providerToggle.type) + assertEquals(insertToggle.key, providerToggle.key) + assertEquals(insertToggle.value, providerToggle.value) + assertEquals(insertToggle.type, providerToggle.type) val updateToggle = Toggle { id = providerToggle.id @@ -149,7 +160,7 @@ class TogglesProviderTest { null, null ) - Assert.assertEquals(1, update) + assertEquals(1, update) cursor = togglesProvider.query( TogglesProviderContract.toggleUri(updateToggleKey), @@ -158,38 +169,12 @@ class TogglesProviderTest { null, null ) - Assert.assertNotNull(cursor) + assertNotNull(cursor) - Assert.assertTrue(cursor.moveToFirst()) + assertTrue(cursor.moveToFirst()) val updatedToggle = Toggle.fromCursor(cursor) - Assert.assertEquals(insertToggle.value!! + insertToggle.value!!, updatedToggle.value) - } - - @Test(expected = UnsupportedOperationException::class) - fun testMissingInsertForToggleWithId() { - togglesProvider.insert( - TogglesProviderContract.toggleUri(0), - getToggle("dummyToggle").toContentValues() - ) - } - - @Test(expected = UnsupportedOperationException::class) - fun testMissingInsertForToggleWithKey() { - togglesProvider.insert( - TogglesProviderContract.toggleUri("fake"), - getToggle("dummyToggle").toContentValues() - ) - } - - @Test(expected = UnsupportedOperationException::class) - fun testMissingUpdateForToggleWithKey() { - togglesProvider.update( - TogglesProviderContract.toggleUri("fake"), - getToggle("dummyToggle").toContentValues(), - null, - null - ) + assertEquals(insertToggle.value!! + insertToggle.value!!, updatedToggle.value) } @Test(expected = UnsupportedOperationException::class) @@ -202,16 +187,6 @@ class TogglesProviderTest { ) } - @Test(expected = UnsupportedOperationException::class) - fun testMissingUpdateForToggleValues() { - togglesProvider.update( - TogglesProviderContract.toggleValueUri(), - getToggleValue("dummyToggleValue").toContentValues(), - null, - null - ) - } - @Test(expected = UnsupportedOperationException::class) fun testMissingQueryForToggles() { togglesProvider.update( @@ -222,43 +197,11 @@ class TogglesProviderTest { ) } - @Test(expected = UnsupportedOperationException::class) - fun testMissingQueryForToggleValues() { - togglesProvider.update( - TogglesProviderContract.toggleValueUri(), - getToggleValue("dummyToggleValue").toContentValues(), - null, - null - ) - } - - @Test(expected = UnsupportedOperationException::class) - fun testMissingDeleteForToggleWithId() { - togglesProvider.delete(TogglesProviderContract.toggleUri(0), null, null) - } - - @Test(expected = UnsupportedOperationException::class) - fun testMissingDeleteForToggleWithKey() { - togglesProvider.delete(TogglesProviderContract.toggleUri("fake"), null, null) - } - @Test(expected = UnsupportedOperationException::class) fun testMissingDeleteForToggles() { togglesProvider.delete(TogglesProviderContract.toggleUri(), null, null) } - @Test(expected = UnsupportedOperationException::class) - fun testMissingDeleteForToggleValues() { - togglesProvider.delete(TogglesProviderContract.toggleValueUri(), null, null) - } - - private fun getToggleValue(value: String): ToggleValue { - return ToggleValue { - id = 0 - this.value = value - } - } - private fun getToggle(key: String): Toggle { return Toggle { id = 0L diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderPredefinedConfigurationValuesTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderPredefinedConfigurationValuesTest.kt new file mode 100644 index 00000000..a084607f --- /dev/null +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderPredefinedConfigurationValuesTest.kt @@ -0,0 +1,151 @@ +package se.eelde.toggles.provider + +import android.app.Application +import android.content.Context +import android.os.Build +import androidx.core.content.ContextCompat +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import app.cash.turbine.test +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.HiltTestApplication +import dagger.hilt.android.testing.UninstallModules +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config +import se.eelde.toggles.BuildConfig +import se.eelde.toggles.R +import se.eelde.toggles.core.Toggle +import se.eelde.toggles.core.ToggleValue +import se.eelde.toggles.core.TogglesProviderContract +import se.eelde.toggles.database.WrenchDatabase +import se.eelde.toggles.di.DatabaseModule +import javax.inject.Inject +import javax.inject.Singleton + +@HiltAndroidTest +@RunWith(AndroidJUnit4::class) +@UninstallModules(DatabaseModule::class) +@Config(application = HiltTestApplication::class, sdk = [Build.VERSION_CODES.P]) +class TogglesProviderPredefinedConfigurationValuesTest { + @get:Rule + var hiltRule = HiltAndroidRule(this) + + private lateinit var togglesProvider: TogglesProvider + + @Module + @InstallIn(SingletonComponent::class) + object TestModule { + @Singleton + @Provides + fun provideWrenchDb(@ApplicationContext context: Context): WrenchDatabase { + return Room.inMemoryDatabaseBuilder(context, WrenchDatabase::class.java) + .allowMainThreadQueries().build() + } + } + + @Inject + lateinit var wrenchDatabase: WrenchDatabase + + @Before + fun setUp() { + hiltRule.inject() + + val contentProviderController = + Robolectric.buildContentProvider(TogglesProvider::class.java) + .create(BuildConfig.CONFIG_AUTHORITY) + togglesProvider = contentProviderController.get() + + val context = ApplicationProvider.getApplicationContext() + val appIcon = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground) + shadowOf(context.packageManager).setApplicationIcon( + context.applicationInfo.packageName, + appIcon + ) + } + + @Test + fun testGetTypePredefinedConfigurationValue() { + val type = togglesProvider.getType(TogglesProviderContract.toggleValueUri()) + assertEquals("vnd.android.cursor.dir/vnd.se.eelde.toggles.predefinedConfigurationValue", type) + } + + @Test + fun testInsertPredefinedConfigurationValueType() = runTest { + val insertToggleKey = "insertToggleKey" + + val toggleUri = TogglesProviderContract.toggleUri() + val insertToggle = getToggle(insertToggleKey) + val insertToggleUri = togglesProvider.insert(toggleUri, insertToggle.toContentValues()) + + val configId = insertToggleUri.lastPathSegment!!.toLong() + val toggleValue = ToggleValue { + configurationId = configId + value = "FIRST" + } + + togglesProvider.insert(TogglesProviderContract.toggleValueUri(), toggleValue.toContentValues()) + wrenchDatabase.predefinedConfigurationValueDao().getByConfigurationId(configId).test { + val wrenchPredefinedConfigurationValueList = awaitItem() + assertEquals(1, wrenchPredefinedConfigurationValueList.size) + wrenchPredefinedConfigurationValueList[0].let { + assertEquals(toggleValue.value, it.value) + assertEquals(toggleValue.configurationId, it.configurationId) + } + } + } + + @Test(expected = UnsupportedOperationException::class) + fun testMissingQueryForToggleValues() { + togglesProvider.update( + TogglesProviderContract.toggleValueUri(), + getToggleValue("dummyToggleValue").toContentValues(), + null, + null + ) + } + + @Test(expected = UnsupportedOperationException::class) + fun testMissingUpdateForToggleValues() { + togglesProvider.update( + TogglesProviderContract.toggleValueUri(), + getToggleValue("dummyToggleValue").toContentValues(), + null, + null + ) + } + + @Test(expected = UnsupportedOperationException::class) + fun testMissingDeleteForToggleValues() { + togglesProvider.delete(TogglesProviderContract.toggleValueUri(), null, null) + } + + private fun getToggleValue(value: String): ToggleValue { + return ToggleValue { + id = 0 + this.value = value + } + } + + private fun getToggle(key: String): Toggle { + return Toggle { + id = 0L + type = "toggletype" + this.key = key + value = "togglevalue" + } + } +} diff --git a/toggles-core/src/main/java/se/eelde/toggles/core/TogglesProviderContract.kt b/toggles-core/src/main/java/se/eelde/toggles/core/TogglesProviderContract.kt index 26f0c305..a8371229 100644 --- a/toggles-core/src/main/java/se/eelde/toggles/core/TogglesProviderContract.kt +++ b/toggles-core/src/main/java/se/eelde/toggles/core/TogglesProviderContract.kt @@ -4,6 +4,7 @@ import android.content.ContentValues import android.database.Cursor import android.net.Uri import androidx.annotation.StringDef +import java.util.Date @Suppress("LibraryEntitiesShouldNotBePublic") public object ColumnNames { @@ -19,6 +20,15 @@ public object ColumnNames { public const val COL_VALUE: String = "value" public const val COL_CONFIG_ID: String = "configurationId" } + + public object ToggleScope { + public const val COL_ID: String = "id" + public const val COL_APP_ID: String = "applicationId" + public const val COL_NAME: String = "name" + public const val COL_SELECTED_TIMESTAMP: String = "selectedTimestamp" + + public const val DEFAULT_SCOPE: String = "wrench_default" + } } @Suppress("LibraryEntitiesShouldNotBePublic") @@ -87,12 +97,19 @@ public fun ToggleValue(initializer: ToggleValue.Builder.() -> Unit): ToggleValue return ToggleValue.Builder().apply(initializer).build() } +public class ToggleScope private constructor( + public val id: Long = 0, + public val name: String, + public val timeStamp: Date, +) + @Suppress("LibraryEntitiesShouldNotBePublic") public class Toggle private constructor( public var id: Long = 0, @ToggleType public val type: String, public val key: String = "", public val value: String? = null, + public val scope: String? = null, ) { public class Builder { @set:JvmSynthetic @@ -219,23 +236,14 @@ public object TogglesProviderContract { private const val TOGGLES_API_VERSION_QUERY_PARAM = "API_VERSION" private const val TOGGLES_API_VERSION = 1 - private val applicationUri = Uri.parse("content://$TOGGLES_AUTHORITY/application") - private val configurationUri = Uri.parse("content://$TOGGLES_AUTHORITY/currentConfiguration") + private val configurationUri = Uri.parse("content://$TOGGLES_AUTHORITY/configuration") + private val currentConfigurationUri = Uri.parse("content://$TOGGLES_AUTHORITY/currentConfiguration") private val configurationValueUri = Uri.parse("content://$TOGGLES_AUTHORITY/predefinedConfigurationValue") - @JvmStatic - public fun applicationUri(id: Long): Uri { - return applicationUri - .buildUpon() - .appendPath(id.toString()) - .appendQueryParameter(TOGGLES_API_VERSION_QUERY_PARAM, TOGGLES_API_VERSION.toString()) - .build() - } - @JvmStatic public fun toggleUri(id: Long): Uri { - return configurationUri + return currentConfigurationUri .buildUpon() .appendPath(id.toString()) .appendQueryParameter(TOGGLES_API_VERSION_QUERY_PARAM, TOGGLES_API_VERSION.toString()) @@ -244,7 +252,7 @@ public object TogglesProviderContract { @JvmStatic public fun toggleUri(key: String): Uri { - return configurationUri + return currentConfigurationUri .buildUpon() .appendPath(key) .appendQueryParameter(TOGGLES_API_VERSION_QUERY_PARAM, TOGGLES_API_VERSION.toString()) @@ -253,7 +261,7 @@ public object TogglesProviderContract { @JvmStatic public fun toggleUri(): Uri { - return configurationUri + return currentConfigurationUri .buildUpon() .appendQueryParameter(TOGGLES_API_VERSION_QUERY_PARAM, TOGGLES_API_VERSION.toString()) .build() @@ -266,4 +274,28 @@ public object TogglesProviderContract { .appendQueryParameter(TOGGLES_API_VERSION_QUERY_PARAM, TOGGLES_API_VERSION.toString()) .build() } + + @JvmStatic + public fun configurationUri(): Uri { + return configurationUri + .buildUpon() + .appendQueryParameter(TOGGLES_API_VERSION_QUERY_PARAM, TOGGLES_API_VERSION.toString()) + .build() + } + @JvmStatic + public fun configurationUri(key: String): Uri { + return configurationUri + .buildUpon() + .appendPath(key) + .appendQueryParameter(TOGGLES_API_VERSION_QUERY_PARAM, TOGGLES_API_VERSION.toString()) + .build() + } + @JvmStatic + public fun configurationUri(id: Long): Uri { + return configurationUri + .buildUpon() + .appendPath(id.toString()) + .appendQueryParameter(TOGGLES_API_VERSION_QUERY_PARAM, TOGGLES_API_VERSION.toString()) + .build() + } } diff --git a/toggles-prefs/src/main/java/se/eelde/toggles/prefs/TogglesPreferencesImpl.kt b/toggles-prefs/src/main/java/se/eelde/toggles/prefs/TogglesPreferencesImpl.kt index fe1e50b3..73e454d8 100644 --- a/toggles-prefs/src/main/java/se/eelde/toggles/prefs/TogglesPreferencesImpl.kt +++ b/toggles-prefs/src/main/java/se/eelde/toggles/prefs/TogglesPreferencesImpl.kt @@ -33,8 +33,7 @@ public class TogglesPreferencesImpl(context: Context) : TogglesPreferences { contentResolver = contentResolver, toggleType = Toggle.TYPE.INTEGER, key = key - ) - ?: return defValue + ) ?: return defValue if (toggle.id == 0L) { toggle = toggle.copy(value = defValue.toString()) From af82f5887c293a66fd39a28406907f1a79a7cec6 Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Fri, 3 May 2024 11:57:36 +0200 Subject: [PATCH 02/24] Mr emulator --- .github/workflows/pull-request.yml | 52 ++++++- build-logic/conventions/build.gradle.kts | 6 +- build-logic/conventions/settings.gradle.kts | 13 +- ...android.application-conventions.gradle.kts | 71 +++++++-- ...les.android.library-conventions.gradle.kts | 2 - ...gles.android.module-conventions.gradle.kts | 53 +++++++ .../toggles.detekt-conventions.gradle.kts | 2 +- build.gradle.kts | 15 ++ gradle.properties | 4 +- gradle/libs.versions.toml | 142 +++++++----------- gradle/wrapper/gradle-wrapper.properties | 2 +- modules/applications/build.gradle.kts | 7 +- .../toggles/applications/ExampleSharedTest.kt | 26 ++++ .../resources/robolectric.properties | 1 + modules/booleanconfiguration/build.gradle.kts | 7 +- modules/compose-theme/build.gradle.kts | 7 +- modules/configurations/build.gradle.kts | 7 +- modules/enumconfiguration/build.gradle.kts | 7 +- modules/help/build.gradle.kts | 7 +- modules/integerconfiguration/build.gradle.kts | 7 +- modules/oss/build.gradle.kts | 7 +- modules/provider/build.gradle.kts | 8 +- modules/stringconfiguration/build.gradle.kts | 7 +- scripts/pr_check.sh | 17 +++ settings.gradle.kts | 13 +- toggles-app/build.gradle.kts | 20 +++ toggles-core/settings.gradle.kts | 11 +- toggles-flow-noop/settings.gradle.kts | 11 +- toggles-flow/build.gradle.kts | 2 +- toggles-flow/settings.gradle.kts | 11 +- toggles-prefs-noop/settings.gradle.kts | 11 +- toggles-prefs/build.gradle.kts | 2 +- toggles-prefs/settings.gradle.kts | 11 +- .../toggles/applications/ExampleSharedTest.kt | 29 ++++ .../resources/robolectric.properties | 1 + 35 files changed, 408 insertions(+), 191 deletions(-) create mode 100644 modules/applications/src/sharedTest/java/se/eelde/toggles/applications/ExampleSharedTest.kt create mode 100644 modules/applications/src/sharedTest/resources/robolectric.properties create mode 100755 scripts/pr_check.sh create mode 100644 toggles-sample/src/sharedTest/java/se/eelde/toggles/applications/ExampleSharedTest.kt create mode 100644 toggles-sample/src/sharedTest/resources/robolectric.properties diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index e3014cce..439fbea9 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -30,7 +30,19 @@ jobs: - name: Setup Gradle uses: gradle/gradle-build-action@v2 - - name: Run app Checks + - name: Run test without shared tests + run: ./gradlew test -Pshared-tests-are-android-tests=false + +# - name: Run test with shared tests +# run: ./gradlew test -Pshared-tests-are-android-tests=true + + - name: Run test without shared tests + run: ./gradlew assembleAndroidTest -Pshared-tests-are-android-tests=false + +# - name: Run test with shared tests +# run: ./gradlew assembleAndroidTest -Pshared-tests-are-android-tests=true + + - name: Run check run: ./gradlew check - name: Run core Checks @@ -55,3 +67,41 @@ jobs: name: Reports path: '**/build/reports/*' retention-days: 2 + + emulator-tests: + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + with: + fetch-depth: '0' + + - name: Check out java + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 17 + + - name: Copy CI gradle.properties + run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Enable KVM group perms + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: run tests + run: ./gradlew :toggles-app:pixel2api30DebugAndroidTest + + - name: "Upload reports" + uses: actions/upload-artifact@v4 + if: failure() + with: + name: "Android test results" + path: '**/build/reports/*' + retention-days: 2 diff --git a/build-logic/conventions/build.gradle.kts b/build-logic/conventions/build.gradle.kts index ff3a239f..952804da 100644 --- a/build-logic/conventions/build.gradle.kts +++ b/build-logic/conventions/build.gradle.kts @@ -10,11 +10,13 @@ java { dependencies { // implementation("gradle.plugin.com.github.spotbugs.snom:spotbugs-gradle-plugin:4.7.2") - implementation("se.premex:ownership-gradle-plugin:0.0.11") + implementation(libs.se.premex.ownership.gradle.plugin) implementation(libs.io.gitlab.arturbosch.detekt.detekt.gradle.plugin) implementation(libs.com.android.tools.build.gradle) implementation(libs.org.jetbrains.kotlin.kotlin.gradle.plugin) - implementation("com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:1.9.10-1.0.13") + implementation(libs.com.google.devtools.ksp.com.google.devtools.ksp.gradle.plugin) + implementation(libs.com.squareup.kotlinpoet) + implementation(libs.org.jetbrains.kotlin.compose.compiler.gradle.plugin) // https://github.com/gradle/gradle/issues/15383 implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) diff --git a/build-logic/conventions/settings.gradle.kts b/build-logic/conventions/settings.gradle.kts index 427ea61f..7afd983f 100644 --- a/build-logic/conventions/settings.gradle.kts +++ b/build-logic/conventions/settings.gradle.kts @@ -1,4 +1,4 @@ -rootProject.name="conventions" +rootProject.name = "conventions" pluginManagement { repositories { @@ -23,6 +23,13 @@ dependencyResolutionManagement { } plugins { - id("com.gradle.enterprise") version "3.16.1" - id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0") + id("com.gradle.develocity") version "3.18" + id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") +} + +develocity { + buildScan { + termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use") + termsOfUseAgree.set("yes") + } } diff --git a/build-logic/conventions/src/main/kotlin/toggles.android.application-conventions.gradle.kts b/build-logic/conventions/src/main/kotlin/toggles.android.application-conventions.gradle.kts index aabf6ca6..ac268315 100644 --- a/build-logic/conventions/src/main/kotlin/toggles.android.application-conventions.gradle.kts +++ b/build-logic/conventions/src/main/kotlin/toggles.android.application-conventions.gradle.kts @@ -9,31 +9,19 @@ plugins { kotlin("android") id("kotlin-android") id("toggles.detekt-conventions") + id("org.jetbrains.kotlin.plugin.compose") } android { - compileSdk = 34 + compileSdk = 35 defaultConfig { minSdk = 21 - targetSdk = 34 + targetSdk = 35 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } - buildFeatures { - compose = true - } - - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } - - @Suppress("UnstableApiUsage") - testOptions { - unitTests.isIncludeAndroidResources = true - } - lint { baseline = file("lint-baseline.xml") checkReleaseBuilds = true @@ -41,6 +29,50 @@ android { warningsAsErrors = true lintConfig = File("../lint.xml") } + + testOptions { + animationsDisabled = true + execution = "ANDROIDX_TEST_ORCHESTRATOR" + unitTests { + isReturnDefaultValues = true + isIncludeAndroidResources = true + } + managedDevices { + devices { + maybeCreate("pixel2api30").apply { + // Use device profiles you typically see in Android Studio. + device = "Pixel 2" + // Use only API levels 30 and higher. + apiLevel = 30 + // To include Google services, use "google". + systemImageSource = "aosp" + } + } + } + } + + sourceSets { +// val sharedTestDir = "src/sharedTest/" +// if(project.file(sharedTestDir).exists()) { +// val sharedTestSourceDir = sharedTestDir + "java" +// val sharedTestResourceDir = sharedTestDir + "resources" +// val sharedIsAndroid = +// providers.gradleProperty("shared-tests-are-android-tests").get().toBoolean() +// if (sharedIsAndroid) { +// logger.lifecycle("Shared tests are androidTests") +// named("androidTest") { +// java.srcDir(sharedTestSourceDir) +// resources.srcDir(sharedTestResourceDir) +// } +// } else { +// logger.lifecycle("Shared tests are unitTests") +// named("test") { +// java.srcDir(sharedTestSourceDir) +// resources.srcDir(sharedTestResourceDir) +// } +// } +// } + } } kotlin { @@ -51,4 +83,13 @@ kotlin { dependencies { lintChecks(libs.com.slack.lint.compose.compose.lint.checks) + + testImplementation(libs.androidx.test.ext.junit) + testImplementation(libs.org.robolectric) + testImplementation(libs.androidx.test.runner) + + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.androidx.test.runner) + + androidTestUtil(libs.androidx.test.orchestrator) } diff --git a/build-logic/conventions/src/main/kotlin/toggles.android.library-conventions.gradle.kts b/build-logic/conventions/src/main/kotlin/toggles.android.library-conventions.gradle.kts index 9a2b6651..050ac20b 100644 --- a/build-logic/conventions/src/main/kotlin/toggles.android.library-conventions.gradle.kts +++ b/build-logic/conventions/src/main/kotlin/toggles.android.library-conventions.gradle.kts @@ -1,10 +1,8 @@ - plugins { id("com.android.library") kotlin("android") id("io.gitlab.arturbosch.detekt") id("toggles.detekt-library-conventions") - } android { diff --git a/build-logic/conventions/src/main/kotlin/toggles.android.module-conventions.gradle.kts b/build-logic/conventions/src/main/kotlin/toggles.android.module-conventions.gradle.kts index 7287ad55..f289258a 100644 --- a/build-logic/conventions/src/main/kotlin/toggles.android.module-conventions.gradle.kts +++ b/build-logic/conventions/src/main/kotlin/toggles.android.module-conventions.gradle.kts @@ -25,6 +25,50 @@ android { warningsAsErrors = true lintConfig = File("../../lint.xml") } + + testOptions { + animationsDisabled = true + execution = "ANDROIDX_TEST_ORCHESTRATOR" + unitTests { + isReturnDefaultValues = true + isIncludeAndroidResources = true + } + managedDevices { + devices { + maybeCreate("pixel2api30").apply { + // Use device profiles you typically see in Android Studio. + device = "Pixel 2" + // Use only API levels 30 and higher. + apiLevel = 30 + // To include Google services, use "google". + systemImageSource = "aosp" + } + } + } + } + + sourceSets { +// val sharedTestDir = "src/sharedTest/" +// if(project.file(sharedTestDir).exists()) { +// val sharedTestSourceDir = sharedTestDir + "java" +// val sharedTestResourceDir = sharedTestDir + "resources" +// val sharedIsAndroid = +// providers.gradleProperty("shared-tests-are-android-tests").get().toBoolean() +// if (sharedIsAndroid) { +// logger.lifecycle("Shared tests are androidTests") +// named("androidTest") { +// java.srcDir(sharedTestSourceDir) +// resources.srcDir(sharedTestResourceDir) +// } +// } else { +// logger.lifecycle("Shared tests are unitTests") +// named("test") { +// java.srcDir(sharedTestSourceDir) +// resources.srcDir(sharedTestResourceDir) +// } +// } +// } + } } kotlin { @@ -35,4 +79,13 @@ kotlin { dependencies { lintChecks(libs.com.slack.lint.compose.compose.lint.checks) + + testImplementation(libs.androidx.test.ext.junit) + testImplementation(libs.org.robolectric) + testImplementation(libs.androidx.test.runner) + + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.androidx.test.runner) + + androidTestUtil(libs.androidx.test.orchestrator) } \ No newline at end of file diff --git a/build-logic/conventions/src/main/kotlin/toggles.detekt-conventions.gradle.kts b/build-logic/conventions/src/main/kotlin/toggles.detekt-conventions.gradle.kts index 5671e3d9..234d4095 100644 --- a/build-logic/conventions/src/main/kotlin/toggles.detekt-conventions.gradle.kts +++ b/build-logic/conventions/src/main/kotlin/toggles.detekt-conventions.gradle.kts @@ -6,7 +6,7 @@ plugins { } dependencies { - detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.22.0") + detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.6") } detekt { diff --git a/build.gradle.kts b/build.gradle.kts index a027d101..6d7ace91 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,6 +18,14 @@ buildscript { } } +versionCatalogUpdate { + keep { + keepUnusedVersions = true + keepUnusedLibraries = true + keepUnusedPlugins = true + } +} + plugins { alias(libs.plugins.com.github.ben.manes.versions) alias(libs.plugins.nl.littlerobots.version.catalog.update) @@ -28,6 +36,13 @@ plugins { alias(libs.plugins.com.autonomousapps.dependency.analysis) } +develocity { + buildScan { + termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use") + termsOfUseAgree.set("yes") + } +} + fun isNonStable(version: String): Boolean { val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase(Locale.getDefault()).contains(it) diff --git a/gradle.properties b/gradle.properties index 0227d7ee..ebd41097 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2g -Xms500m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx12120m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit @@ -42,3 +42,5 @@ android.enableJetifier=false org.gradle.caching=true org.gradle.configuration-cache=true org.gradle.configuration-cache.max-problems=5 + +shared-tests-are-android-tests=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9c9b4041..bf92c3e9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,105 +1,68 @@ [versions] -agp = "8.2.1" -androidx-activity = "1.7.1" -androidx-appcompat = "1.6.1" +agp = "8.6.0" +androidx-activity = "1.9.2" +androidx-appcompat = "1.7.0" androidx-arch-core = "2.2.0" -androidx-collection = "1.1.0" -androidx-compose-bom = "2023.10.01" -androidx-compose-compiler = "1.5.8" -androidx-core = "1.10.0" -androidx-hilt = "1.0.0" -androidx-lifecycle = "2.6.1" -androidx-navigation = "2.7.0" -androidx-room = "2.6.0-alpha03" -androidx-savedstate = "1.2.1" -androidx-test = "1.5.0" -androidx-vectordrawable = "1.1.0" -com-google-dagger = "2.48" -com-squareup-leakcanary = "2.8.1" -com-squareup-moshi = "1.15.0" -dokka = "1.9.0" -espresso-core = "3.5.1" +androidx-compose-bom = "2024.09.00" +androidx-hilt = "1.2.0" +androidx-lifecycle = "2.8.5" +androidx-navigation = "2.8.0" +androidx-room = "2.6.1" +androidx-test = "1.6.1" +com-google-dagger = "2.52" +com-squareup-leakcanary = "2.14" +com-squareup-moshi = "1.15.1" +dokka = "1.9.20" +espresso-core = "3.6.1" +io-gitlab-arturbosch-detekt = "1.23.6" junit = "4.13.2" -kotlin = "1.9.22" -org-jetbrains-kotlinx = "1.6.4" +kotlin = "2.0.20" +ksp = "2.0.20-1.0.25" +orchestrator = "1.5.0" +org-jetbrains-kotlinx = "1.8.1" +se-eelde-toggles = "0.0.1" [libraries] -androidx-activity = { module = "androidx.activity:activity", version.ref = "androidx-activity" } androidx-activity-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } -androidx-activity-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" } androidx-annotation = "androidx.annotation:annotation:1.6.0" androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } -androidx-arch-core-core-common = { module = "androidx.arch.core:core-common", version.ref = "androidx-arch-core" } -androidx-arch-core-core-runtime = { module = "androidx.arch.core:core-runtime", version.ref = "androidx-arch-core" } androidx-arch-core-core-testing = { module = "androidx.arch.core:core-testing", version.ref = "androidx-arch-core" } -androidx-collection = { module = "androidx.collection:collection", version.ref = "androidx-collection" } -androidx-collection-collection-ktx = { module = "androidx.collection:collection-ktx", version.ref = "androidx-collection" } androidx-compose-animation = { module = "androidx.compose.animation:animation" } -androidx-compose-animation-animation-core = { module = "androidx.compose.animation:animation-core" } androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "androidx-compose-bom" } androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" } androidx-compose-foundation-foundation-layout = { module = "androidx.compose.foundation:foundation-layout" } -androidx-compose-material-material-icons-core = { module = "androidx.compose.material:material-icons-core" } androidx-compose-material-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" } -androidx-compose-material-material-ripple = { module = "androidx.compose.material:material-ripple" } androidx-compose-material3 = { module = "androidx.compose.material3:material3" } androidx-compose-runtime = { module = "androidx.compose.runtime:runtime" } androidx-compose-ui = { module = "androidx.compose.ui:ui" } -androidx-compose-ui-ui-geometry = { module = "androidx.compose.ui:ui-geometry" } -androidx-compose-ui-ui-graphics = { module = "androidx.compose.ui:ui-graphics" } androidx-compose-ui-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4" } androidx-compose-ui-ui-text = { module = "androidx.compose.ui:ui-text" } androidx-compose-ui-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } -androidx-compose-ui-ui-tooling-data = { module = "androidx.compose.ui:ui-tooling-data" } androidx-compose-ui-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } -androidx-compose-ui-ui-unit = { module = "androidx.compose.ui:ui-unit" } -androidx-compose-ui-ui-util = { module = "androidx.compose.ui:ui-util" } -androidx-concurrent-concurrent-futures = "androidx.concurrent:concurrent-futures:1.1.0" -androidx-core = { module = "androidx.core:core", version.ref = "androidx-core" } -androidx-core-core-ktx = "androidx.core:core-ktx:1.9.0" -androidx-cursoradapter = "androidx.cursoradapter:cursoradapter:1.0.0" -androidx-customview-customview-poolingcontainer = "androidx.customview:customview-poolingcontainer:1.0.0" -androidx-fragment = "androidx.fragment:fragment:1.5.1" +androidx-core-core-ktx = "androidx.core:core-ktx:1.13.1" androidx-hilt-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "androidx-hilt" } -androidx-hilt-hilt-navigation = { module = "androidx.hilt:hilt-navigation", version.ref = "androidx-hilt" } androidx-hilt-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidx-hilt" } -androidx-interpolator = "androidx.interpolator:interpolator:1.0.0" -androidx-lifecycle-lifecycle-common = { module = "androidx.lifecycle:lifecycle-common", version.ref = "androidx-lifecycle" } androidx-lifecycle-lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "androidx-lifecycle" } -androidx-lifecycle-lifecycle-runtime-compose = "androidx.lifecycle:lifecycle-runtime-compose:2.6.1" -androidx-lifecycle-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime", version.ref = "androidx-lifecycle" } +androidx-lifecycle-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } androidx-lifecycle-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" } -androidx-lifecycle-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "androidx-lifecycle" } androidx-lifecycle-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" } androidx-lifecycle-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" } -androidx-lifecycle-lifecycle-viewmodel-savedstate = { module = "androidx.lifecycle:lifecycle-viewmodel-savedstate", version.ref = "androidx-lifecycle" } -androidx-loader = "androidx.loader:loader:1.0.0" -androidx-navigation-navigation-common = { module = "androidx.navigation:navigation-common", version.ref = "androidx-navigation" } -androidx-navigation-navigation-common-ktx = { module = "androidx.navigation:navigation-common-ktx", version.ref = "androidx-navigation" } androidx-navigation-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation" } -androidx-navigation-navigation-runtime = { module = "androidx.navigation:navigation-runtime", version.ref = "androidx-navigation" } androidx-navigation-navigation-runtime-ktx = { module = "androidx.navigation:navigation-runtime-ktx", version.ref = "androidx-navigation" } androidx-room-room-compiler = { module = "androidx.room:room-compiler", version.ref = "androidx-room" } androidx-room-room-ktx = { module = "androidx.room:room-ktx", version.ref = "androidx-room" } androidx-room-room-runtime = { module = "androidx.room:room-runtime", version.ref = "androidx-room" } androidx-room-room-testing = { module = "androidx.room:room-testing", version.ref = "androidx-room" } -androidx-savedstate = { module = "androidx.savedstate:savedstate", version.ref = "androidx-savedstate" } androidx-startup-startup-runtime = "androidx.startup:startup-runtime:1.1.1" -androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" } androidx-test-core-ktx = { module = "androidx.test:core-ktx", version.ref = "androidx-test" } androidx-test-espresso-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso-core" } -androidx-test-ext-junit = "androidx.test.ext:junit:1.1.5" -androidx-test-ext-truth = "androidx.test.ext:truth:1.5.0" +androidx-test-ext-junit = "androidx.test.ext:junit:1.2.1" +androidx-test-ext-truth = "androidx.test.ext:truth:1.6.0" +androidx-test-orchestrator = { module = "androidx.test:orchestrator", version.ref = "orchestrator" } androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test" } -androidx-test-runner = "androidx.test:runner:1.5.2" -androidx-tracing = "androidx.tracing:tracing:1.0.0" -androidx-vectordrawable = { module = "androidx.vectordrawable:vectordrawable", version.ref = "androidx-vectordrawable" } -androidx-vectordrawable-vectordrawable-animated = { module = "androidx.vectordrawable:vectordrawable-animated", version.ref = "androidx-vectordrawable" } -androidx-versionedparcelable = "androidx.versionedparcelable:versionedparcelable:1.1.1" -androidx-viewpager = "androidx.viewpager:viewpager:1.0.0" -app-cash-licensee-licensee-gradle-plugin = "app.cash.licensee:licensee-gradle-plugin:1.8.0" -com-android-tools-build-gradle = "com.android.tools.build:gradle:8.2.1" -com-google-code-findbugs-jsr305 = "com.google.code.findbugs:jsr305:3.0.2" +androidx-test-runner = "androidx.test:runner:1.6.2" +app-cash-licensee-licensee-gradle-plugin = "app.cash.licensee:licensee-gradle-plugin:1.11.0" +com-android-tools-build-gradle = "com.android.tools.build:gradle:8.6.0" com-google-dagger = { module = "com.google.dagger:dagger", version.ref = "com-google-dagger" } com-google-dagger-dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "com-google-dagger" } com-google-dagger-hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "com-google-dagger" } @@ -107,44 +70,45 @@ com-google-dagger-hilt-android-compiler = { module = "com.google.dagger:hilt-and com-google-dagger-hilt-android-gradle-plugin = { module = "com.google.dagger:hilt-android-gradle-plugin", version.ref = "com-google-dagger" } com-google-dagger-hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "com-google-dagger" } com-google-dagger-hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "com-google-dagger" } -com-google-firebase-firebase-crashlytics-gradle = "com.google.firebase:firebase-crashlytics-gradle:2.9.8" -com-google-gms-google-services = "com.google.gms:google-services:4.3.15" -com-slack-lint-compose-compose-lint-checks = "com.slack.lint.compose:compose-lint-checks:1.2.0" +com-google-devtools-ksp-com-google-devtools-ksp-gradle-plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } +com-google-gms-google-services = "com.google.gms:google-services:4.4.2" +com-slack-lint-compose-compose-lint-checks = "com.slack.lint.compose:compose-lint-checks:1.3.1" +com-squareup-kotlinpoet = "com.squareup:kotlinpoet:1.16.0" com-squareup-leakcanary-leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "com-squareup-leakcanary" } com-squareup-moshi = { module = "com.squareup.moshi:moshi", version.ref = "com-squareup-moshi" } com-squareup-moshi-moshi-kotlin-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "com-squareup-moshi" } -com-squareup-okio = "com.squareup.okio:okio:3.5.0" -espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso-core" } -io-gitlab-arturbosch-detekt-detekt-formatting = "io.gitlab.arturbosch.detekt:detekt-formatting:1.23.1" -io-gitlab-arturbosch-detekt-detekt-gradle-plugin = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.1" -io-gitlab-arturbosch-detekt-detekt-rules-libraries = "io.gitlab.arturbosch.detekt:detekt-rules-libraries:1.23.1" +com-squareup-okio = "com.squareup.okio:okio:3.9.0" +io-gitlab-arturbosch-detekt-detekt-formatting = "io.gitlab.arturbosch.detekt:detekt-formatting:1.23.7" +io-gitlab-arturbosch-detekt-detekt-gradle-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "io-gitlab-arturbosch-detekt" } +io-gitlab-arturbosch-detekt-detekt-rules-libraries = { module = "io.gitlab.arturbosch.detekt:detekt-rules-libraries", version.ref = "io-gitlab-arturbosch-detekt" } junit = { module = "junit:junit", version.ref = "junit" } org-jetbrains-dokka-dokka-gradle-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } +org-jetbrains-kotlin-compose-compiler-gradle-plugin = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" } org-jetbrains-kotlin-kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } -org-jetbrains-kotlin-kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } -org-jetbrains-kotlin-kotlin-stdlib-jdk8 = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0" -org-jetbrains-kotlinx-kotlinx-collections-immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5" +org-jetbrains-kotlinx-kotlinx-collections-immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8" org-jetbrains-kotlinx-kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" } org-jetbrains-kotlinx-kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "org-jetbrains-kotlinx" } org-jetbrains-kotlinx-kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test" } -org-robolectric = "org.robolectric:robolectric:4.10.3" +org-robolectric = "org.robolectric:robolectric:4.13" app-cash-turbine = "app.cash.turbine:turbine:1.0.0" -se-eelde-toggles-toggles-core = { module = "se.eelde.toggles:toggles-core" } -se-eelde-toggles-toggles-flow = { module = "se.eelde.toggles:toggles-flow" } -se-eelde-toggles-toggles-prefs = { module = "se.eelde.toggles:toggles-prefs" } -#se-eelde-toggles-toggles-core = "se.eelde.toggles:toggles-core:0.0.2" -#se-eelde-toggles-toggles-flow = "se.eelde.toggles:toggles-flow:0.0.1" -#se-eelde-toggles-toggles-prefs = "se.eelde.toggles:toggles-prefs:0.0.1" +#se-eelde-toggles-toggles-core = { module = "se.eelde.toggles:toggles-core" } +#se-eelde-toggles-toggles-flow = { module = "se.eelde.toggles:toggles-flow" } +#se-eelde-toggles-toggles-prefs = { module = "se.eelde.toggles:toggles-prefs" } +se-eelde-toggles-toggles-core = "se.eelde.toggles:toggles-core:0.0.2" +se-eelde-toggles-toggles-flow = { module = "se.eelde.toggles:toggles-flow", version.ref = "se-eelde-toggles" } +se-eelde-toggles-toggles-prefs = { module = "se.eelde.toggles:toggles-prefs", version.ref = "se-eelde-toggles" } +se-premex-ownership-gradle-plugin = "se.premex:ownership-gradle-plugin:0.0.11" + [plugins] com-android-library = { id = "com.android.library", version.ref = "agp" } -com-autonomousapps-dependency-analysis = "com.autonomousapps.dependency-analysis:1.28.0" -com-github-ben-manes-versions = "com.github.ben-manes.versions:0.50.0" -com-github-triplet-play = "com.github.triplet.play:3.8.4" +com-autonomousapps-dependency-analysis = "com.autonomousapps.dependency-analysis:2.0.1" +com-github-ben-manes-versions = "com.github.ben-manes.versions:0.51.0" +com-github-triplet-play = "com.github.triplet.play:3.11.0" com-google-dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "com-google-dagger" } -com-google-devtools-ksp = "com.google.devtools.ksp:1.9.22-1.0.16" +com-google-devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } com-google-gms-google-services = "com.google.gms.google-services:4.3.15" com-vanniktech-maven-publish = "com.vanniktech.maven.publish:0.27.0" -nl-littlerobots-version-catalog-update = "nl.littlerobots.version-catalog-update:0.8.3" +nl-littlerobots-version-catalog-update = "nl.littlerobots.version-catalog-update:0.8.4" org-jetbrains-dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } org-jetbrains-kotlinx-binary-compatibility-validator = "org.jetbrains.kotlinx.binary-compatibility-validator:0.13.2" -se-premex-gross = "se.premex.gross:0.2.0" +se-premex-gross = "se.premex.gross:0.4.2" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e093..9355b415 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/modules/applications/build.gradle.kts b/modules/applications/build.gradle.kts index 583950ac..4e8de7c3 100644 --- a/modules/applications/build.gradle.kts +++ b/modules/applications/build.gradle.kts @@ -2,16 +2,11 @@ plugins { id("toggles.android.module-conventions") id("toggles.ownership-conventions") alias(libs.plugins.com.google.devtools.ksp) + id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "se.eelde.toggles.applications" - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } } dependencies { diff --git a/modules/applications/src/sharedTest/java/se/eelde/toggles/applications/ExampleSharedTest.kt b/modules/applications/src/sharedTest/java/se/eelde/toggles/applications/ExampleSharedTest.kt new file mode 100644 index 00000000..027c9e74 --- /dev/null +++ b/modules/applications/src/sharedTest/java/se/eelde/toggles/applications/ExampleSharedTest.kt @@ -0,0 +1,26 @@ +package se.eelde.toggles.applications + +import android.app.Application +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * ./gradlew :modules:applications:pixel2api30DebugAndroidTest -Pshared-tests-are-android-tests=false + * ./gradlew :modules:applications:pixel2api30DebugAndroidTest -Pshared-tests-are-android-tests=true + */ +@RunWith(AndroidJUnit4::class) +class ExampleSharedTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } + + @Test + fun useAppContext() { + val context = ApplicationProvider.getApplicationContext() + assertEquals("se.eelde.toggles.applications.test", context.packageName) + } +} diff --git a/modules/applications/src/sharedTest/resources/robolectric.properties b/modules/applications/src/sharedTest/resources/robolectric.properties new file mode 100644 index 00000000..37420983 --- /dev/null +++ b/modules/applications/src/sharedTest/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=33 \ No newline at end of file diff --git a/modules/booleanconfiguration/build.gradle.kts b/modules/booleanconfiguration/build.gradle.kts index 72d059a6..f77927d4 100644 --- a/modules/booleanconfiguration/build.gradle.kts +++ b/modules/booleanconfiguration/build.gradle.kts @@ -2,16 +2,11 @@ plugins { id("toggles.android.module-conventions") id("toggles.ownership-conventions") alias(libs.plugins.com.google.devtools.ksp) + id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "se.eelde.toggles.booleanconfiguration" - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } } dependencies { diff --git a/modules/compose-theme/build.gradle.kts b/modules/compose-theme/build.gradle.kts index 60e589b2..ea537764 100644 --- a/modules/compose-theme/build.gradle.kts +++ b/modules/compose-theme/build.gradle.kts @@ -1,16 +1,11 @@ plugins { id("toggles.android.module-conventions") id("toggles.ownership-conventions") + id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "se.eelde.toggles.composetheme" - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } } dependencies { implementation(platform(libs.androidx.compose.bom)) diff --git a/modules/configurations/build.gradle.kts b/modules/configurations/build.gradle.kts index a9541d7f..d56019e5 100644 --- a/modules/configurations/build.gradle.kts +++ b/modules/configurations/build.gradle.kts @@ -2,16 +2,11 @@ plugins { id("toggles.android.module-conventions") id("toggles.ownership-conventions") alias(libs.plugins.com.google.devtools.ksp) + id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "se.eelde.toggles.configurations" - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } } dependencies { diff --git a/modules/enumconfiguration/build.gradle.kts b/modules/enumconfiguration/build.gradle.kts index 531d1e92..815e0ebe 100644 --- a/modules/enumconfiguration/build.gradle.kts +++ b/modules/enumconfiguration/build.gradle.kts @@ -2,16 +2,11 @@ plugins { id("toggles.android.module-conventions") id("toggles.ownership-conventions") alias(libs.plugins.com.google.devtools.ksp) + id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "se.eelde.toggles.enumconfiguration" - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } } dependencies { diff --git a/modules/help/build.gradle.kts b/modules/help/build.gradle.kts index 62ab0678..59e09102 100644 --- a/modules/help/build.gradle.kts +++ b/modules/help/build.gradle.kts @@ -2,16 +2,11 @@ plugins { id("toggles.android.module-conventions") id("toggles.ownership-conventions") alias(libs.plugins.com.google.devtools.ksp) + id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "se.eelde.toggles.help" - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } } dependencies { diff --git a/modules/integerconfiguration/build.gradle.kts b/modules/integerconfiguration/build.gradle.kts index 1adf999a..68723e79 100644 --- a/modules/integerconfiguration/build.gradle.kts +++ b/modules/integerconfiguration/build.gradle.kts @@ -2,16 +2,11 @@ plugins { id("toggles.android.module-conventions") id("toggles.ownership-conventions") alias(libs.plugins.com.google.devtools.ksp) + id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "se.eelde.toggles.integerconfiguration" - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } } dependencies { diff --git a/modules/oss/build.gradle.kts b/modules/oss/build.gradle.kts index d5af9f62..b362e668 100644 --- a/modules/oss/build.gradle.kts +++ b/modules/oss/build.gradle.kts @@ -2,16 +2,11 @@ plugins { id("toggles.android.module-conventions") id("toggles.ownership-conventions") alias(libs.plugins.com.google.devtools.ksp) + id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "se.eelde.toggles.oss" - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } } dependencies { diff --git a/modules/provider/build.gradle.kts b/modules/provider/build.gradle.kts index daf67d98..d3b8946c 100644 --- a/modules/provider/build.gradle.kts +++ b/modules/provider/build.gradle.kts @@ -2,17 +2,11 @@ plugins { id("toggles.android.module-conventions") id("toggles.ownership-conventions") alias(libs.plugins.com.google.devtools.ksp) + id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "se.eelde.toggles.provider" - buildFeatures { - - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } } dependencies { diff --git a/modules/stringconfiguration/build.gradle.kts b/modules/stringconfiguration/build.gradle.kts index 4aa9d1d3..107e0dc3 100644 --- a/modules/stringconfiguration/build.gradle.kts +++ b/modules/stringconfiguration/build.gradle.kts @@ -2,16 +2,11 @@ plugins { id("toggles.android.module-conventions") id("toggles.ownership-conventions") alias(libs.plugins.com.google.devtools.ksp) + id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "se.eelde.toggles.stringconfiguration" - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } } dependencies { diff --git a/scripts/pr_check.sh b/scripts/pr_check.sh new file mode 100755 index 00000000..d2dd13e5 --- /dev/null +++ b/scripts/pr_check.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +cd "$SCRIPT_DIR"/.. || exit + +./gradlew detekt +./gradlew test -Pshared-tests-are-android-tests=false +./gradlew test -Pshared-tests-are-android-tests=true +./gradlew assembleAndroidTest -Pshared-tests-are-android-tests=false +./gradlew assembleAndroidTest -Pshared-tests-are-android-tests=true +./gradlew check +./gradlew :toggles-core:check +./gradlew :toggles-flow:check --no-configuration-cache +./gradlew :toggles-flow-noop:check +./gradlew :toggles-prefs:check --no-configuration-cache +./gradlew :toggles-prefs-noop:check diff --git a/settings.gradle.kts b/settings.gradle.kts index d596e8c9..d0a5cbe4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,8 +10,15 @@ pluginManagement { } plugins { - id("com.gradle.enterprise") version "3.16.1" - id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0") + id("com.gradle.develocity") version "3.18" + id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") +} + +develocity { + buildScan { + termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use") + termsOfUseAgree.set("yes") + } } buildCache { @@ -25,7 +32,7 @@ buildCache { } } -private val localLibraries = true +private val localLibraries = false rootProject.name = "Toggles" includeBuild("build-logic/conventions") diff --git a/toggles-app/build.gradle.kts b/toggles-app/build.gradle.kts index 768e7e38..fd07caa1 100644 --- a/toggles-app/build.gradle.kts +++ b/toggles-app/build.gradle.kts @@ -96,6 +96,26 @@ android { versionNameSuffix = " debug" } } + testOptions { + animationsDisabled = true + execution = "ANDROIDX_TEST_ORCHESTRATOR" + unitTests { + isReturnDefaultValues = true + isIncludeAndroidResources = true + } + managedDevices { + devices { + maybeCreate("pixel2api30").apply { + // Use device profiles you typically see in Android Studio. + device = "Pixel 2" + // Use only API levels 30 and higher. + apiLevel = 30 + // To include Google services, use "google". + systemImageSource = "aosp" + } + } + } + } } dependencies { diff --git a/toggles-core/settings.gradle.kts b/toggles-core/settings.gradle.kts index af96acbd..f6ca5ae4 100644 --- a/toggles-core/settings.gradle.kts +++ b/toggles-core/settings.gradle.kts @@ -23,8 +23,15 @@ pluginManagement { } plugins { - id("com.gradle.enterprise") version "3.16.1" - id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0") + id("com.gradle.develocity") version "3.18" + id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") +} + +develocity { + buildScan { + termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use") + termsOfUseAgree.set("yes") + } } dependencyResolutionManagement { diff --git a/toggles-flow-noop/settings.gradle.kts b/toggles-flow-noop/settings.gradle.kts index 441fef75..a2a9fb80 100644 --- a/toggles-flow-noop/settings.gradle.kts +++ b/toggles-flow-noop/settings.gradle.kts @@ -23,8 +23,15 @@ pluginManagement { } plugins { - id("com.gradle.enterprise") version "3.16.1" - id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0") + id("com.gradle.develocity") version "3.18" + id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") +} + +develocity { + buildScan { + termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use") + termsOfUseAgree.set("yes") + } } dependencyResolutionManagement { diff --git a/toggles-flow/build.gradle.kts b/toggles-flow/build.gradle.kts index e16816f1..2fc396f7 100644 --- a/toggles-flow/build.gradle.kts +++ b/toggles-flow/build.gradle.kts @@ -13,7 +13,7 @@ android { dependencies { testImplementation(libs.junit) - testImplementation(libs.androidx.test.core) + testImplementation(libs.androidx.test.core.ktx) testImplementation(libs.androidx.test.ext.truth) testImplementation(libs.androidx.test.rules) testImplementation(libs.androidx.test.runner) diff --git a/toggles-flow/settings.gradle.kts b/toggles-flow/settings.gradle.kts index a10920c2..453ff5df 100644 --- a/toggles-flow/settings.gradle.kts +++ b/toggles-flow/settings.gradle.kts @@ -23,8 +23,15 @@ pluginManagement { } plugins { - id("com.gradle.enterprise") version "3.16.1" - id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0") + id("com.gradle.develocity") version "3.18" + id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") +} + +develocity { + buildScan { + termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use") + termsOfUseAgree.set("yes") + } } dependencyResolutionManagement { diff --git a/toggles-prefs-noop/settings.gradle.kts b/toggles-prefs-noop/settings.gradle.kts index a51331da..a68b295d 100644 --- a/toggles-prefs-noop/settings.gradle.kts +++ b/toggles-prefs-noop/settings.gradle.kts @@ -23,8 +23,15 @@ pluginManagement { } plugins { - id("com.gradle.enterprise") version "3.16.1" - id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0") + id("com.gradle.develocity") version "3.18" + id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") +} + +develocity { + buildScan { + termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use") + termsOfUseAgree.set("yes") + } } dependencyResolutionManagement { diff --git a/toggles-prefs/build.gradle.kts b/toggles-prefs/build.gradle.kts index 6dc28f59..0ccf4a23 100644 --- a/toggles-prefs/build.gradle.kts +++ b/toggles-prefs/build.gradle.kts @@ -13,7 +13,7 @@ android { dependencies { testImplementation(libs.junit) - testImplementation(libs.androidx.test.core) + testImplementation(libs.androidx.test.core.ktx) testImplementation(libs.androidx.test.ext.truth) testImplementation(libs.androidx.test.rules) testImplementation(libs.androidx.test.runner) diff --git a/toggles-prefs/settings.gradle.kts b/toggles-prefs/settings.gradle.kts index fdd6a980..a577266e 100644 --- a/toggles-prefs/settings.gradle.kts +++ b/toggles-prefs/settings.gradle.kts @@ -23,8 +23,15 @@ pluginManagement { } plugins { - id("com.gradle.enterprise") version "3.16.1" - id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0") + id("com.gradle.develocity") version "3.18" + id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") +} + +develocity { + buildScan { + termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use") + termsOfUseAgree.set("yes") + } } dependencyResolutionManagement { diff --git a/toggles-sample/src/sharedTest/java/se/eelde/toggles/applications/ExampleSharedTest.kt b/toggles-sample/src/sharedTest/java/se/eelde/toggles/applications/ExampleSharedTest.kt new file mode 100644 index 00000000..a833cc28 --- /dev/null +++ b/toggles-sample/src/sharedTest/java/se/eelde/toggles/applications/ExampleSharedTest.kt @@ -0,0 +1,29 @@ +package se.eelde.toggles.applications + +import android.app.Application +import android.content.pm.ApplicationInfo +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + + +@RunWith(AndroidJUnit4::class) +class ExampleSharedTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } + + @Test + fun useAppContext() { + val context = ApplicationProvider.getApplicationContext() + + assertTrue( + "Package name missmatch ${context.packageName}", + context.packageName.startsWith("se.eelde.toggles.example") + ) + } +} diff --git a/toggles-sample/src/sharedTest/resources/robolectric.properties b/toggles-sample/src/sharedTest/resources/robolectric.properties new file mode 100644 index 00000000..37420983 --- /dev/null +++ b/toggles-sample/src/sharedTest/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=33 \ No newline at end of file From 8bdf574632b22b1b60fd11e1692f5e9f8b8b2eb4 Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Sat, 14 Sep 2024 11:54:29 +0200 Subject: [PATCH 03/24] Small iteration --- gradle/libs.versions.toml | 12 ++++++------ .../se/eelde/toggles/provider/TogglesProvider.kt | 2 +- scripts/pr_check.sh | 1 + .../TogglesProviderMatcherConfigurationTest.kt | 1 - 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bf92c3e9..c802bb1c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ agp = "8.6.0" androidx-activity = "1.9.2" androidx-appcompat = "1.7.0" androidx-arch-core = "2.2.0" -androidx-compose-bom = "2024.09.00" +androidx-compose-bom = "2024.09.01" androidx-hilt = "1.2.0" androidx-lifecycle = "2.8.5" androidx-navigation = "2.8.0" @@ -62,6 +62,7 @@ androidx-test-orchestrator = { module = "androidx.test:orchestrator", version.re androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test" } androidx-test-runner = "androidx.test:runner:1.6.2" app-cash-licensee-licensee-gradle-plugin = "app.cash.licensee:licensee-gradle-plugin:1.11.0" +app-cash-turbine = "app.cash.turbine:turbine:1.1.0" com-android-tools-build-gradle = "com.android.tools.build:gradle:8.6.0" com-google-dagger = { module = "com.google.dagger:dagger", version.ref = "com-google-dagger" } com-google-dagger-dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "com-google-dagger" } @@ -77,7 +78,7 @@ com-squareup-kotlinpoet = "com.squareup:kotlinpoet:1.16.0" com-squareup-leakcanary-leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "com-squareup-leakcanary" } com-squareup-moshi = { module = "com.squareup.moshi:moshi", version.ref = "com-squareup-moshi" } com-squareup-moshi-moshi-kotlin-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "com-squareup-moshi" } -com-squareup-okio = "com.squareup.okio:okio:3.9.0" +com-squareup-okio = "com.squareup.okio:okio:3.9.1" io-gitlab-arturbosch-detekt-detekt-formatting = "io.gitlab.arturbosch.detekt:detekt-formatting:1.23.7" io-gitlab-arturbosch-detekt-detekt-gradle-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "io-gitlab-arturbosch-detekt" } io-gitlab-arturbosch-detekt-detekt-rules-libraries = { module = "io.gitlab.arturbosch.detekt:detekt-rules-libraries", version.ref = "io-gitlab-arturbosch-detekt" } @@ -90,7 +91,6 @@ org-jetbrains-kotlinx-kotlinx-coroutines-android = { module = "org.jetbrains.kot org-jetbrains-kotlinx-kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "org-jetbrains-kotlinx" } org-jetbrains-kotlinx-kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test" } org-robolectric = "org.robolectric:robolectric:4.13" -app-cash-turbine = "app.cash.turbine:turbine:1.0.0" #se-eelde-toggles-toggles-core = { module = "se.eelde.toggles:toggles-core" } #se-eelde-toggles-toggles-flow = { module = "se.eelde.toggles:toggles-flow" } #se-eelde-toggles-toggles-prefs = { module = "se.eelde.toggles:toggles-prefs" } @@ -101,14 +101,14 @@ se-premex-ownership-gradle-plugin = "se.premex:ownership-gradle-plugin:0.0.11" [plugins] com-android-library = { id = "com.android.library", version.ref = "agp" } -com-autonomousapps-dependency-analysis = "com.autonomousapps.dependency-analysis:2.0.1" +com-autonomousapps-dependency-analysis = "com.autonomousapps.dependency-analysis:2.0.2" com-github-ben-manes-versions = "com.github.ben-manes.versions:0.51.0" com-github-triplet-play = "com.github.triplet.play:3.11.0" com-google-dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "com-google-dagger" } com-google-devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } -com-google-gms-google-services = "com.google.gms.google-services:4.3.15" +com-google-gms-google-services = "com.google.gms.google-services:4.4.2" com-vanniktech-maven-publish = "com.vanniktech.maven.publish:0.27.0" nl-littlerobots-version-catalog-update = "nl.littlerobots.version-catalog-update:0.8.4" org-jetbrains-dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } -org-jetbrains-kotlinx-binary-compatibility-validator = "org.jetbrains.kotlinx.binary-compatibility-validator:0.13.2" +org-jetbrains-kotlinx-binary-compatibility-validator = "org.jetbrains.kotlinx.binary-compatibility-validator:0.16.3" se-premex-gross = "se.premex.gross:0.4.2" diff --git a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt index 76682ded..f4ee9dfc 100644 --- a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt +++ b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt @@ -236,7 +236,7 @@ class TogglesProvider : ContentProvider() { } UriMatch.CONFIGURATIONS -> { - + TODO("Not yet implemented") } else -> { diff --git a/scripts/pr_check.sh b/scripts/pr_check.sh index d2dd13e5..735c3753 100755 --- a/scripts/pr_check.sh +++ b/scripts/pr_check.sh @@ -5,6 +5,7 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) cd "$SCRIPT_DIR"/.. || exit ./gradlew detekt +./gradlew versionCatalogFormat ./gradlew test -Pshared-tests-are-android-tests=false ./gradlew test -Pshared-tests-are-android-tests=true ./gradlew assembleAndroidTest -Pshared-tests-are-android-tests=false diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt index 847ca130..30aedbd3 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt @@ -26,7 +26,6 @@ import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config import se.eelde.toggles.BuildConfig import se.eelde.toggles.R -import se.eelde.toggles.core.Toggle import se.eelde.toggles.core.TogglesProviderContract import se.eelde.toggles.database.WrenchDatabase import se.eelde.toggles.di.DatabaseModule From 4cf8abc2431a0423b63b603feeb3ee7facd7deea Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Sat, 14 Sep 2024 14:04:46 +0200 Subject: [PATCH 04/24] Some happy case tests in place --- .../database/WrenchConfigurationDao.kt | 77 +++--- .../database/tables/ConfigurationTable.kt | 4 +- .../tables/ConfigurationValueTable.kt | 8 +- .../eelde/toggles/provider/TogglesProvider.kt | 49 +++- ...gglesProviderMatcherConfigurationIdTest.kt | 62 ++++- ...glesProviderMatcherConfigurationKeyTest.kt | 5 +- ...TogglesProviderMatcherConfigurationTest.kt | 43 +++- .../java/se/eelde/toggles/core/ColumnNames.kt | 39 +++ .../java/se/eelde/toggles/core/CursorExt.kt | 17 ++ .../main/java/se/eelde/toggles/core/Toggle.kt | 119 +++++++++ .../java/se/eelde/toggles/core/ToggleScope.kt | 9 + .../java/se/eelde/toggles/core/ToggleValue.kt | 69 ++++++ .../toggles/core/TogglesConfiguration.kt | 95 +++++++ .../toggles/core/TogglesConfigurationValue.kt | 116 +++++++++ .../toggles/core/TogglesProviderContract.kt | 231 +----------------- 15 files changed, 660 insertions(+), 283 deletions(-) create mode 100644 toggles-core/src/main/java/se/eelde/toggles/core/ColumnNames.kt create mode 100644 toggles-core/src/main/java/se/eelde/toggles/core/CursorExt.kt create mode 100644 toggles-core/src/main/java/se/eelde/toggles/core/Toggle.kt create mode 100644 toggles-core/src/main/java/se/eelde/toggles/core/ToggleScope.kt create mode 100644 toggles-core/src/main/java/se/eelde/toggles/core/ToggleValue.kt create mode 100644 toggles-core/src/main/java/se/eelde/toggles/core/TogglesConfiguration.kt create mode 100644 toggles-core/src/main/java/se/eelde/toggles/core/TogglesConfigurationValue.kt diff --git a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt index 0c5430c3..b929f6b8 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt @@ -19,63 +19,63 @@ interface WrenchConfigurationDao { @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " WHERE configuration.id = (:configurationId) AND configurationValue.scope = (:scopeId)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " WHERE configuration.id = (:configurationId) AND configurationValue.scope = (:scopeId)" ) fun getToggle(configurationId: Long, scopeId: Long): Cursor @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope = (:scopeId)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope = (:scopeId)" ) fun getToggle(configurationKey: String, scopeId: Long): Cursor @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value," + - " scope.name as scope" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + - " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope IN (:scopeId)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value," + + " scope.name as scope" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + + " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope IN (:scopeId)" ) fun getToggles(configurationKey: String, scopeId: List): Cursor @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value," + - " scope.name as scope" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + - " WHERE configuration.configurationKey = (:configurationKey)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value," + + " scope.name as scope" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + + " WHERE configuration.configurationKey = (:configurationKey)" ) fun getToggles(configurationKey: String): Cursor @Query( "SELECT * " + - " FROM " + ConfigurationTable.TABLE_NAME + - " WHERE configuration.applicationId = (:applicationId) AND configuration.configurationKey = (:configurationKey)" + " FROM " + ConfigurationTable.TABLE_NAME + + " WHERE configuration.applicationId = (:applicationId) AND configuration.configurationKey = (:configurationKey)" ) fun getWrenchConfiguration(applicationId: Long, configurationKey: String): WrenchConfiguration? @Query( "SELECT * " + - " FROM " + ConfigurationTable.TABLE_NAME + - " WHERE configuration.applicationId = (:applicationId) AND configuration.configurationKey = (:configurationKey)" + " FROM " + ConfigurationTable.TABLE_NAME + + " WHERE configuration.applicationId = (:applicationId) AND configuration.configurationKey = (:configurationKey)" ) fun getWrenchConfigurationByKey( applicationId: Long, @@ -84,14 +84,17 @@ interface WrenchConfigurationDao { @Query( "SELECT * " + - " FROM " + ConfigurationTable.TABLE_NAME + - " WHERE configuration.applicationId = (:applicationId) AND configuration.id = (:configurationId)" + " FROM " + ConfigurationTable.TABLE_NAME + + " WHERE configuration.applicationId = (:applicationId) AND configuration.id = (:configurationId)" ) fun getWrenchConfigurationById(applicationId: Long, configurationId: Long): WrenchConfiguration? @Query("SELECT * FROM configuration WHERE id = :configurationId") fun getConfiguration(configurationId: Long): Flow + @Query("SELECT * FROM configuration WHERE id = :configurationId") + fun getConfigurationCursor(configurationId: Long): Cursor + @Transaction @Query( "SELECT id, applicationId, configurationKey, configurationType FROM configuration WHERE applicationId = :applicationId ORDER BY lastUse DESC" @@ -107,9 +110,15 @@ interface WrenchConfigurationDao { query: String ): Flow> + @Query("DELETE FROM configuration WHERE applicationId = :callingApplication AND id = :id") + fun deleteConfiguration(callingApplication: Long, id: Long): Int + @Insert fun insert(wrenchConfiguration: WrenchConfiguration): Long @Query("UPDATE configuration set lastUse=:date WHERE id= :configurationId") suspend fun touch(configurationId: Long, date: Date) + + @Query("UPDATE configuration SET configurationKey = :key, configurationType = :type WHERE applicationId = :callingApplication AND id= :id") + fun updateConfiguration(callingApplication: Long, id: Long, key: String, type: String): Int } diff --git a/modules/database/src/main/java/se/eelde/toggles/database/tables/ConfigurationTable.kt b/modules/database/src/main/java/se/eelde/toggles/database/tables/ConfigurationTable.kt index 8d2e7aab..e7783d83 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/tables/ConfigurationTable.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/tables/ConfigurationTable.kt @@ -7,7 +7,7 @@ interface ConfigurationTable { const val TABLE_NAME = "configuration" const val COL_ID = ColumnNames.Toggle.COL_ID const val COL_APP_ID = "applicationId" - const val COL_KEY = ColumnNames.Toggle.COL_KEY - const val COL_TYPE = ColumnNames.Toggle.COL_TYPE + const val COL_KEY = ColumnNames.Configuration.COL_KEY + const val COL_TYPE = ColumnNames.Configuration.COL_TYPE } } diff --git a/modules/database/src/main/java/se/eelde/toggles/database/tables/ConfigurationValueTable.kt b/modules/database/src/main/java/se/eelde/toggles/database/tables/ConfigurationValueTable.kt index 08887832..d13a1363 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/tables/ConfigurationValueTable.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/tables/ConfigurationValueTable.kt @@ -5,9 +5,9 @@ import se.eelde.toggles.core.ColumnNames interface ConfigurationValueTable { companion object { const val TABLE_NAME = "configurationValue" - const val COL_ID = ColumnNames.Toggle.COL_ID - const val COL_CONFIG_ID = "configurationId" - const val COL_VALUE = ColumnNames.Toggle.COL_VALUE - const val COL_SCOPE = "scope" + const val COL_ID = ColumnNames.ConfigurationValue.COL_ID + const val COL_CONFIG_ID = ColumnNames.ConfigurationValue.COL_CONFIG_ID + const val COL_VALUE = ColumnNames.ConfigurationValue.COL_VALUE + const val COL_SCOPE = ColumnNames.ConfigurationValue.COL_SCOPE } } diff --git a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt index f4ee9dfc..d723b612 100644 --- a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt +++ b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt @@ -12,7 +12,9 @@ import dagger.hilt.EntryPoint import dagger.hilt.InstallIn import dagger.hilt.android.EntryPointAccessors import dagger.hilt.components.SingletonComponent +import se.eelde.toggles.core.ColumnNames import se.eelde.toggles.core.Toggle +import se.eelde.toggles.core.TogglesConfiguration import se.eelde.toggles.database.WrenchApplication import se.eelde.toggles.database.WrenchApplicationDao import se.eelde.toggles.database.WrenchConfiguration @@ -161,6 +163,10 @@ class TogglesProvider : ContentProvider() { cursor = configurationDao.getToggles(uri.lastPathSegment!!) } + UriMatch.CONFIGURATION_ID -> { + cursor = configurationDao.getConfigurationCursor(uri.lastPathSegment!!.toLong()) + } + else -> { throw UnsupportedOperationException("Not yet implemented $uri") } @@ -236,7 +242,14 @@ class TogglesProvider : ContentProvider() { } UriMatch.CONFIGURATIONS -> { - TODO("Not yet implemented") + val togglesConfiguration = TogglesConfiguration.fromContentValues(values!!) + val wrenchConfiguration = WrenchConfiguration( + id = togglesConfiguration.id, + applicationId = callingApplication.id, + key = togglesConfiguration.key, + type = togglesConfiguration.type + ) + insertId = configurationDao.insert(wrenchConfiguration) } else -> { @@ -292,6 +305,16 @@ class TogglesProvider : ContentProvider() { } } + UriMatch.CONFIGURATION_ID -> { + val fromContentValues = TogglesConfiguration.fromContentValues(values!!) + updatedRows = configurationDao.updateConfiguration( + callingApplication = callingApplication.id, + id = uri.lastPathSegment!!.toLong(), + key = fromContentValues.key, + type = fromContentValues.type + ) + } + else -> { throw UnsupportedOperationException("Not yet implemented $uri") } @@ -311,7 +334,23 @@ class TogglesProvider : ContentProvider() { assertValidApiVersion(togglesPreferences, uri) } - throw UnsupportedOperationException("Not yet implemented") + when (togglesUriMatcher.match(uri)) { + UriMatch.CONFIGURATION_ID -> { + val deletedRows = + configurationDao.deleteConfiguration( + callingApplication.id, + uri.lastPathSegment!!.toLong() + ) + if (deletedRows > 0) { + context!!.contentResolver.notifyChange(uri, null) + } + return deletedRows + } + + else -> { + throw UnsupportedOperationException("Not yet implemented $uri") + } + } } override fun getType(uri: Uri): String { @@ -335,10 +374,10 @@ class TogglesProvider : ContentProvider() { "vnd.android.cursor.dir/vnd.${context!!.packageName}.configuration" UriMatch.CONFIGURATION_ID -> - "vnd.android.cursor.dir/vnd.${context!!.packageName}.configuration" + "vnd.android.cursor.item/vnd.${context!!.packageName}.configuration" UriMatch.CONFIGURATION_KEY -> - "vnd.android.cursor.dir/vnd.${context!!.packageName}.configuration" + "vnd.android.cursor.item/vnd.${context!!.packageName}.configuration" UriMatch.PREDEFINED_CONFIGURATION_VALUES -> "vnd.android.cursor.dir/vnd.${context!!.packageName}.predefinedConfigurationValue" @@ -422,7 +461,7 @@ class TogglesProvider : ContentProvider() { if (strictApiVersion) { throw IllegalArgumentException( "This content provider requires you to provide a " + - "valid api-version in a queryParameter" + "valid api-version in a queryParameter" ) } } diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationIdTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationIdTest.kt index 74b9561a..e61e507a 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationIdTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationIdTest.kt @@ -17,6 +17,7 @@ import dagger.hilt.android.testing.HiltTestApplication import dagger.hilt.android.testing.UninstallModules import dagger.hilt.components.SingletonComponent import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test @@ -26,6 +27,8 @@ import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config import se.eelde.toggles.BuildConfig import se.eelde.toggles.R +import se.eelde.toggles.core.Toggle +import se.eelde.toggles.core.TogglesConfiguration import se.eelde.toggles.core.TogglesProviderContract import se.eelde.toggles.database.WrenchDatabase import se.eelde.toggles.di.DatabaseModule @@ -53,6 +56,11 @@ class TogglesProviderMatcherConfigurationIdTest { } } + val togglesConfiguration = TogglesConfiguration { + type = Toggle.TYPE.BOOLEAN + key = "myConfigurationkey" + } + @Inject lateinit var wrenchDatabase: WrenchDatabase @@ -79,23 +87,67 @@ class TogglesProviderMatcherConfigurationIdTest { assertEquals("vnd.android.cursor.item/vnd.se.eelde.toggles.configuration", type) } - @Test + @Test(expected = UnsupportedOperationException::class) fun testInsert() { - TODO("To be implemented") + togglesProvider.insert( + TogglesProviderContract.configurationUri(0), + null + ) } @Test fun testUpdate() { - TODO("To be implemented") + val uri = togglesProvider.insert( + TogglesProviderContract.configurationUri(), + togglesConfiguration.toContentValues(), + ) + + val updatedConfiguration = togglesConfiguration.copy(key = "newKey", type = Toggle.TYPE.STRING) + + val rowsUpdated = togglesProvider.update( + TogglesProviderContract.configurationUri(uri.lastPathSegment!!), + updatedConfiguration.toContentValues(), + null, + null + ) + + val query = togglesProvider.query(uri, null, null, null, null) + assertTrue(query.moveToFirst()) + val fromCursor = TogglesConfiguration.fromCursor(query) + + assertEquals(1, rowsUpdated) + assertEquals(1, fromCursor.id) } @Test fun testQuery() { - TODO("To be implemented") + val uri = togglesProvider.insert( + TogglesProviderContract.configurationUri(), + togglesConfiguration.toContentValues(), + ) + + val configurationUri = TogglesProviderContract.configurationUri(uri.lastPathSegment!!) + + val cursor = togglesProvider.query(configurationUri, null, null, null, null) + assertTrue(cursor.moveToFirst()) + TogglesConfiguration.fromCursor(cursor).also { cursorConfiguration -> + assertEquals(togglesConfiguration.key, cursorConfiguration.key) + assertEquals(togglesConfiguration.type, cursorConfiguration.type) + } } @Test fun testDelete() { - TODO("To be implemented") + val uri = togglesProvider.insert( + TogglesProviderContract.configurationUri(), + togglesConfiguration.toContentValues(), + ) + + val rowsDeleted = togglesProvider.delete( + TogglesProviderContract.configurationUri(uri.lastPathSegment!!), + null, + null + ) + assertEquals(1, rowsDeleted) } } diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt index 1a8d9dbf..057849ee 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt @@ -96,6 +96,9 @@ class TogglesProviderMatcherConfigurationKeyTest { @Test fun testDelete() { - TODO("To be implemented") + togglesProvider.delete( + TogglesProviderContract.configurationUri("key"), + null + ) } } diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt index 30aedbd3..8ad6ed6d 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt @@ -26,6 +26,8 @@ import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config import se.eelde.toggles.BuildConfig import se.eelde.toggles.R +import se.eelde.toggles.core.Toggle +import se.eelde.toggles.core.TogglesConfiguration import se.eelde.toggles.core.TogglesProviderContract import se.eelde.toggles.database.WrenchDatabase import se.eelde.toggles.di.DatabaseModule @@ -79,18 +81,49 @@ class TogglesProviderMatcherConfigurationTest { assertEquals("vnd.android.cursor.dir/vnd.se.eelde.toggles.configuration", type) } - @Test + @Test(expected = UnsupportedOperationException::class) fun testUpdate() { - TODO("To be implemented") + togglesProvider.update( + TogglesProviderContract.configurationUri(), + null, null, null + ) } @Test + fun testInsert() { + val togglesConfiguration = TogglesConfiguration { + type = Toggle.TYPE.BOOLEAN + key = "myConfigurationkey" + } + + val uri = togglesProvider.insert( + TogglesProviderContract.configurationUri(), + togglesConfiguration.toContentValues(), + ) + + assertEquals( + "content://se.eelde.toggles.configprovider/configuration/1?API_VERSION=1", + uri.toString() + ) + } + + @Test(expected = UnsupportedOperationException::class) fun testQuery() { - TODO("To be implemented") + togglesProvider.query( + TogglesProviderContract.configurationUri(), + null, + null, + null, + null + ) } - @Test + @Test(expected = UnsupportedOperationException::class) fun testDelete() { - TODO("To be implemented") + togglesProvider.delete( + TogglesProviderContract.configurationUri(), + null, + null, + ) } } diff --git a/toggles-core/src/main/java/se/eelde/toggles/core/ColumnNames.kt b/toggles-core/src/main/java/se/eelde/toggles/core/ColumnNames.kt new file mode 100644 index 00000000..72a5fb29 --- /dev/null +++ b/toggles-core/src/main/java/se/eelde/toggles/core/ColumnNames.kt @@ -0,0 +1,39 @@ +package se.eelde.toggles.core + +@Suppress("LibraryEntitiesShouldNotBePublic") +public object ColumnNames { + public object Toggle { + public const val COL_KEY: String = Configuration.COL_KEY + public const val COL_ID: String = Configuration.COL_ID + public const val COL_VALUE: String = ConfigurationValue.COL_VALUE + public const val COL_TYPE: String = Configuration.COL_TYPE + } + + public object ToggleValue { + public const val COL_ID: String = ConfigurationValue.COL_ID + public const val COL_VALUE: String = ConfigurationValue.COL_VALUE + public const val COL_CONFIG_ID: String = ConfigurationValue.COL_CONFIG_ID + } + + public object ToggleScope { + public const val COL_ID: String = "id" + public const val COL_APP_ID: String = "applicationId" + public const val COL_NAME: String = "name" + public const val COL_SELECTED_TIMESTAMP: String = "selectedTimestamp" + + public const val DEFAULT_SCOPE: String = "wrench_default" + } + + public object Configuration { + public const val COL_ID: String = "id" + public const val COL_KEY: String = "configurationKey" + public const val COL_TYPE: String = "configurationType" + } + + public object ConfigurationValue { + public const val COL_ID: String = "id" + public const val COL_CONFIG_ID: String = "configurationId" + public const val COL_VALUE: String = "value" + public const val COL_SCOPE: String = "scope" + } +} \ No newline at end of file diff --git a/toggles-core/src/main/java/se/eelde/toggles/core/CursorExt.kt b/toggles-core/src/main/java/se/eelde/toggles/core/CursorExt.kt new file mode 100644 index 00000000..dce54d4e --- /dev/null +++ b/toggles-core/src/main/java/se/eelde/toggles/core/CursorExt.kt @@ -0,0 +1,17 @@ +package se.eelde.toggles.core + +import android.database.Cursor + +internal fun Cursor.getStringOrThrow(columnName: String): String = getStringOrNull(columnName)!! + +internal fun Cursor.getStringOrNull(columnName: String): String? { + val index = getColumnIndexOrThrow(columnName) + return if (isNull(index)) null else getString(index) +} + +internal fun Cursor.getLongOrThrow(columnName: String): Long = getLongOrNull(columnName)!! + +internal fun Cursor.getLongOrNull(columnName: String): Long? { + val index = getColumnIndexOrThrow(columnName) + return if (isNull(index)) null else getLong(index) +} \ No newline at end of file diff --git a/toggles-core/src/main/java/se/eelde/toggles/core/Toggle.kt b/toggles-core/src/main/java/se/eelde/toggles/core/Toggle.kt new file mode 100644 index 00000000..20963e07 --- /dev/null +++ b/toggles-core/src/main/java/se/eelde/toggles/core/Toggle.kt @@ -0,0 +1,119 @@ +package se.eelde.toggles.core + +import android.content.ContentValues +import android.database.Cursor +import androidx.annotation.StringDef + +@Suppress("LibraryEntitiesShouldNotBePublic") +public class Toggle private constructor( + public var id: Long = 0, + @ToggleType public val type: String, + public val key: String = "", + public val value: String? = null, + public val scope: String? = null, +) { + public class Builder { + @set:JvmSynthetic + public var id: Long = 0 + + @set:JvmSynthetic + @ToggleType + public var type: String = "" + + @set:JvmSynthetic + public var key: String = "" + + @set:JvmSynthetic + public var value: String? = null + + public fun setId(id: Long): Builder = apply { this.id = id } + public fun setType(@ToggleType type: String): Builder = + apply { this.type = type } + + public fun setKey(key: String): Builder = apply { this.key = key } + public fun setValue(value: String?): Builder = apply { this.value = value } + + public fun build(): Toggle = + Toggle(id = id, type = type, key = key, value = value) + } + + public fun copy( + id: Long = this.id, + type: String = this.type, + key: String = this.key, + value: String? = this.value + ): Toggle = + Toggle(id = id, type = type, key = key, value = value) + + public fun toContentValues(): ContentValues = ContentValues().apply { + put(ColumnNames.Toggle.COL_ID, id) + put(ColumnNames.Toggle.COL_KEY, key) + put(ColumnNames.Toggle.COL_VALUE, value) + put(ColumnNames.Toggle.COL_TYPE, type) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Toggle + + if (id != other.id) return false + if (type != other.type) return false + if (key != other.key) return false + if (value != other.value) return false + + return true + } + + override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + type.hashCode() + result = 31 * result + key.hashCode() + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + + override fun toString(): String { + return "Toggle(id=$id, type='$type', key='$key', value=$value)" + } + + @StringDef(TYPE.BOOLEAN, TYPE.STRING, TYPE.INTEGER, TYPE.ENUM) + @Retention(AnnotationRetention.SOURCE) + public annotation class ToggleType + + public object TYPE { + public const val BOOLEAN: String = "boolean" + public const val STRING: String = "string" + public const val INTEGER: String = "integer" + public const val ENUM: String = "enum" + } + + public companion object { + @JvmStatic + public fun fromContentValues(values: ContentValues): Toggle { + return Toggle( + id = values.getAsLong(ColumnNames.Toggle.COL_ID) ?: 0, + type = values.getAsString(ColumnNames.Toggle.COL_TYPE), + key = values.getAsString(ColumnNames.Toggle.COL_KEY), + value = values.getAsString(ColumnNames.Toggle.COL_VALUE) + ) + } + + @JvmStatic + public fun fromCursor(cursor: Cursor): Toggle { + return Toggle( + id = cursor.getLongOrThrow(ColumnNames.Toggle.COL_ID), + type = cursor.getStringOrThrow(ColumnNames.Toggle.COL_TYPE), + key = cursor.getStringOrThrow(ColumnNames.Toggle.COL_KEY), + value = cursor.getStringOrNull(ColumnNames.Toggle.COL_VALUE), + ) + } + } +} + +@JvmSynthetic +@Suppress("LibraryEntitiesShouldNotBePublic") +public fun Toggle(initializer: Toggle.Builder.() -> Unit): Toggle { + return Toggle.Builder().apply(initializer).build() +} diff --git a/toggles-core/src/main/java/se/eelde/toggles/core/ToggleScope.kt b/toggles-core/src/main/java/se/eelde/toggles/core/ToggleScope.kt new file mode 100644 index 00000000..2b6ab0e0 --- /dev/null +++ b/toggles-core/src/main/java/se/eelde/toggles/core/ToggleScope.kt @@ -0,0 +1,9 @@ +package se.eelde.toggles.core + +import java.util.Date + +public class ToggleScope private constructor( + public val id: Long = 0, + public val name: String, + public val timeStamp: Date, +) \ No newline at end of file diff --git a/toggles-core/src/main/java/se/eelde/toggles/core/ToggleValue.kt b/toggles-core/src/main/java/se/eelde/toggles/core/ToggleValue.kt new file mode 100644 index 00000000..47f971a8 --- /dev/null +++ b/toggles-core/src/main/java/se/eelde/toggles/core/ToggleValue.kt @@ -0,0 +1,69 @@ +package se.eelde.toggles.core + +import android.content.ContentValues + +@Suppress("LibraryEntitiesShouldNotBePublic") +public class ToggleValue private constructor( + public val id: Long = 0, + public val configurationId: Long = 0, + public val value: String? = null +) { + + public class Builder { + @set:JvmSynthetic + public var id: Long = 0 + + @set:JvmSynthetic + public var configurationId: Long = 0 + + @set:JvmSynthetic + public var value: String? = null + + public fun setId(id: Long): Builder = apply { this.id = id } + public fun setConfigurationId(configurationId: Long): Builder = + apply { this.configurationId = configurationId } + + public fun setValue(value: String?): Builder = apply { this.value = value } + + public fun build(): ToggleValue = + ToggleValue(id = id, configurationId = configurationId, value = value) + } + + public fun toContentValues(): ContentValues = ContentValues().apply { + if (id > 0) { + put(ColumnNames.ToggleValue.COL_ID, id) + } + put(ColumnNames.ToggleValue.COL_CONFIG_ID, configurationId) + put(ColumnNames.ToggleValue.COL_VALUE, value) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ToggleValue + + if (id != other.id) return false + if (configurationId != other.configurationId) return false + if (value != other.value) return false + + return true + } + + override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + configurationId.hashCode() + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + + override fun toString(): String { + return "ToggleValue(id=$id, configurationId=$configurationId, value=$value)" + } +} + +@JvmSynthetic +@Suppress("LibraryEntitiesShouldNotBePublic") +public fun ToggleValue(initializer: ToggleValue.Builder.() -> Unit): ToggleValue { + return ToggleValue.Builder().apply(initializer).build() +} \ No newline at end of file diff --git a/toggles-core/src/main/java/se/eelde/toggles/core/TogglesConfiguration.kt b/toggles-core/src/main/java/se/eelde/toggles/core/TogglesConfiguration.kt new file mode 100644 index 00000000..fcaae810 --- /dev/null +++ b/toggles-core/src/main/java/se/eelde/toggles/core/TogglesConfiguration.kt @@ -0,0 +1,95 @@ +package se.eelde.toggles.core + +import android.content.ContentValues +import android.database.Cursor + +@Suppress("LibraryEntitiesShouldNotBePublic") +public class TogglesConfiguration private constructor( + public var id: Long = 0, + @Toggle.ToggleType public val type: String, + public val key: String = "", +) { + public class Builder { + @set:JvmSynthetic + public var id: Long = 0 + + @set:JvmSynthetic + @Toggle.ToggleType + public var type: String = "" + + @set:JvmSynthetic + public var key: String = "" + + public fun setId(id: Long): Builder = apply { this.id = id } + public fun setType(@Toggle.ToggleType type: String): Builder = + apply { this.type = type } + + public fun setKey(key: String): Builder = apply { this.key = key } + + public fun build(): TogglesConfiguration = + TogglesConfiguration(id = id, type = type, key = key) + } + + public fun copy( + id: Long = this.id, + type: String = this.type, + key: String = this.key, + ): TogglesConfiguration = + TogglesConfiguration(id = id, type = type, key = key) + + public fun toContentValues(): ContentValues = ContentValues().apply { + put(ColumnNames.Configuration.COL_ID, id) + put(ColumnNames.Configuration.COL_KEY, key) + put(ColumnNames.Configuration.COL_TYPE, type) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as TogglesConfiguration + + if (id != other.id) return false + if (type != other.type) return false + if (key != other.key) return false + + return true + } + + override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + type.hashCode() + result = 31 * result + key.hashCode() + return result + } + + override fun toString(): String { + return "TogglesConfiguration(id=$id, type='$type', key='$key')" + } + + public companion object { + @JvmStatic + public fun fromContentValues(values: ContentValues): TogglesConfiguration { + return TogglesConfiguration( + id = values.getAsLong(ColumnNames.Configuration.COL_ID) ?: 0, + type = values.getAsString(ColumnNames.Configuration.COL_TYPE), + key = values.getAsString(ColumnNames.Configuration.COL_KEY), + ) + } + + @JvmStatic + public fun fromCursor(cursor: Cursor): TogglesConfiguration { + return TogglesConfiguration( + id = cursor.getLongOrThrow(ColumnNames.Configuration.COL_ID), + type = cursor.getStringOrThrow(ColumnNames.Configuration.COL_TYPE), + key = cursor.getStringOrThrow(ColumnNames.Configuration.COL_KEY), + ) + } + } +} + +@JvmSynthetic +@Suppress("LibraryEntitiesShouldNotBePublic") +public fun TogglesConfiguration(initializer: TogglesConfiguration.Builder.() -> Unit): TogglesConfiguration { + return TogglesConfiguration.Builder().apply(initializer).build() +} \ No newline at end of file diff --git a/toggles-core/src/main/java/se/eelde/toggles/core/TogglesConfigurationValue.kt b/toggles-core/src/main/java/se/eelde/toggles/core/TogglesConfigurationValue.kt new file mode 100644 index 00000000..cc16aee6 --- /dev/null +++ b/toggles-core/src/main/java/se/eelde/toggles/core/TogglesConfigurationValue.kt @@ -0,0 +1,116 @@ +package se.eelde.toggles.core + +import android.content.ContentValues +import android.database.Cursor + +@Suppress("LibraryEntitiesShouldNotBePublic") +public class TogglesConfigurationValue private constructor( + public var id: Long = 0, + public val configurationId: Long, + public val value: String? = null, + public val scope: Long, +) { + public class Builder { + @set:JvmSynthetic + public var id: Long = 0 + + @set:JvmSynthetic + public var configurationId: Long = 0 + + @set:JvmSynthetic + public var value: String? = null + + @set:JvmSynthetic + public var scope: Long = 0 + + public fun setId(id: Long): Builder = apply { this.id = id } + public fun setConfigurationId(configurationId: Long): Builder = + apply { this.configurationId = configurationId } + + public fun setValue(value: String): Builder = apply { this.value = value } + public fun setScope(scope: Long): Builder = apply { this.scope = scope } + + public fun build(): TogglesConfigurationValue = + TogglesConfigurationValue( + id = id, + configurationId = configurationId, + value = value, + scope = scope + ) + } + + public fun copy( + id: Long = this.id, + configurationId: Long = this.configurationId, + value: String? = this.value, + scope: Long = this.scope + ): TogglesConfigurationValue = + TogglesConfigurationValue( + id = id, + configurationId = configurationId, + value = value, + scope = scope + ) + + public fun toContentValues(): ContentValues = ContentValues().apply { + put(ColumnNames.ConfigurationValue.COL_ID, id) + put(ColumnNames.ConfigurationValue.COL_CONFIG_ID, configurationId) + put(ColumnNames.ConfigurationValue.COL_VALUE, value) + put(ColumnNames.ConfigurationValue.COL_SCOPE, scope) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as TogglesConfigurationValue + + if (id != other.id) return false + if (configurationId != other.configurationId) return false + if (value != other.value) return false + if (scope != other.scope) return false + + return true + } + + override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + configurationId.hashCode() + result = 31 * result + (value?.hashCode() ?: 0) + result = 31 * result + scope.hashCode() + return result + } + + override fun toString(): String { + return "TogglesConfigurationValue(id=$id, type='$configurationId', value=$value, scope='$scope')" + } + + public companion object { + @JvmStatic + public fun fromContentValues(values: ContentValues): TogglesConfigurationValue { + return TogglesConfigurationValue( + id = values.getAsLong(ColumnNames.Toggle.COL_ID) ?: 0, + configurationId = values.getAsLong(ColumnNames.Toggle.COL_TYPE), + value = values.getAsString(ColumnNames.Toggle.COL_VALUE), + scope = values.getAsLong(ColumnNames.Toggle.COL_KEY), + ) + } + + @JvmStatic + public fun fromCursor(cursor: Cursor): TogglesConfigurationValue { + return TogglesConfigurationValue( + id = cursor.getLongOrThrow(ColumnNames.Toggle.COL_ID), + configurationId = cursor.getLongOrThrow(ColumnNames.Toggle.COL_TYPE), + value = cursor.getStringOrNull(ColumnNames.Toggle.COL_VALUE), + scope = cursor.getLongOrThrow(ColumnNames.Toggle.COL_KEY), + ) + } + } +} + +@JvmSynthetic +@Suppress("LibraryEntitiesShouldNotBePublic") +public fun TogglesConfigurationValue(initializer: TogglesConfigurationValue.Builder.() -> Unit): + TogglesConfigurationValue { + return TogglesConfigurationValue.Builder().apply(initializer).build() +} \ No newline at end of file diff --git a/toggles-core/src/main/java/se/eelde/toggles/core/TogglesProviderContract.kt b/toggles-core/src/main/java/se/eelde/toggles/core/TogglesProviderContract.kt index a8371229..eff0dae3 100644 --- a/toggles-core/src/main/java/se/eelde/toggles/core/TogglesProviderContract.kt +++ b/toggles-core/src/main/java/se/eelde/toggles/core/TogglesProviderContract.kt @@ -1,235 +1,9 @@ package se.eelde.toggles.core -import android.content.ContentValues -import android.database.Cursor import android.net.Uri -import androidx.annotation.StringDef import java.util.Date -@Suppress("LibraryEntitiesShouldNotBePublic") -public object ColumnNames { - public object Toggle { - public const val COL_KEY: String = "configurationKey" - public const val COL_ID: String = "id" - public const val COL_VALUE: String = "value" - public const val COL_TYPE: String = "configurationType" - } - - public object ToggleValue { - public const val COL_ID: String = "id" - public const val COL_VALUE: String = "value" - public const val COL_CONFIG_ID: String = "configurationId" - } - - public object ToggleScope { - public const val COL_ID: String = "id" - public const val COL_APP_ID: String = "applicationId" - public const val COL_NAME: String = "name" - public const val COL_SELECTED_TIMESTAMP: String = "selectedTimestamp" - - public const val DEFAULT_SCOPE: String = "wrench_default" - } -} - -@Suppress("LibraryEntitiesShouldNotBePublic") -public class ToggleValue private constructor( - public val id: Long = 0, - public val configurationId: Long = 0, - public val value: String? = null -) { - - public class Builder { - @set:JvmSynthetic - public var id: Long = 0 - - @set:JvmSynthetic - public var configurationId: Long = 0 - - @set:JvmSynthetic - public var value: String? = null - - public fun setId(id: Long): Builder = apply { this.id = id } - public fun setConfigurationId(configurationId: Long): Builder = - apply { this.configurationId = configurationId } - - public fun setValue(value: String?): Builder = apply { this.value = value } - - public fun build(): ToggleValue = - ToggleValue(id = id, configurationId = configurationId, value = value) - } - - public fun toContentValues(): ContentValues = ContentValues().apply { - if (id > 0) { - put(ColumnNames.ToggleValue.COL_ID, id) - } - put(ColumnNames.ToggleValue.COL_CONFIG_ID, configurationId) - put(ColumnNames.ToggleValue.COL_VALUE, value) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as ToggleValue - - if (id != other.id) return false - if (configurationId != other.configurationId) return false - if (value != other.value) return false - - return true - } - - override fun hashCode(): Int { - var result = id.hashCode() - result = 31 * result + configurationId.hashCode() - result = 31 * result + (value?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "ToggleValue(id=$id, configurationId=$configurationId, value=$value)" - } -} - -@JvmSynthetic -@Suppress("LibraryEntitiesShouldNotBePublic") -public fun ToggleValue(initializer: ToggleValue.Builder.() -> Unit): ToggleValue { - return ToggleValue.Builder().apply(initializer).build() -} - -public class ToggleScope private constructor( - public val id: Long = 0, - public val name: String, - public val timeStamp: Date, -) - -@Suppress("LibraryEntitiesShouldNotBePublic") -public class Toggle private constructor( - public var id: Long = 0, - @ToggleType public val type: String, - public val key: String = "", - public val value: String? = null, - public val scope: String? = null, -) { - public class Builder { - @set:JvmSynthetic - public var id: Long = 0 - - @set:JvmSynthetic - @ToggleType - public var type: String = "" - - @set:JvmSynthetic - public var key: String = "" - @set:JvmSynthetic - public var value: String? = null - - public fun setId(id: Long): Builder = apply { this.id = id } - public fun setType(@ToggleType type: String): Builder = - apply { this.type = type } - - public fun setKey(key: String): Builder = apply { this.key = key } - public fun setValue(value: String?): Builder = apply { this.value = value } - - public fun build(): Toggle = - Toggle(id = id, type = type, key = key, value = value) - } - - public fun copy( - id: Long = this.id, - type: String = this.type, - key: String = this.key, - value: String? = this.value - ): Toggle = - Toggle(id = id, type = type, key = key, value = value) - - public fun toContentValues(): ContentValues = ContentValues().apply { - put(ColumnNames.Toggle.COL_ID, id) - put(ColumnNames.Toggle.COL_KEY, key) - put(ColumnNames.Toggle.COL_VALUE, value) - put(ColumnNames.Toggle.COL_TYPE, type) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Toggle - - if (id != other.id) return false - if (type != other.type) return false - if (key != other.key) return false - if (value != other.value) return false - - return true - } - - override fun hashCode(): Int { - var result = id.hashCode() - result = 31 * result + type.hashCode() - result = 31 * result + key.hashCode() - result = 31 * result + (value?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "Toggle(id=$id, type='$type', key='$key', value=$value)" - } - - @StringDef(TYPE.BOOLEAN, TYPE.STRING, TYPE.INTEGER, TYPE.ENUM) - @Retention(AnnotationRetention.SOURCE) - public annotation class ToggleType - - public object TYPE { - public const val BOOLEAN: String = "boolean" - public const val STRING: String = "string" - public const val INTEGER: String = "integer" - public const val ENUM: String = "enum" - } - - public companion object { - @JvmStatic - public fun fromContentValues(values: ContentValues): Toggle { - return Toggle( - id = values.getAsLong(ColumnNames.Toggle.COL_ID) ?: 0, - type = values.getAsString(ColumnNames.Toggle.COL_TYPE), - key = values.getAsString(ColumnNames.Toggle.COL_KEY), - value = values.getAsString(ColumnNames.Toggle.COL_VALUE) - ) - } - - @JvmStatic - public fun fromCursor(cursor: Cursor): Toggle { - return Toggle( - id = cursor.getLongOrThrow(ColumnNames.Toggle.COL_ID), - type = cursor.getStringOrThrow(ColumnNames.Toggle.COL_TYPE), - key = cursor.getStringOrThrow(ColumnNames.Toggle.COL_KEY), - value = cursor.getStringOrNull(ColumnNames.Toggle.COL_VALUE), - ) - } - } -} - -@JvmSynthetic -@Suppress("LibraryEntitiesShouldNotBePublic") -public fun Toggle(initializer: Toggle.Builder.() -> Unit): Toggle { - return Toggle.Builder().apply(initializer).build() -} - -private fun Cursor.getStringOrThrow(columnName: String): String = getStringOrNull(columnName)!! - -private fun Cursor.getStringOrNull(columnName: String): String? { - val index = getColumnIndexOrThrow(columnName) - return if (isNull(index)) null else getString(index) -} - -private fun Cursor.getLongOrThrow(columnName: String): Long = getLongOrNull(columnName)!! - -private fun Cursor.getLongOrNull(columnName: String): Long? { - val index = getColumnIndexOrThrow(columnName) - return if (isNull(index)) null else getLong(index) -} public object TogglesProviderContract { private const val TOGGLES_AUTHORITY = "se.eelde.toggles.configprovider" @@ -237,7 +11,8 @@ public object TogglesProviderContract { private const val TOGGLES_API_VERSION = 1 private val configurationUri = Uri.parse("content://$TOGGLES_AUTHORITY/configuration") - private val currentConfigurationUri = Uri.parse("content://$TOGGLES_AUTHORITY/currentConfiguration") + private val currentConfigurationUri = + Uri.parse("content://$TOGGLES_AUTHORITY/currentConfiguration") private val configurationValueUri = Uri.parse("content://$TOGGLES_AUTHORITY/predefinedConfigurationValue") @@ -282,6 +57,7 @@ public object TogglesProviderContract { .appendQueryParameter(TOGGLES_API_VERSION_QUERY_PARAM, TOGGLES_API_VERSION.toString()) .build() } + @JvmStatic public fun configurationUri(key: String): Uri { return configurationUri @@ -290,6 +66,7 @@ public object TogglesProviderContract { .appendQueryParameter(TOGGLES_API_VERSION_QUERY_PARAM, TOGGLES_API_VERSION.toString()) .build() } + @JvmStatic public fun configurationUri(id: Long): Uri { return configurationUri From 83495f9606379e6ffb9169f8c38b335dcb3c34e3 Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Sat, 14 Sep 2024 14:18:07 +0200 Subject: [PATCH 05/24] Tests for configuration key methods in CP --- .../database/WrenchConfigurationDao.kt | 12 +++-- .../eelde/toggles/provider/TogglesProvider.kt | 15 +++++- ...glesProviderMatcherConfigurationKeyTest.kt | 49 ++++++++++++++++--- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt index b929f6b8..c5c9b830 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt @@ -92,9 +92,6 @@ interface WrenchConfigurationDao { @Query("SELECT * FROM configuration WHERE id = :configurationId") fun getConfiguration(configurationId: Long): Flow - @Query("SELECT * FROM configuration WHERE id = :configurationId") - fun getConfigurationCursor(configurationId: Long): Cursor - @Transaction @Query( "SELECT id, applicationId, configurationKey, configurationType FROM configuration WHERE applicationId = :applicationId ORDER BY lastUse DESC" @@ -113,6 +110,9 @@ interface WrenchConfigurationDao { @Query("DELETE FROM configuration WHERE applicationId = :callingApplication AND id = :id") fun deleteConfiguration(callingApplication: Long, id: Long): Int + @Query("DELETE FROM configuration WHERE applicationId = :callingApplication AND configurationKey = :configurationKey") + fun deleteConfiguration(callingApplication: Long, configurationKey: String): Int + @Insert fun insert(wrenchConfiguration: WrenchConfiguration): Long @@ -121,4 +121,10 @@ interface WrenchConfigurationDao { @Query("UPDATE configuration SET configurationKey = :key, configurationType = :type WHERE applicationId = :callingApplication AND id= :id") fun updateConfiguration(callingApplication: Long, id: Long, key: String, type: String): Int + + @Query("SELECT * FROM configuration WHERE id = :configurationId and applicationId = :callingApplication") + fun getConfigurationCursor(callingApplication: Long, configurationId: Long): Cursor + + @Query("SELECT * FROM configuration WHERE configurationKey = :configurationKey and applicationId = :callingApplication") + fun getConfigurationCursor(callingApplication: Long, configurationKey: String): Cursor } diff --git a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt index d723b612..83248d08 100644 --- a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt +++ b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt @@ -160,11 +160,11 @@ class TogglesProvider : ContentProvider() { } UriMatch.CONFIGURATION_KEY -> { - cursor = configurationDao.getToggles(uri.lastPathSegment!!) + cursor = configurationDao.getConfigurationCursor(callingApplication.id, uri.lastPathSegment!!) } UriMatch.CONFIGURATION_ID -> { - cursor = configurationDao.getConfigurationCursor(uri.lastPathSegment!!.toLong()) + cursor = configurationDao.getConfigurationCursor(callingApplication.id, uri.lastPathSegment!!.toLong()) } else -> { @@ -346,6 +346,17 @@ class TogglesProvider : ContentProvider() { } return deletedRows } + UriMatch.CONFIGURATION_KEY -> { + val deletedRows = + configurationDao.deleteConfiguration( + callingApplication.id, + uri.lastPathSegment!! + ) + if (deletedRows > 0) { + context!!.contentResolver.notifyChange(uri, null) + } + return deletedRows + } else -> { throw UnsupportedOperationException("Not yet implemented $uri") diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt index 057849ee..0a43324c 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt @@ -17,6 +17,7 @@ import dagger.hilt.android.testing.HiltTestApplication import dagger.hilt.android.testing.UninstallModules import dagger.hilt.components.SingletonComponent import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test @@ -26,6 +27,8 @@ import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config import se.eelde.toggles.BuildConfig import se.eelde.toggles.R +import se.eelde.toggles.core.Toggle +import se.eelde.toggles.core.TogglesConfiguration import se.eelde.toggles.core.TogglesProviderContract import se.eelde.toggles.database.WrenchDatabase import se.eelde.toggles.di.DatabaseModule @@ -53,6 +56,11 @@ class TogglesProviderMatcherConfigurationKeyTest { } } + val togglesConfiguration = TogglesConfiguration { + type = Toggle.TYPE.BOOLEAN + key = "myConfigurationkey" + } + @Inject lateinit var wrenchDatabase: WrenchDatabase @@ -79,26 +87,53 @@ class TogglesProviderMatcherConfigurationKeyTest { assertEquals("vnd.android.cursor.item/vnd.se.eelde.toggles.configuration", type) } - @Test + @Test(expected = UnsupportedOperationException::class) fun testInsert() { - TODO("To be implemented") + togglesProvider.insert( + TogglesProviderContract.configurationUri("key"), + null + ) } - @Test + @Test(expected = UnsupportedOperationException::class) fun testUpdate() { - TODO("To be implemented") + togglesProvider.update( + TogglesProviderContract.configurationUri("key"), + null, + null, + null + ) } @Test fun testQuery() { - TODO("To be implemented") + val uri = togglesProvider.insert( + TogglesProviderContract.configurationUri(), + togglesConfiguration.toContentValues(), + ) + + val configurationUri = TogglesProviderContract.configurationUri(togglesConfiguration.key) + + val cursor = togglesProvider.query(configurationUri, null, null, null, null) + assertTrue(cursor.moveToFirst()) + TogglesConfiguration.fromCursor(cursor).also { cursorConfiguration -> + assertEquals(togglesConfiguration.key, cursorConfiguration.key) + assertEquals(togglesConfiguration.type, cursorConfiguration.type) + } } @Test fun testDelete() { - togglesProvider.delete( - TogglesProviderContract.configurationUri("key"), + val uri = togglesProvider.insert( + TogglesProviderContract.configurationUri(), + togglesConfiguration.toContentValues(), + ) + + val rowsDeleted = togglesProvider.delete( + TogglesProviderContract.configurationUri(togglesConfiguration.key), + null, null ) + assertEquals(1, rowsDeleted) } } From c024eae671e212f10656fafb6e71d8f1347ecd1b Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Sat, 14 Sep 2024 14:18:47 +0200 Subject: [PATCH 06/24] Ignore .kotlin directory --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d276bb19..17eb19cd 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,6 @@ captures/ **/release/*.* -build-cache \ No newline at end of file +build-cache + +.kotlin From 6175520c5babad2ae8f7e75398080da721a80da4 Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Sat, 14 Sep 2024 14:36:30 +0200 Subject: [PATCH 07/24] Separate daos for provider and application because they differ so much in their need. --- .../applications/ApplicationViewModel.kt | 5 +- .../BooleanValueViewModel.kt | 8 +- .../configurations/ConfigurationViewModel.kt | 12 +- .../eelde/toggles/database/WrenchDatabase.kt | 25 +++- .../application/TogglesApplicationDao.kt} | 11 +- .../application/TogglesConfigurationDao.kt} | 6 +- .../TogglesConfigurationValueDao.kt} | 5 +- ...TogglesPredefinedConfigurationValueDao.kt} | 5 +- .../application/TogglesScopeDao.kt} | 5 +- .../dao/provider/ProviderApplicationDao.kt | 18 +++ .../dao/provider/ProviderConfigurationDao.kt | 97 +++++++++++++ .../provider/ProviderConfigurationValueDao.kt | 34 +++++ ...ProviderPredefinedConfigurationValueDao.kt | 25 ++++ .../database/dao/provider/ProviderScopeDao.kt | 35 +++++ .../enumconfiguration/EnumValueViewModel.kt | 12 +- .../IntegerValueViewModel.kt | 8 +- .../eelde/toggles/provider/TogglesProvider.kt | 58 ++++---- .../StringValueViewModel.kt | 8 +- .../java/se/eelde/toggles/di/DaoModule.kt | 30 ++++ .../se/eelde/toggles/di/DatabaseModule.kt | 23 ---- .../se/eelde/toggles/di/ProviderDaoModule.kt | 31 +++++ .../toggles/dialogs/scope/ScopeViewModel.kt | 4 +- ...oviderPredefinedConfigurationValuesTest.kt | 2 +- ...gglesProviderMatcherConfigurationIdTest.kt | 3 +- ...glesProviderMatcherConfigurationKeyTest.kt | 3 +- ...TogglesProviderMatcherConfigurationTest.kt | 3 +- ...esProviderMatcherConfigurationValueTest.kt | 130 ++++++++++++++++++ 27 files changed, 502 insertions(+), 104 deletions(-) rename modules/database/src/main/java/se/eelde/toggles/database/{WrenchApplicationDao.kt => dao/application/TogglesApplicationDao.kt} (64%) rename modules/database/src/main/java/se/eelde/toggles/database/{WrenchConfigurationDao.kt => dao/application/TogglesConfigurationDao.kt} (96%) rename modules/database/src/main/java/se/eelde/toggles/database/{WrenchConfigurationValueDao.kt => dao/application/TogglesConfigurationValueDao.kt} (92%) rename modules/database/src/main/java/se/eelde/toggles/database/{WrenchPredefinedConfigurationValueDao.kt => dao/application/TogglesPredefinedConfigurationValueDao.kt} (84%) rename modules/database/src/main/java/se/eelde/toggles/database/{WrenchScopeDao.kt => dao/application/TogglesScopeDao.kt} (93%) create mode 100644 modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderApplicationDao.kt create mode 100644 modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationDao.kt create mode 100644 modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationValueDao.kt create mode 100644 modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderPredefinedConfigurationValueDao.kt create mode 100644 modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderScopeDao.kt create mode 100644 toggles-app/src/main/java/se/eelde/toggles/di/DaoModule.kt create mode 100644 toggles-app/src/main/java/se/eelde/toggles/di/ProviderDaoModule.kt rename toggles-app/src/test/java/se/eelde/toggles/provider/{ => configuration}/TogglesProviderMatcherConfigurationIdTest.kt (98%) rename toggles-app/src/test/java/se/eelde/toggles/provider/{ => configuration}/TogglesProviderMatcherConfigurationKeyTest.kt (97%) rename toggles-app/src/test/java/se/eelde/toggles/provider/{ => configuration}/TogglesProviderMatcherConfigurationTest.kt (97%) create mode 100644 toggles-app/src/test/java/se/eelde/toggles/provider/configurationValue/TogglesProviderMatcherConfigurationValueTest.kt diff --git a/modules/applications/src/main/java/se/eelde/toggles/applications/ApplicationViewModel.kt b/modules/applications/src/main/java/se/eelde/toggles/applications/ApplicationViewModel.kt index 8d0df56b..86d1d606 100644 --- a/modules/applications/src/main/java/se/eelde/toggles/applications/ApplicationViewModel.kt +++ b/modules/applications/src/main/java/se/eelde/toggles/applications/ApplicationViewModel.kt @@ -5,10 +5,9 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import se.eelde.toggles.database.WrenchApplication -import se.eelde.toggles.database.WrenchApplicationDao +import se.eelde.toggles.database.dao.application.TogglesApplicationDao import javax.inject.Inject internal data class ViewState( @@ -22,7 +21,7 @@ internal sealed class PartialViewState { } @HiltViewModel -internal class ApplicationViewModel @Inject constructor(applicationDao: WrenchApplicationDao) : +internal class ApplicationViewModel @Inject constructor(applicationDao: TogglesApplicationDao) : ViewModel() { private val _state = MutableStateFlow(reduce(ViewState(), PartialViewState.Empty)) diff --git a/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt b/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt index b3ee2a74..ffe84b9d 100644 --- a/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt +++ b/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt @@ -12,9 +12,9 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import se.eelde.toggles.core.TogglesProviderContract -import se.eelde.toggles.database.WrenchConfigurationDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationValue -import se.eelde.toggles.database.WrenchConfigurationValueDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.provider.notifyUpdate import java.util.Date import javax.inject.Inject @@ -38,8 +38,8 @@ private sealed class PartialViewState { class FragmentBooleanValueViewModel @Inject internal constructor( savedStateHandle: SavedStateHandle, private val application: Application, - private val configurationDao: WrenchConfigurationDao, - private val configurationValueDao: WrenchConfigurationValueDao + private val configurationDao: TogglesConfigurationDao, + private val configurationValueDao: TogglesConfigurationValueDao ) : ViewModel() { private val _state = MutableStateFlow(reduce(ViewState(), PartialViewState.Empty)) diff --git a/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt b/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt index 97ca2f51..2e5fb5f7 100644 --- a/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt +++ b/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt @@ -18,11 +18,11 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import se.eelde.toggles.database.WrenchApplication -import se.eelde.toggles.database.WrenchApplicationDao -import se.eelde.toggles.database.WrenchConfigurationDao +import se.eelde.toggles.database.dao.application.TogglesApplicationDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationWithValues import se.eelde.toggles.database.WrenchScope -import se.eelde.toggles.database.WrenchScopeDao +import se.eelde.toggles.database.dao.application.TogglesScopeDao import javax.inject.Inject internal data class ViewState( @@ -46,9 +46,9 @@ internal sealed class PartialViewState { @Suppress("StaticFieldLeak") class ConfigurationViewModel @Inject internal constructor( @ApplicationContext private val context: Context, - private val applicationDao: WrenchApplicationDao, - configurationDao: WrenchConfigurationDao, - scopeDao: WrenchScopeDao, + private val applicationDao: TogglesApplicationDao, + configurationDao: TogglesConfigurationDao, + scopeDao: TogglesScopeDao, val savedStateHandle: SavedStateHandle, ) : ViewModel() { private val applicationId: Long = savedStateHandle.get("applicationId")!! diff --git a/modules/database/src/main/java/se/eelde/toggles/database/WrenchDatabase.kt b/modules/database/src/main/java/se/eelde/toggles/database/WrenchDatabase.kt index 450644ea..86eddf56 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/WrenchDatabase.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/WrenchDatabase.kt @@ -3,6 +3,16 @@ package se.eelde.toggles.database import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters +import se.eelde.toggles.database.dao.application.TogglesApplicationDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao +import se.eelde.toggles.database.dao.application.TogglesPredefinedConfigurationValueDao +import se.eelde.toggles.database.dao.application.TogglesScopeDao +import se.eelde.toggles.database.dao.provider.ProviderApplicationDao +import se.eelde.toggles.database.dao.provider.ProviderConfigurationDao +import se.eelde.toggles.database.dao.provider.ProviderConfigurationValueDao +import se.eelde.toggles.database.dao.provider.ProviderPredefinedConfigurationValueDao +import se.eelde.toggles.database.dao.provider.ProviderScopeDao @Database( entities = [ @@ -17,13 +27,18 @@ import androidx.room.TypeConverters @TypeConverters(RoomDateConverter::class) abstract class WrenchDatabase : RoomDatabase() { - abstract fun applicationDao(): WrenchApplicationDao + abstract fun togglesApplicationDao(): TogglesApplicationDao + abstract fun providerApplicationDao(): ProviderApplicationDao - abstract fun configurationDao(): WrenchConfigurationDao + abstract fun togglesConfigurationDao(): TogglesConfigurationDao + abstract fun providerConfigurationDao(): ProviderConfigurationDao - abstract fun configurationValueDao(): WrenchConfigurationValueDao + abstract fun togglesConfigurationValueDao(): TogglesConfigurationValueDao + abstract fun providerConfigurationValueDao(): ProviderConfigurationValueDao - abstract fun predefinedConfigurationValueDao(): WrenchPredefinedConfigurationValueDao + abstract fun togglesPredefinedConfigurationValueDao(): TogglesPredefinedConfigurationValueDao + abstract fun providerPredefinedConfigurationValueDao(): ProviderPredefinedConfigurationValueDao - abstract fun scopeDao(): WrenchScopeDao + abstract fun togglesScopeDao(): TogglesScopeDao + abstract fun providerScopeDao(): ProviderScopeDao } diff --git a/modules/database/src/main/java/se/eelde/toggles/database/WrenchApplicationDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesApplicationDao.kt similarity index 64% rename from modules/database/src/main/java/se/eelde/toggles/database/WrenchApplicationDao.kt rename to modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesApplicationDao.kt index 4a023250..f6a5a721 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/WrenchApplicationDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesApplicationDao.kt @@ -1,14 +1,15 @@ -package se.eelde.toggles.database +package se.eelde.toggles.database.dao.application import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.Query import kotlinx.coroutines.flow.Flow +import se.eelde.toggles.database.WrenchApplication import se.eelde.toggles.database.tables.ApplicationTable @Dao -interface WrenchApplicationDao { +interface TogglesApplicationDao { @Query("SELECT * FROM " + ApplicationTable.TABLE_NAME) fun getApplications(): Flow> @@ -16,12 +17,6 @@ interface WrenchApplicationDao { @Query("SELECT * FROM " + ApplicationTable.TABLE_NAME + " WHERE id = (:id)") suspend fun getApplication(id: Long): WrenchApplication? - @Query("SELECT * FROM " + ApplicationTable.TABLE_NAME + " WHERE packageName IN (:packageName)") - fun loadByPackageName(packageName: String): WrenchApplication? - - @Insert - fun insert(application: WrenchApplication): Long - @Delete suspend fun delete(application: WrenchApplication) } diff --git a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesConfigurationDao.kt similarity index 96% rename from modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt rename to modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesConfigurationDao.kt index c5c9b830..b293d264 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesConfigurationDao.kt @@ -1,6 +1,6 @@ @file:Suppress("MaxLineLength") -package se.eelde.toggles.database +package se.eelde.toggles.database.dao.application import android.database.Cursor import androidx.room.Dao @@ -8,6 +8,8 @@ import androidx.room.Insert import androidx.room.Query import androidx.room.Transaction import kotlinx.coroutines.flow.Flow +import se.eelde.toggles.database.WrenchConfiguration +import se.eelde.toggles.database.WrenchConfigurationWithValues import se.eelde.toggles.database.tables.ConfigurationTable import se.eelde.toggles.database.tables.ConfigurationValueTable import se.eelde.toggles.database.tables.ScopeTable @@ -15,7 +17,7 @@ import java.util.Date @Suppress("TooManyFunctions") @Dao -interface WrenchConfigurationDao { +interface TogglesConfigurationDao { @Query( "SELECT configuration.id, " + diff --git a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationValueDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesConfigurationValueDao.kt similarity index 92% rename from modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationValueDao.kt rename to modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesConfigurationValueDao.kt index 061c8652..802f00f2 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/WrenchConfigurationValueDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesConfigurationValueDao.kt @@ -1,6 +1,6 @@ @file:Suppress("MaxLineLength") -package se.eelde.toggles.database +package se.eelde.toggles.database.dao.application import androidx.room.Dao import androidx.room.Delete @@ -8,10 +8,11 @@ import androidx.room.Insert import androidx.room.Query import androidx.room.Update import kotlinx.coroutines.flow.Flow +import se.eelde.toggles.database.WrenchConfigurationValue import se.eelde.toggles.database.tables.ConfigurationValueTable @Dao -interface WrenchConfigurationValueDao { +interface TogglesConfigurationValueDao { @Query( "SELECT * FROM " + ConfigurationValueTable.TABLE_NAME + " WHERE " + ConfigurationValueTable.COL_CONFIG_ID + " = (:configurationId) AND " + ConfigurationValueTable.COL_SCOPE + " = (:scopeId)" diff --git a/modules/database/src/main/java/se/eelde/toggles/database/WrenchPredefinedConfigurationValueDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesPredefinedConfigurationValueDao.kt similarity index 84% rename from modules/database/src/main/java/se/eelde/toggles/database/WrenchPredefinedConfigurationValueDao.kt rename to modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesPredefinedConfigurationValueDao.kt index a476f1c9..545874e5 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/WrenchPredefinedConfigurationValueDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesPredefinedConfigurationValueDao.kt @@ -1,15 +1,16 @@ @file:Suppress("MaxLineLength") -package se.eelde.toggles.database +package se.eelde.toggles.database.dao.application import androidx.room.Dao import androidx.room.Insert import androidx.room.Query import kotlinx.coroutines.flow.Flow +import se.eelde.toggles.database.WrenchPredefinedConfigurationValue import se.eelde.toggles.database.tables.PredefinedConfigurationValueTable @Dao -interface WrenchPredefinedConfigurationValueDao { +interface TogglesPredefinedConfigurationValueDao { @Query( """SELECT * FROM ${PredefinedConfigurationValueTable.TABLE_NAME} WHERE ${PredefinedConfigurationValueTable.COL_CONFIG_ID} = (:configurationId)""" diff --git a/modules/database/src/main/java/se/eelde/toggles/database/WrenchScopeDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesScopeDao.kt similarity index 93% rename from modules/database/src/main/java/se/eelde/toggles/database/WrenchScopeDao.kt rename to modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesScopeDao.kt index a8a9a735..6a6fcd05 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/WrenchScopeDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesScopeDao.kt @@ -1,6 +1,6 @@ @file:Suppress("MaxLineLength") -package se.eelde.toggles.database +package se.eelde.toggles.database.dao.application import androidx.room.Dao import androidx.room.Delete @@ -8,10 +8,11 @@ import androidx.room.Insert import androidx.room.Query import androidx.room.Update import kotlinx.coroutines.flow.Flow +import se.eelde.toggles.database.WrenchScope import se.eelde.toggles.database.tables.ScopeTable @Dao -interface WrenchScopeDao { +interface TogglesScopeDao { @Query( "SELECT * FROM " + ScopeTable.TABLE_NAME + " WHERE " + ScopeTable.COL_APP_ID + " = (:applicationId) AND " + ScopeTable.COL_NAME + " != '" + WrenchScope.SCOPE_DEFAULT + "'" diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderApplicationDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderApplicationDao.kt new file mode 100644 index 00000000..6daab9f8 --- /dev/null +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderApplicationDao.kt @@ -0,0 +1,18 @@ +package se.eelde.toggles.database.dao.provider + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import se.eelde.toggles.database.WrenchApplication +import se.eelde.toggles.database.tables.ApplicationTable + +@Dao +interface ProviderApplicationDao { + + @Query("SELECT * FROM " + ApplicationTable.TABLE_NAME + " WHERE packageName IN (:packageName)") + fun loadByPackageName(packageName: String): WrenchApplication? + + @Insert + fun insert(application: WrenchApplication): Long +} diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationDao.kt new file mode 100644 index 00000000..df466160 --- /dev/null +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationDao.kt @@ -0,0 +1,97 @@ +@file:Suppress("MaxLineLength") + +package se.eelde.toggles.database.dao.provider + +import android.database.Cursor +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Transaction +import kotlinx.coroutines.flow.Flow +import se.eelde.toggles.database.WrenchConfiguration +import se.eelde.toggles.database.WrenchConfigurationWithValues +import se.eelde.toggles.database.tables.ConfigurationTable +import se.eelde.toggles.database.tables.ConfigurationValueTable +import se.eelde.toggles.database.tables.ScopeTable +import java.util.Date + +@Suppress("TooManyFunctions") +@Dao +interface ProviderConfigurationDao { + + @Query( + "SELECT configuration.id, " + + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " WHERE configuration.id = (:configurationId) AND configurationValue.scope = (:scopeId)" + ) + fun getToggle(configurationId: Long, scopeId: Long): Cursor + + @Query( + "SELECT configuration.id, " + + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope = (:scopeId)" + ) + fun getToggle(configurationKey: String, scopeId: Long): Cursor + + @Query( + "SELECT configuration.id, " + + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value," + + " scope.name as scope" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + + " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope IN (:scopeId)" + ) + fun getToggles(configurationKey: String, scopeId: List): Cursor + + @Query( + "SELECT configuration.id, " + + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value," + + " scope.name as scope" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + + " WHERE configuration.configurationKey = (:configurationKey)" + ) + fun getToggles(configurationKey: String): Cursor + + @Query( + "SELECT * " + + " FROM " + ConfigurationTable.TABLE_NAME + + " WHERE configuration.applicationId = (:applicationId) AND configuration.configurationKey = (:configurationKey)" + ) + fun getWrenchConfiguration(applicationId: Long, configurationKey: String): WrenchConfiguration? + + @Query("DELETE FROM configuration WHERE applicationId = :callingApplication AND id = :id") + fun deleteConfiguration(callingApplication: Long, id: Long): Int + + @Query("DELETE FROM configuration WHERE applicationId = :callingApplication AND configurationKey = :configurationKey") + fun deleteConfiguration(callingApplication: Long, configurationKey: String): Int + + @Insert + fun insert(wrenchConfiguration: WrenchConfiguration): Long + + @Query("UPDATE configuration set lastUse=:date WHERE id= :configurationId") + suspend fun touch(configurationId: Long, date: Date) + + @Query("UPDATE configuration SET configurationKey = :key, configurationType = :type WHERE applicationId = :callingApplication AND id= :id") + fun updateConfiguration(callingApplication: Long, id: Long, key: String, type: String): Int + + @Query("SELECT * FROM configuration WHERE id = :configurationId and applicationId = :callingApplication") + fun getConfigurationCursor(callingApplication: Long, configurationId: Long): Cursor + + @Query("SELECT * FROM configuration WHERE configurationKey = :configurationKey and applicationId = :callingApplication") + fun getConfigurationCursor(callingApplication: Long, configurationKey: String): Cursor +} diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationValueDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationValueDao.kt new file mode 100644 index 00000000..6b461aa2 --- /dev/null +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationValueDao.kt @@ -0,0 +1,34 @@ +@file:Suppress("MaxLineLength") + +package se.eelde.toggles.database.dao.provider + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import kotlinx.coroutines.flow.Flow +import se.eelde.toggles.database.WrenchConfigurationValue +import se.eelde.toggles.database.tables.ConfigurationValueTable + +@Dao +interface ProviderConfigurationValueDao { + @Query( + "UPDATE " + ConfigurationValueTable.TABLE_NAME + + " SET " + ConfigurationValueTable.COL_VALUE + " = (:value)" + + " WHERE " + ConfigurationValueTable.COL_CONFIG_ID + " = (:configurationId) AND " + ConfigurationValueTable.COL_SCOPE + " = (:scopeId) " + ) + fun updateConfigurationValueSync(configurationId: Long, scopeId: Long, value: String): Int + + @Insert + fun insertSync(wrenchConfigurationValue: WrenchConfigurationValue): Long + + @Insert + suspend fun insert(wrenchConfigurationValue: WrenchConfigurationValue): Long + + @Update + fun update(wrenchConfigurationValue: WrenchConfigurationValue): Int + + @Delete + suspend fun delete(selectedConfigurationValue: WrenchConfigurationValue) +} diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderPredefinedConfigurationValueDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderPredefinedConfigurationValueDao.kt new file mode 100644 index 00000000..2b8e5823 --- /dev/null +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderPredefinedConfigurationValueDao.kt @@ -0,0 +1,25 @@ +@file:Suppress("MaxLineLength") + +package se.eelde.toggles.database.dao.provider + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import kotlinx.coroutines.flow.Flow +import se.eelde.toggles.database.WrenchPredefinedConfigurationValue +import se.eelde.toggles.database.tables.PredefinedConfigurationValueTable + +@Dao +interface ProviderPredefinedConfigurationValueDao { + + @Query( + """SELECT * FROM ${PredefinedConfigurationValueTable.TABLE_NAME} WHERE ${PredefinedConfigurationValueTable.COL_CONFIG_ID} = (:configurationId) AND ${PredefinedConfigurationValueTable.COL_VALUE} = (:value) """ + ) + fun getByConfigurationAndValueId( + configurationId: Long, + value: String + ): WrenchPredefinedConfigurationValue + + @Insert + fun insert(fullConfig: WrenchPredefinedConfigurationValue): Long +} diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderScopeDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderScopeDao.kt new file mode 100644 index 00000000..a6216a75 --- /dev/null +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderScopeDao.kt @@ -0,0 +1,35 @@ +@file:Suppress("MaxLineLength") + +package se.eelde.toggles.database.dao.provider + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import kotlinx.coroutines.flow.Flow +import se.eelde.toggles.database.WrenchScope +import se.eelde.toggles.database.tables.ScopeTable + +@Dao +interface ProviderScopeDao { + + @Insert + fun insert(scope: WrenchScope): Long + + @Delete + fun delete(scope: WrenchScope) + + @Query( + "SELECT * FROM " + ScopeTable.TABLE_NAME + " WHERE " + ScopeTable.COL_APP_ID + " = (:applicationId) ORDER BY " + ScopeTable.COL_SELECTED_TIMESTAMP + " DESC LIMIT 1" + ) + fun getSelectedScope(applicationId: Long): WrenchScope? + + @Query( + "SELECT * FROM " + ScopeTable.TABLE_NAME + " WHERE " + ScopeTable.COL_APP_ID + " = (:applicationId) AND " + ScopeTable.COL_NAME + " = '" + WrenchScope.SCOPE_DEFAULT + "'" + ) + fun getDefaultScope(applicationId: Long): WrenchScope? + + @Update + suspend fun update(scope: WrenchScope) +} diff --git a/modules/enumconfiguration/src/main/java/se/eelde/toggles/enumconfiguration/EnumValueViewModel.kt b/modules/enumconfiguration/src/main/java/se/eelde/toggles/enumconfiguration/EnumValueViewModel.kt index 186403a9..ef07b0f0 100644 --- a/modules/enumconfiguration/src/main/java/se/eelde/toggles/enumconfiguration/EnumValueViewModel.kt +++ b/modules/enumconfiguration/src/main/java/se/eelde/toggles/enumconfiguration/EnumValueViewModel.kt @@ -12,11 +12,11 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import se.eelde.toggles.core.TogglesProviderContract -import se.eelde.toggles.database.WrenchConfigurationDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationValue -import se.eelde.toggles.database.WrenchConfigurationValueDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.database.WrenchPredefinedConfigurationValue -import se.eelde.toggles.database.WrenchPredefinedConfigurationValueDao +import se.eelde.toggles.database.dao.application.TogglesPredefinedConfigurationValueDao import se.eelde.toggles.provider.notifyInsert import se.eelde.toggles.provider.notifyUpdate import java.util.Date @@ -47,9 +47,9 @@ internal sealed class PartialViewState { class FragmentEnumValueViewModel @Inject internal constructor( private val savedStateHandle: SavedStateHandle, private val application: Application, - private val configurationDao: WrenchConfigurationDao, - private val configurationValueDao: WrenchConfigurationValueDao, - private val predefinedConfigurationValueDao: WrenchPredefinedConfigurationValueDao + private val configurationDao: TogglesConfigurationDao, + private val configurationValueDao: TogglesConfigurationValueDao, + private val predefinedConfigurationValueDao: TogglesPredefinedConfigurationValueDao ) : ViewModel() { private val _state = MutableStateFlow(reduce(ViewState(), PartialViewState.Empty)) diff --git a/modules/integerconfiguration/src/main/java/se/eelde/toggles/integerconfiguration/IntegerValueViewModel.kt b/modules/integerconfiguration/src/main/java/se/eelde/toggles/integerconfiguration/IntegerValueViewModel.kt index df7826bc..7c75faf4 100644 --- a/modules/integerconfiguration/src/main/java/se/eelde/toggles/integerconfiguration/IntegerValueViewModel.kt +++ b/modules/integerconfiguration/src/main/java/se/eelde/toggles/integerconfiguration/IntegerValueViewModel.kt @@ -12,9 +12,9 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import se.eelde.toggles.core.TogglesProviderContract -import se.eelde.toggles.database.WrenchConfigurationDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationValue -import se.eelde.toggles.database.WrenchConfigurationValueDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.provider.notifyUpdate import java.util.Date import javax.inject.Inject @@ -38,8 +38,8 @@ private sealed class PartialViewState { class FragmentIntegerValueViewModel @Inject internal constructor( savedStateHandle: SavedStateHandle, private val application: Application, - private val configurationDao: WrenchConfigurationDao, - private val configurationValueDao: WrenchConfigurationValueDao + private val configurationDao: TogglesConfigurationDao, + private val configurationValueDao: TogglesConfigurationValueDao ) : ViewModel() { private val _state = MutableStateFlow(reduce(ViewState(), PartialViewState.Empty)) diff --git a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt index 83248d08..8ec49c3e 100644 --- a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt +++ b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt @@ -12,41 +12,45 @@ import dagger.hilt.EntryPoint import dagger.hilt.InstallIn import dagger.hilt.android.EntryPointAccessors import dagger.hilt.components.SingletonComponent -import se.eelde.toggles.core.ColumnNames import se.eelde.toggles.core.Toggle import se.eelde.toggles.core.TogglesConfiguration import se.eelde.toggles.database.WrenchApplication -import se.eelde.toggles.database.WrenchApplicationDao +import se.eelde.toggles.database.dao.application.TogglesApplicationDao import se.eelde.toggles.database.WrenchConfiguration -import se.eelde.toggles.database.WrenchConfigurationDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationValue -import se.eelde.toggles.database.WrenchConfigurationValueDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.database.WrenchPredefinedConfigurationValue -import se.eelde.toggles.database.WrenchPredefinedConfigurationValueDao +import se.eelde.toggles.database.dao.application.TogglesPredefinedConfigurationValueDao import se.eelde.toggles.database.WrenchScope -import se.eelde.toggles.database.WrenchScopeDao +import se.eelde.toggles.database.dao.application.TogglesScopeDao +import se.eelde.toggles.database.dao.provider.ProviderApplicationDao +import se.eelde.toggles.database.dao.provider.ProviderConfigurationDao +import se.eelde.toggles.database.dao.provider.ProviderConfigurationValueDao +import se.eelde.toggles.database.dao.provider.ProviderPredefinedConfigurationValueDao +import se.eelde.toggles.database.dao.provider.ProviderScopeDao import se.eelde.toggles.prefs.TogglesPreferences import java.util.Date class TogglesProvider : ContentProvider() { - private val applicationDao: WrenchApplicationDao by lazy { - applicationEntryPoint.provideWrenchApplicationDao() + private val applicationDao: ProviderApplicationDao by lazy { + applicationEntryPoint.provideProviderApplicationDao() } - private val scopeDao: WrenchScopeDao by lazy { - applicationEntryPoint.provideWrenchScopeDao() + private val scopeDao: ProviderScopeDao by lazy { + applicationEntryPoint.provideProviderScopeDao() } - val configurationDao: WrenchConfigurationDao by lazy { - applicationEntryPoint.provideWrenchConfigurationDao() + val configurationDao: ProviderConfigurationDao by lazy { + applicationEntryPoint.provideProviderConfigurationDao() } - val configurationValueDao: WrenchConfigurationValueDao by lazy { - applicationEntryPoint.provideWrenchConfigurationValueDao() + val configurationValueDao: ProviderConfigurationValueDao by lazy { + applicationEntryPoint.provideProviderConfigurationValueDao() } - private val predefinedConfigurationDao: WrenchPredefinedConfigurationValueDao by lazy { + private val predefinedConfigurationDao: ProviderPredefinedConfigurationValueDao by lazy { applicationEntryPoint.providePredefinedConfigurationValueDao() } @@ -55,11 +59,11 @@ class TogglesProvider : ContentProvider() { } private val togglesPreferences: TogglesPreferences by lazy { - applicationEntryPoint.providesWrenchPreferences() + applicationEntryPoint.provideTogglesPreferences() } private val togglesUriMatcher: TogglesUriMatcher by lazy { - applicationEntryPoint.providesTogglesUriMatcher() + applicationEntryPoint.provideTogglesUriMatcher() } private val applicationEntryPoint: TogglesProviderEntryPoint by lazy { @@ -69,17 +73,17 @@ class TogglesProvider : ContentProvider() { @EntryPoint @InstallIn(SingletonComponent::class) interface TogglesProviderEntryPoint { - fun provideWrenchApplicationDao(): WrenchApplicationDao - fun provideWrenchConfigurationDao(): WrenchConfigurationDao - fun provideWrenchConfigurationValueDao(): WrenchConfigurationValueDao - fun provideWrenchScopeDao(): WrenchScopeDao - fun providePredefinedConfigurationValueDao(): WrenchPredefinedConfigurationValueDao + fun provideProviderApplicationDao(): ProviderApplicationDao + fun provideProviderConfigurationDao(): ProviderConfigurationDao + fun provideProviderConfigurationValueDao(): ProviderConfigurationValueDao + fun provideProviderScopeDao(): ProviderScopeDao + fun providePredefinedConfigurationValueDao(): ProviderPredefinedConfigurationValueDao fun providePackageManagerWrapper(): IPackageManagerWrapper - fun providesWrenchPreferences(): TogglesPreferences - fun providesTogglesUriMatcher(): TogglesUriMatcher + fun provideTogglesPreferences(): TogglesPreferences + fun provideTogglesUriMatcher(): TogglesUriMatcher } - private fun getCallingApplication(applicationDao: WrenchApplicationDao): WrenchApplication = + private fun getCallingApplication(applicationDao: ProviderApplicationDao): WrenchApplication = synchronized(this) { var wrenchApplication: WrenchApplication? = applicationDao.loadByPackageName(packageManagerWrapper.callingApplicationPackageName!!) @@ -404,7 +408,7 @@ class TogglesProvider : ContentProvider() { @Synchronized private fun getDefaultScope( context: Context?, - scopeDao: WrenchScopeDao?, + scopeDao: ProviderScopeDao?, applicationId: Long ): WrenchScope? { if (context == null) { @@ -425,7 +429,7 @@ class TogglesProvider : ContentProvider() { @Synchronized private fun getSelectedScope( context: Context?, - scopeDao: WrenchScopeDao?, + scopeDao: ProviderScopeDao?, applicationId: Long ): WrenchScope? { if (context == null) { diff --git a/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt b/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt index ba8b8bf0..198f5ca0 100644 --- a/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt +++ b/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt @@ -12,9 +12,9 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import se.eelde.toggles.core.TogglesProviderContract -import se.eelde.toggles.database.WrenchConfigurationDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationValue -import se.eelde.toggles.database.WrenchConfigurationValueDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.provider.notifyUpdate import java.util.Date import javax.inject.Inject @@ -39,8 +39,8 @@ class FragmentStringValueViewModel @Inject internal constructor( savedStateHandle: SavedStateHandle, private val application: Application, - private val configurationDao: WrenchConfigurationDao, - private val configurationValueDao: WrenchConfigurationValueDao + private val configurationDao: TogglesConfigurationDao, + private val configurationValueDao: TogglesConfigurationValueDao ) : ViewModel() { private val _state = MutableStateFlow(reduce(ViewState(), PartialViewState.Empty)) diff --git a/toggles-app/src/main/java/se/eelde/toggles/di/DaoModule.kt b/toggles-app/src/main/java/se/eelde/toggles/di/DaoModule.kt new file mode 100644 index 00000000..9c05fc64 --- /dev/null +++ b/toggles-app/src/main/java/se/eelde/toggles/di/DaoModule.kt @@ -0,0 +1,30 @@ +package se.eelde.toggles.di + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import se.eelde.toggles.database.WrenchDatabase + +@Module +@InstallIn(SingletonComponent::class) +object ApplicationDaoModule { + @Provides + fun provideTogglesApplicationDao(wrenchDatabase: WrenchDatabase) = + wrenchDatabase.togglesApplicationDao() + + @Provides + fun provideTogglesConfigurationDao(wrenchDatabase: WrenchDatabase) = + wrenchDatabase.togglesConfigurationDao() + + @Provides + fun provideTogglesConfigurationValueDao(wrenchDatabase: WrenchDatabase) = + wrenchDatabase.togglesConfigurationValueDao() + + @Provides + fun provideTogglesScopeDao(wrenchDatabase: WrenchDatabase) = wrenchDatabase.togglesScopeDao() + + @Provides + fun providePredefinedConfigurationValueDao(wrenchDatabase: WrenchDatabase) = + wrenchDatabase.togglesPredefinedConfigurationValueDao() +} \ No newline at end of file diff --git a/toggles-app/src/main/java/se/eelde/toggles/di/DatabaseModule.kt b/toggles-app/src/main/java/se/eelde/toggles/di/DatabaseModule.kt index 81a46209..20949891 100644 --- a/toggles-app/src/main/java/se/eelde/toggles/di/DatabaseModule.kt +++ b/toggles-app/src/main/java/se/eelde/toggles/di/DatabaseModule.kt @@ -26,26 +26,3 @@ object DatabaseModule { .build() } } - -@Module -@InstallIn(SingletonComponent::class) -object DaoModule { - @Provides - fun provideWrenchApplicationDao(wrenchDatabase: WrenchDatabase) = - wrenchDatabase.applicationDao() - - @Provides - fun provideWrenchConfigurationDao(wrenchDatabase: WrenchDatabase) = - wrenchDatabase.configurationDao() - - @Provides - fun provideWrenchConfigurationValueDao(wrenchDatabase: WrenchDatabase) = - wrenchDatabase.configurationValueDao() - - @Provides - fun provideWrenchScopeDao(wrenchDatabase: WrenchDatabase) = wrenchDatabase.scopeDao() - - @Provides - fun providePredefinedConfigurationValueDao(wrenchDatabase: WrenchDatabase) = - wrenchDatabase.predefinedConfigurationValueDao() -} diff --git a/toggles-app/src/main/java/se/eelde/toggles/di/ProviderDaoModule.kt b/toggles-app/src/main/java/se/eelde/toggles/di/ProviderDaoModule.kt new file mode 100644 index 00000000..0c5677a0 --- /dev/null +++ b/toggles-app/src/main/java/se/eelde/toggles/di/ProviderDaoModule.kt @@ -0,0 +1,31 @@ +package se.eelde.toggles.di + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import se.eelde.toggles.database.WrenchDatabase + + +@Module +@InstallIn(SingletonComponent::class) +object ProviderDaoModule { + @Provides + fun provideProviderApplicationDao(wrenchDatabase: WrenchDatabase) = + wrenchDatabase.providerApplicationDao() + + @Provides + fun provideProviderConfigurationDao(wrenchDatabase: WrenchDatabase) = + wrenchDatabase.providerConfigurationDao() + + @Provides + fun provideProviderConfigurationValueDao(wrenchDatabase: WrenchDatabase) = + wrenchDatabase.providerConfigurationValueDao() + + @Provides + fun provideProviderScopeDao(wrenchDatabase: WrenchDatabase) = wrenchDatabase.providerScopeDao() + + @Provides + fun provideProviderPredefinedConfigurationValueDao(wrenchDatabase: WrenchDatabase) = + wrenchDatabase.providerPredefinedConfigurationValueDao() +} \ No newline at end of file diff --git a/toggles-app/src/main/java/se/eelde/toggles/dialogs/scope/ScopeViewModel.kt b/toggles-app/src/main/java/se/eelde/toggles/dialogs/scope/ScopeViewModel.kt index 9dd4f4d7..4686ea00 100644 --- a/toggles-app/src/main/java/se/eelde/toggles/dialogs/scope/ScopeViewModel.kt +++ b/toggles-app/src/main/java/se/eelde/toggles/dialogs/scope/ScopeViewModel.kt @@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import se.eelde.toggles.database.WrenchScope -import se.eelde.toggles.database.WrenchScopeDao +import se.eelde.toggles.database.dao.application.TogglesScopeDao import java.util.Date import javax.inject.Inject @@ -33,7 +33,7 @@ private sealed class PartialViewState { @HiltViewModel class ScopeViewModel @Inject internal constructor( private val savedStateHandle: SavedStateHandle, - private val scopeDao: WrenchScopeDao + private val scopeDao: TogglesScopeDao ) : ViewModel() { private val applicationId: Long = savedStateHandle.get("applicationId")!! diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderPredefinedConfigurationValuesTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderPredefinedConfigurationValuesTest.kt index a084607f..f0ef8869 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderPredefinedConfigurationValuesTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderPredefinedConfigurationValuesTest.kt @@ -98,7 +98,7 @@ class TogglesProviderPredefinedConfigurationValuesTest { } togglesProvider.insert(TogglesProviderContract.toggleValueUri(), toggleValue.toContentValues()) - wrenchDatabase.predefinedConfigurationValueDao().getByConfigurationId(configId).test { + wrenchDatabase.togglesPredefinedConfigurationValueDao().getByConfigurationId(configId).test { val wrenchPredefinedConfigurationValueList = awaitItem() assertEquals(1, wrenchPredefinedConfigurationValueList.size) wrenchPredefinedConfigurationValueList[0].let { diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationIdTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationIdTest.kt similarity index 98% rename from toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationIdTest.kt rename to toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationIdTest.kt index e61e507a..c821b843 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationIdTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationIdTest.kt @@ -1,4 +1,4 @@ -package se.eelde.toggles.provider +package se.eelde.toggles.provider.configuration import android.app.Application import android.content.Context @@ -32,6 +32,7 @@ import se.eelde.toggles.core.TogglesConfiguration import se.eelde.toggles.core.TogglesProviderContract import se.eelde.toggles.database.WrenchDatabase import se.eelde.toggles.di.DatabaseModule +import se.eelde.toggles.provider.TogglesProvider import javax.inject.Inject import javax.inject.Singleton diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationKeyTest.kt similarity index 97% rename from toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt rename to toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationKeyTest.kt index 0a43324c..b7e2d049 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationKeyTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationKeyTest.kt @@ -1,4 +1,4 @@ -package se.eelde.toggles.provider +package se.eelde.toggles.provider.configuration import android.app.Application import android.content.Context @@ -32,6 +32,7 @@ import se.eelde.toggles.core.TogglesConfiguration import se.eelde.toggles.core.TogglesProviderContract import se.eelde.toggles.database.WrenchDatabase import se.eelde.toggles.di.DatabaseModule +import se.eelde.toggles.provider.TogglesProvider import javax.inject.Inject import javax.inject.Singleton diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationTest.kt similarity index 97% rename from toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt rename to toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationTest.kt index 8ad6ed6d..e707852e 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/TogglesProviderMatcherConfigurationTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationTest.kt @@ -1,4 +1,4 @@ -package se.eelde.toggles.provider +package se.eelde.toggles.provider.configuration import android.app.Application import android.content.Context @@ -31,6 +31,7 @@ import se.eelde.toggles.core.TogglesConfiguration import se.eelde.toggles.core.TogglesProviderContract import se.eelde.toggles.database.WrenchDatabase import se.eelde.toggles.di.DatabaseModule +import se.eelde.toggles.provider.TogglesProvider import javax.inject.Inject import javax.inject.Singleton diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/configurationValue/TogglesProviderMatcherConfigurationValueTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/configurationValue/TogglesProviderMatcherConfigurationValueTest.kt new file mode 100644 index 00000000..6cbb5dd8 --- /dev/null +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/configurationValue/TogglesProviderMatcherConfigurationValueTest.kt @@ -0,0 +1,130 @@ +package se.eelde.toggles.provider.configurationValue + +import android.app.Application +import android.content.Context +import android.os.Build +import androidx.core.content.ContextCompat +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.HiltTestApplication +import dagger.hilt.android.testing.UninstallModules +import dagger.hilt.components.SingletonComponent +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config +import se.eelde.toggles.BuildConfig +import se.eelde.toggles.R +import se.eelde.toggles.core.Toggle +import se.eelde.toggles.core.TogglesConfiguration +import se.eelde.toggles.core.TogglesProviderContract +import se.eelde.toggles.database.WrenchDatabase +import se.eelde.toggles.di.DatabaseModule +import se.eelde.toggles.provider.TogglesProvider +import javax.inject.Inject +import javax.inject.Singleton + +@HiltAndroidTest +@RunWith(AndroidJUnit4::class) +@UninstallModules(DatabaseModule::class) +@Config(application = HiltTestApplication::class, sdk = [Build.VERSION_CODES.P]) +class TogglesProviderMatcherConfigurationValueTest { + @get:Rule + var hiltRule = HiltAndroidRule(this) + + private lateinit var togglesProvider: TogglesProvider + + @Module + @InstallIn(SingletonComponent::class) + object TestModule { + @Singleton + @Provides + fun provideWrenchDb(@ApplicationContext context: Context): WrenchDatabase { + return Room.inMemoryDatabaseBuilder(context, WrenchDatabase::class.java) + .allowMainThreadQueries().build() + } + } + + @Inject + lateinit var wrenchDatabase: WrenchDatabase + + @Before + fun setUp() { + hiltRule.inject() + + val contentProviderController = + Robolectric.buildContentProvider(TogglesProvider::class.java) + .create(BuildConfig.CONFIG_AUTHORITY) + togglesProvider = contentProviderController.get() + + val context = ApplicationProvider.getApplicationContext() + val appIcon = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground) + shadowOf(context.packageManager).setApplicationIcon( + context.applicationInfo.packageName, + appIcon + ) + } + + @Test + fun testGetTypePredefinedConfigurationValue() { + val type = togglesProvider.getType(TogglesProviderContract.configurationUri()) + assertEquals("vnd.android.cursor.dir/vnd.se.eelde.toggles.configuration", type) + } + + @Test(expected = UnsupportedOperationException::class) + fun testUpdate() { + togglesProvider.update( + TogglesProviderContract.configurationUri(), + null, null, null + ) + } + + @Test + fun testInsert() { + val togglesConfiguration = TogglesConfiguration { + type = Toggle.TYPE.BOOLEAN + key = "myConfigurationkey" + } + + val uri = togglesProvider.insert( + TogglesProviderContract.configurationUri(), + togglesConfiguration.toContentValues(), + ) + + assertEquals( + "content://se.eelde.toggles.configprovider/configuration/1?API_VERSION=1", + uri.toString() + ) + } + + @Test(expected = UnsupportedOperationException::class) + fun testQuery() { + togglesProvider.query( + TogglesProviderContract.configurationUri(), + null, + null, + null, + null + ) + } + + @Test(expected = UnsupportedOperationException::class) + fun testDelete() { + togglesProvider.delete( + TogglesProviderContract.configurationUri(), + null, + null, + ) + } +} From dab6396338e5635dba1e6b19316e50001d9118ff Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Sat, 14 Sep 2024 20:01:10 +0200 Subject: [PATCH 08/24] Use local libraries --- gradle.properties | 2 +- settings.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index ebd41097..03cc77e8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -43,4 +43,4 @@ org.gradle.caching=true org.gradle.configuration-cache=true org.gradle.configuration-cache.max-problems=5 -shared-tests-are-android-tests=true +shared-tests-are-android-tests=false diff --git a/settings.gradle.kts b/settings.gradle.kts index d0a5cbe4..2141f0f3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -32,7 +32,7 @@ buildCache { } } -private val localLibraries = false +private val localLibraries = true rootProject.name = "Toggles" includeBuild("build-logic/conventions") From 98c238fd238e514b58dbdc5cb34de98e1dc38ad6 Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Mon, 16 Sep 2024 18:05:19 +0200 Subject: [PATCH 09/24] Enable edge to edge --- gradle/libs.versions.toml | 1 + .../BooleanValueViewModel.kt | 2 +- .../se/eelde/toggles/composetheme/Color.kt | 86 +++++++++ .../se/eelde/toggles/composetheme/Theme.kt | 174 ++++++++++++++---- .../se/eelde/toggles/composetheme/Type.kt | 146 +++++++++++++++ .../configurations/ConfigurationViewModel.kt | 4 +- .../dao/application/TogglesApplicationDao.kt | 1 - .../application/TogglesConfigurationDao.kt | 80 ++++---- .../dao/provider/ProviderApplicationDao.kt | 1 - .../dao/provider/ProviderConfigurationDao.kt | 77 ++++---- .../provider/ProviderConfigurationValueDao.kt | 1 - ...ProviderPredefinedConfigurationValueDao.kt | 1 - .../database/dao/provider/ProviderScopeDao.kt | 1 - .../enumconfiguration/EnumValueViewModel.kt | 4 +- .../IntegerValueViewModel.kt | 2 +- .../eelde/toggles/provider/TogglesProvider.kt | 8 +- .../StringValueViewModel.kt | 2 +- toggles-app/build.gradle.kts | 1 + .../java/se/eelde/toggles/MainActivity.kt | 7 +- .../{DaoModule.kt => ApplicationDaoModule.kt} | 2 +- .../se/eelde/toggles/di/ProviderDaoModule.kt | 3 +- toggles-app/src/main/res/values/styles.xml | 4 +- ...glesProviderMatcherConfigurationKeyTest.kt | 4 +- ...TogglesProviderMatcherConfigurationTest.kt | 4 +- ...esProviderMatcherConfigurationValueTest.kt | 4 +- toggles-sample/build.gradle.kts | 1 + .../java/com/example/toggles/MainActivity.kt | 7 + toggles-sample/src/main/res/values/styles.xml | 4 +- 28 files changed, 491 insertions(+), 141 deletions(-) create mode 100644 modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Color.kt create mode 100644 modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Type.kt rename toggles-app/src/main/java/se/eelde/toggles/di/{DaoModule.kt => ApplicationDaoModule.kt} (99%) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c802bb1c..96fef60d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -40,6 +40,7 @@ androidx-compose-ui-ui-text = { module = "androidx.compose.ui:ui-text" } androidx-compose-ui-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } androidx-compose-ui-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } androidx-core-core-ktx = "androidx.core:core-ktx:1.13.1" +androidx-core-core-splashscreen = "androidx.core:core-splashscreen:1.0.1" androidx-hilt-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "androidx-hilt" } androidx-hilt-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidx-hilt" } androidx-lifecycle-lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "androidx-lifecycle" } diff --git a/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt b/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt index ffe84b9d..9389741d 100644 --- a/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt +++ b/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt @@ -12,8 +12,8 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import se.eelde.toggles.core.TogglesProviderContract -import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationValue +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.provider.notifyUpdate import java.util.Date diff --git a/modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Color.kt b/modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Color.kt new file mode 100644 index 00000000..960b4674 --- /dev/null +++ b/modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Color.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * 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. + */ +@file:Suppress("MagicNumber") + +package se.eelde.toggles.composetheme + +import androidx.compose.ui.graphics.Color + +/** + * Now in Android colors. + */ +internal val Blue10 = Color(0xFF001F28) +internal val Blue20 = Color(0xFF003544) +internal val Blue30 = Color(0xFF004D61) +internal val Blue40 = Color(0xFF006780) +internal val Blue80 = Color(0xFF5DD5FC) +internal val Blue90 = Color(0xFFB8EAFF) +internal val DarkGreen10 = Color(0xFF0D1F12) +internal val DarkGreen20 = Color(0xFF223526) +internal val DarkGreen30 = Color(0xFF394B3C) +internal val DarkGreen40 = Color(0xFF4F6352) +internal val DarkGreen80 = Color(0xFFB7CCB8) +internal val DarkGreen90 = Color(0xFFD3E8D3) +internal val DarkGreenGray10 = Color(0xFF1A1C1A) +internal val DarkGreenGray20 = Color(0xFF2F312E) +internal val DarkGreenGray90 = Color(0xFFE2E3DE) +internal val DarkGreenGray95 = Color(0xFFF0F1EC) +internal val DarkGreenGray99 = Color(0xFFFBFDF7) +internal val DarkPurpleGray10 = Color(0xFF201A1B) +internal val DarkPurpleGray20 = Color(0xFF362F30) +internal val DarkPurpleGray90 = Color(0xFFECDFE0) +internal val DarkPurpleGray95 = Color(0xFFFAEEEF) +internal val DarkPurpleGray99 = Color(0xFFFCFCFC) +internal val Green10 = Color(0xFF00210B) +internal val Green20 = Color(0xFF003919) +internal val Green30 = Color(0xFF005227) +internal val Green40 = Color(0xFF006D36) +internal val Green80 = Color(0xFF0EE37C) +internal val Green90 = Color(0xFF5AFF9D) +internal val GreenGray30 = Color(0xFF414941) +internal val GreenGray50 = Color(0xFF727971) +internal val GreenGray60 = Color(0xFF8B938A) +internal val GreenGray80 = Color(0xFFC1C9BF) +internal val GreenGray90 = Color(0xFFDDE5DB) +internal val Orange10 = Color(0xFF380D00) +internal val Orange20 = Color(0xFF5B1A00) +internal val Orange30 = Color(0xFF812800) +internal val Orange40 = Color(0xFFA23F16) +internal val Orange80 = Color(0xFFFFB59B) +internal val Orange90 = Color(0xFFFFDBCF) +internal val Purple10 = Color(0xFF36003C) +internal val Purple20 = Color(0xFF560A5D) +internal val Purple30 = Color(0xFF702776) +internal val Purple40 = Color(0xFF8B418F) +internal val Purple80 = Color(0xFFFFA9FE) +internal val Purple90 = Color(0xFFFFD6FA) +internal val PurpleGray30 = Color(0xFF4D444C) +internal val PurpleGray50 = Color(0xFF7F747C) +internal val PurpleGray60 = Color(0xFF998D96) +internal val PurpleGray80 = Color(0xFFD0C3CC) +internal val PurpleGray90 = Color(0xFFEDDEE8) +internal val Red10 = Color(0xFF410002) +internal val Red20 = Color(0xFF690005) +internal val Red30 = Color(0xFF93000A) +internal val Red40 = Color(0xFFBA1A1A) +internal val Red80 = Color(0xFFFFB4AB) +internal val Red90 = Color(0xFFFFDAD6) +internal val Teal10 = Color(0xFF001F26) +internal val Teal20 = Color(0xFF02363F) +internal val Teal30 = Color(0xFF214D56) +internal val Teal40 = Color(0xFF3A656F) +internal val Teal80 = Color(0xFFA2CED9) +internal val Teal90 = Color(0xFFBEEAF6) diff --git a/modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Theme.kt b/modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Theme.kt index 207ab0f1..1778c490 100644 --- a/modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Theme.kt +++ b/modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Theme.kt @@ -1,69 +1,162 @@ package se.eelde.toggles.composetheme -import android.app.Activity import android.os.Build +import androidx.annotation.ChecksSdkIntAtLeast +import androidx.annotation.VisibleForTesting import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Typography import androidx.compose.material3.darkColorScheme import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp -import androidx.core.view.ViewCompat -private val DarkColorScheme = darkColorScheme( - primary = Color(color = 0xFF7673C5), - secondary = Color(color = 0xFF07BD46), - tertiary = Color(color = 0xFF07BD46) +/** + * Light default theme color scheme + */ +@VisibleForTesting +val LightDefaultColorScheme = lightColorScheme( + primary = Purple40, + onPrimary = Color.White, + primaryContainer = Purple90, + onPrimaryContainer = Purple10, + secondary = Orange40, + onSecondary = Color.White, + secondaryContainer = Orange90, + onSecondaryContainer = Orange10, + tertiary = Blue40, + onTertiary = Color.White, + tertiaryContainer = Blue90, + onTertiaryContainer = Blue10, + error = Red40, + onError = Color.White, + errorContainer = Red90, + onErrorContainer = Red10, + background = DarkPurpleGray99, + onBackground = DarkPurpleGray10, + surface = DarkPurpleGray99, + onSurface = DarkPurpleGray10, + surfaceVariant = PurpleGray90, + onSurfaceVariant = PurpleGray30, + inverseSurface = DarkPurpleGray20, + inverseOnSurface = DarkPurpleGray95, + outline = PurpleGray50, ) -private val LightColorScheme = lightColorScheme( - primary = Color(color = 0xFF1E1A87), - secondary = Color(color = 0xff287F46), - tertiary = Color(color = 0xff287F46) +/** + * Dark default theme color scheme + */ +@VisibleForTesting +val DarkDefaultColorScheme = darkColorScheme( + primary = Purple80, + onPrimary = Purple20, + primaryContainer = Purple30, + onPrimaryContainer = Purple90, + secondary = Orange80, + onSecondary = Orange20, + secondaryContainer = Orange30, + onSecondaryContainer = Orange90, + tertiary = Blue80, + onTertiary = Blue20, + tertiaryContainer = Blue30, + onTertiaryContainer = Blue90, + error = Red80, + onError = Red20, + errorContainer = Red30, + onErrorContainer = Red90, + background = DarkPurpleGray10, + onBackground = DarkPurpleGray90, + surface = DarkPurpleGray10, + onSurface = DarkPurpleGray90, + surfaceVariant = PurpleGray30, + onSurfaceVariant = PurpleGray80, + inverseSurface = DarkPurpleGray90, + inverseOnSurface = DarkPurpleGray10, + outline = PurpleGray60, ) -val Typography = Typography( - bodyLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp - ) +/** + * Light Android theme color scheme + */ +@VisibleForTesting +val LightAndroidColorScheme = lightColorScheme( + primary = Green40, + onPrimary = Color.White, + primaryContainer = Green90, + onPrimaryContainer = Green10, + secondary = DarkGreen40, + onSecondary = Color.White, + secondaryContainer = DarkGreen90, + onSecondaryContainer = DarkGreen10, + tertiary = Teal40, + onTertiary = Color.White, + tertiaryContainer = Teal90, + onTertiaryContainer = Teal10, + error = Red40, + onError = Color.White, + errorContainer = Red90, + onErrorContainer = Red10, + background = DarkGreenGray99, + onBackground = DarkGreenGray10, + surface = DarkGreenGray99, + onSurface = DarkGreenGray10, + surfaceVariant = GreenGray90, + onSurfaceVariant = GreenGray30, + inverseSurface = DarkGreenGray20, + inverseOnSurface = DarkGreenGray95, + outline = GreenGray50, +) + +/** + * Dark Android theme color scheme + */ +@VisibleForTesting +val DarkAndroidColorScheme = darkColorScheme( + primary = Green80, + onPrimary = Green20, + primaryContainer = Green30, + onPrimaryContainer = Green90, + secondary = DarkGreen80, + onSecondary = DarkGreen20, + secondaryContainer = DarkGreen30, + onSecondaryContainer = DarkGreen90, + tertiary = Teal80, + onTertiary = Teal20, + tertiaryContainer = Teal30, + onTertiaryContainer = Teal90, + error = Red80, + onError = Red20, + errorContainer = Red30, + onErrorContainer = Red90, + background = DarkGreenGray10, + onBackground = DarkGreenGray90, + surface = DarkGreenGray10, + onSurface = DarkGreenGray90, + surfaceVariant = GreenGray30, + onSurfaceVariant = GreenGray80, + inverseSurface = DarkGreenGray90, + inverseOnSurface = DarkGreenGray10, + outline = GreenGray60, ) @Composable fun TogglesTheme( darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, - content: @Composable () -> Unit + androidTheme: Boolean = false, + disableDynamicTheming: Boolean = true, + content: @Composable () -> Unit, ) { + // Color scheme val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + androidTheme -> if (darkTheme) DarkAndroidColorScheme else LightAndroidColorScheme + !disableDynamicTheming && supportsDynamicTheming() -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } - darkTheme -> DarkColorScheme - else -> LightColorScheme - } - val view = LocalView.current - if (!view.isInEditMode) { - SideEffect { - (view.context as Activity).window.statusBarColor = colorScheme.primary.toArgb() - ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars = darkTheme - } + + else -> if (darkTheme) DarkDefaultColorScheme else LightDefaultColorScheme } MaterialTheme( @@ -72,3 +165,6 @@ fun TogglesTheme( content = content ) } + +@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) +fun supportsDynamicTheming() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S diff --git a/modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Type.kt b/modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Type.kt new file mode 100644 index 00000000..69e8756e --- /dev/null +++ b/modules/compose-theme/src/main/java/se/eelde/toggles/composetheme/Type.kt @@ -0,0 +1,146 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * 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 se.eelde.toggles.composetheme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.LineHeightStyle +import androidx.compose.ui.text.style.LineHeightStyle.Alignment +import androidx.compose.ui.text.style.LineHeightStyle.Trim +import androidx.compose.ui.unit.sp + +internal val Typography = Typography( + displayLarge = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 57.sp, + lineHeight = 64.sp, + letterSpacing = (-0.25).sp, + ), + displayMedium = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 45.sp, + lineHeight = 52.sp, + letterSpacing = 0.sp, + ), + displaySmall = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 36.sp, + lineHeight = 44.sp, + letterSpacing = 0.sp, + ), + headlineLarge = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 32.sp, + lineHeight = 40.sp, + letterSpacing = 0.sp, + ), + headlineMedium = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 28.sp, + lineHeight = 36.sp, + letterSpacing = 0.sp, + ), + headlineSmall = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 24.sp, + lineHeight = 32.sp, + letterSpacing = 0.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Bottom, + trim = Trim.None, + ), + ), + titleLarge = TextStyle( + fontWeight = FontWeight.Bold, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Bottom, + trim = Trim.LastLineBottom, + ), + ), + titleMedium = TextStyle( + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + lineHeight = 24.sp, + letterSpacing = 0.1.sp, + ), + titleSmall = TextStyle( + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.1.sp, + ), + // Default text style + bodyLarge = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Center, + trim = Trim.None, + ), + ), + bodyMedium = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.25.sp, + ), + bodySmall = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 12.sp, + lineHeight = 16.sp, + letterSpacing = 0.4.sp, + ), + // Used for Button + labelLarge = TextStyle( + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.1.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Center, + trim = Trim.LastLineBottom, + ), + ), + // Used for Navigation items + labelMedium = TextStyle( + fontWeight = FontWeight.Medium, + fontSize = 12.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Center, + trim = Trim.LastLineBottom, + ), + ), + // Used for Tag + labelSmall = TextStyle( + fontWeight = FontWeight.Medium, + fontSize = 10.sp, + lineHeight = 14.sp, + letterSpacing = 0.sp, + lineHeightStyle = LineHeightStyle( + alignment = Alignment.Center, + trim = Trim.LastLineBottom, + ), + ), +) diff --git a/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt b/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt index 2e5fb5f7..bc812215 100644 --- a/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt +++ b/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt @@ -18,10 +18,10 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import se.eelde.toggles.database.WrenchApplication -import se.eelde.toggles.database.dao.application.TogglesApplicationDao -import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationWithValues import se.eelde.toggles.database.WrenchScope +import se.eelde.toggles.database.dao.application.TogglesApplicationDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.dao.application.TogglesScopeDao import javax.inject.Inject diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesApplicationDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesApplicationDao.kt index f6a5a721..fc25e032 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesApplicationDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesApplicationDao.kt @@ -2,7 +2,6 @@ package se.eelde.toggles.database.dao.application import androidx.room.Dao import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import kotlinx.coroutines.flow.Flow import se.eelde.toggles.database.WrenchApplication diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesConfigurationDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesConfigurationDao.kt index b293d264..809b6cd6 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesConfigurationDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/application/TogglesConfigurationDao.kt @@ -21,63 +21,63 @@ interface TogglesConfigurationDao { @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " WHERE configuration.id = (:configurationId) AND configurationValue.scope = (:scopeId)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " WHERE configuration.id = (:configurationId) AND configurationValue.scope = (:scopeId)" ) fun getToggle(configurationId: Long, scopeId: Long): Cursor @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope = (:scopeId)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope = (:scopeId)" ) fun getToggle(configurationKey: String, scopeId: Long): Cursor @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value," + - " scope.name as scope" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + - " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope IN (:scopeId)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value," + + " scope.name as scope" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + + " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope IN (:scopeId)" ) fun getToggles(configurationKey: String, scopeId: List): Cursor @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value," + - " scope.name as scope" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + - " WHERE configuration.configurationKey = (:configurationKey)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value," + + " scope.name as scope" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + + " WHERE configuration.configurationKey = (:configurationKey)" ) fun getToggles(configurationKey: String): Cursor @Query( "SELECT * " + - " FROM " + ConfigurationTable.TABLE_NAME + - " WHERE configuration.applicationId = (:applicationId) AND configuration.configurationKey = (:configurationKey)" + " FROM " + ConfigurationTable.TABLE_NAME + + " WHERE configuration.applicationId = (:applicationId) AND configuration.configurationKey = (:configurationKey)" ) fun getWrenchConfiguration(applicationId: Long, configurationKey: String): WrenchConfiguration? @Query( "SELECT * " + - " FROM " + ConfigurationTable.TABLE_NAME + - " WHERE configuration.applicationId = (:applicationId) AND configuration.configurationKey = (:configurationKey)" + " FROM " + ConfigurationTable.TABLE_NAME + + " WHERE configuration.applicationId = (:applicationId) AND configuration.configurationKey = (:configurationKey)" ) fun getWrenchConfigurationByKey( applicationId: Long, @@ -86,8 +86,8 @@ interface TogglesConfigurationDao { @Query( "SELECT * " + - " FROM " + ConfigurationTable.TABLE_NAME + - " WHERE configuration.applicationId = (:applicationId) AND configuration.id = (:configurationId)" + " FROM " + ConfigurationTable.TABLE_NAME + + " WHERE configuration.applicationId = (:applicationId) AND configuration.id = (:configurationId)" ) fun getWrenchConfigurationById(applicationId: Long, configurationId: Long): WrenchConfiguration? @@ -112,7 +112,9 @@ interface TogglesConfigurationDao { @Query("DELETE FROM configuration WHERE applicationId = :callingApplication AND id = :id") fun deleteConfiguration(callingApplication: Long, id: Long): Int - @Query("DELETE FROM configuration WHERE applicationId = :callingApplication AND configurationKey = :configurationKey") + @Query( + "DELETE FROM configuration WHERE applicationId = :callingApplication AND configurationKey = :configurationKey" + ) fun deleteConfiguration(callingApplication: Long, configurationKey: String): Int @Insert @@ -121,12 +123,16 @@ interface TogglesConfigurationDao { @Query("UPDATE configuration set lastUse=:date WHERE id= :configurationId") suspend fun touch(configurationId: Long, date: Date) - @Query("UPDATE configuration SET configurationKey = :key, configurationType = :type WHERE applicationId = :callingApplication AND id= :id") + @Query( + "UPDATE configuration SET configurationKey = :key, configurationType = :type WHERE applicationId = :callingApplication AND id= :id" + ) fun updateConfiguration(callingApplication: Long, id: Long, key: String, type: String): Int @Query("SELECT * FROM configuration WHERE id = :configurationId and applicationId = :callingApplication") fun getConfigurationCursor(callingApplication: Long, configurationId: Long): Cursor - @Query("SELECT * FROM configuration WHERE configurationKey = :configurationKey and applicationId = :callingApplication") + @Query( + "SELECT * FROM configuration WHERE configurationKey = :configurationKey and applicationId = :callingApplication" + ) fun getConfigurationCursor(callingApplication: Long, configurationKey: String): Cursor } diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderApplicationDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderApplicationDao.kt index 6daab9f8..6b06455c 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderApplicationDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderApplicationDao.kt @@ -1,7 +1,6 @@ package se.eelde.toggles.database.dao.provider import androidx.room.Dao -import androidx.room.Delete import androidx.room.Insert import androidx.room.Query import se.eelde.toggles.database.WrenchApplication diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationDao.kt index df466160..29af31b8 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationDao.kt @@ -6,10 +6,7 @@ import android.database.Cursor import androidx.room.Dao import androidx.room.Insert import androidx.room.Query -import androidx.room.Transaction -import kotlinx.coroutines.flow.Flow import se.eelde.toggles.database.WrenchConfiguration -import se.eelde.toggles.database.WrenchConfigurationWithValues import se.eelde.toggles.database.tables.ConfigurationTable import se.eelde.toggles.database.tables.ConfigurationValueTable import se.eelde.toggles.database.tables.ScopeTable @@ -21,63 +18,65 @@ interface ProviderConfigurationDao { @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " WHERE configuration.id = (:configurationId) AND configurationValue.scope = (:scopeId)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " WHERE configuration.id = (:configurationId) AND configurationValue.scope = (:scopeId)" ) fun getToggle(configurationId: Long, scopeId: Long): Cursor @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope = (:scopeId)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope = (:scopeId)" ) fun getToggle(configurationKey: String, scopeId: Long): Cursor @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value," + - " scope.name as scope" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + - " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope IN (:scopeId)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value," + + " scope.name as scope" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + + " WHERE configuration.configurationKey = (:configurationKey) AND configurationValue.scope IN (:scopeId)" ) fun getToggles(configurationKey: String, scopeId: List): Cursor @Query( "SELECT configuration.id, " + - " configuration.configurationKey, " + - " configuration.configurationType," + - " configurationValue.value," + - " scope.name as scope" + - " FROM " + ConfigurationTable.TABLE_NAME + - " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + - " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + - " WHERE configuration.configurationKey = (:configurationKey)" + " configuration.configurationKey, " + + " configuration.configurationType," + + " configurationValue.value," + + " scope.name as scope" + + " FROM " + ConfigurationTable.TABLE_NAME + + " INNER JOIN " + ConfigurationValueTable.TABLE_NAME + " ON configuration.id = configurationValue.configurationId " + + " INNER JOIN " + ScopeTable.TABLE_NAME + " ON configurationValue.scope = scope.id " + + " WHERE configuration.configurationKey = (:configurationKey)" ) fun getToggles(configurationKey: String): Cursor @Query( "SELECT * " + - " FROM " + ConfigurationTable.TABLE_NAME + - " WHERE configuration.applicationId = (:applicationId) AND configuration.configurationKey = (:configurationKey)" + " FROM " + ConfigurationTable.TABLE_NAME + + " WHERE configuration.applicationId = (:applicationId) AND configuration.configurationKey = (:configurationKey)" ) fun getWrenchConfiguration(applicationId: Long, configurationKey: String): WrenchConfiguration? - + @Query("DELETE FROM configuration WHERE applicationId = :callingApplication AND id = :id") fun deleteConfiguration(callingApplication: Long, id: Long): Int - @Query("DELETE FROM configuration WHERE applicationId = :callingApplication AND configurationKey = :configurationKey") + @Query( + "DELETE FROM configuration WHERE applicationId = :callingApplication AND configurationKey = :configurationKey" + ) fun deleteConfiguration(callingApplication: Long, configurationKey: String): Int @Insert @@ -86,12 +85,16 @@ interface ProviderConfigurationDao { @Query("UPDATE configuration set lastUse=:date WHERE id= :configurationId") suspend fun touch(configurationId: Long, date: Date) - @Query("UPDATE configuration SET configurationKey = :key, configurationType = :type WHERE applicationId = :callingApplication AND id= :id") + @Query( + "UPDATE configuration SET configurationKey = :key, configurationType = :type WHERE applicationId = :callingApplication AND id= :id" + ) fun updateConfiguration(callingApplication: Long, id: Long, key: String, type: String): Int @Query("SELECT * FROM configuration WHERE id = :configurationId and applicationId = :callingApplication") fun getConfigurationCursor(callingApplication: Long, configurationId: Long): Cursor - @Query("SELECT * FROM configuration WHERE configurationKey = :configurationKey and applicationId = :callingApplication") + @Query( + "SELECT * FROM configuration WHERE configurationKey = :configurationKey and applicationId = :callingApplication" + ) fun getConfigurationCursor(callingApplication: Long, configurationKey: String): Cursor } diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationValueDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationValueDao.kt index 6b461aa2..80c5c4a3 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationValueDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderConfigurationValueDao.kt @@ -7,7 +7,6 @@ import androidx.room.Delete import androidx.room.Insert import androidx.room.Query import androidx.room.Update -import kotlinx.coroutines.flow.Flow import se.eelde.toggles.database.WrenchConfigurationValue import se.eelde.toggles.database.tables.ConfigurationValueTable diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderPredefinedConfigurationValueDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderPredefinedConfigurationValueDao.kt index 2b8e5823..5ea4bedf 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderPredefinedConfigurationValueDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderPredefinedConfigurationValueDao.kt @@ -5,7 +5,6 @@ package se.eelde.toggles.database.dao.provider import androidx.room.Dao import androidx.room.Insert import androidx.room.Query -import kotlinx.coroutines.flow.Flow import se.eelde.toggles.database.WrenchPredefinedConfigurationValue import se.eelde.toggles.database.tables.PredefinedConfigurationValueTable diff --git a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderScopeDao.kt b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderScopeDao.kt index a6216a75..a8e935a7 100644 --- a/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderScopeDao.kt +++ b/modules/database/src/main/java/se/eelde/toggles/database/dao/provider/ProviderScopeDao.kt @@ -7,7 +7,6 @@ import androidx.room.Delete import androidx.room.Insert import androidx.room.Query import androidx.room.Update -import kotlinx.coroutines.flow.Flow import se.eelde.toggles.database.WrenchScope import se.eelde.toggles.database.tables.ScopeTable diff --git a/modules/enumconfiguration/src/main/java/se/eelde/toggles/enumconfiguration/EnumValueViewModel.kt b/modules/enumconfiguration/src/main/java/se/eelde/toggles/enumconfiguration/EnumValueViewModel.kt index ef07b0f0..02f7b87f 100644 --- a/modules/enumconfiguration/src/main/java/se/eelde/toggles/enumconfiguration/EnumValueViewModel.kt +++ b/modules/enumconfiguration/src/main/java/se/eelde/toggles/enumconfiguration/EnumValueViewModel.kt @@ -12,10 +12,10 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import se.eelde.toggles.core.TogglesProviderContract -import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationValue -import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.database.WrenchPredefinedConfigurationValue +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao +import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.database.dao.application.TogglesPredefinedConfigurationValueDao import se.eelde.toggles.provider.notifyInsert import se.eelde.toggles.provider.notifyUpdate diff --git a/modules/integerconfiguration/src/main/java/se/eelde/toggles/integerconfiguration/IntegerValueViewModel.kt b/modules/integerconfiguration/src/main/java/se/eelde/toggles/integerconfiguration/IntegerValueViewModel.kt index 7c75faf4..9e8fcd4f 100644 --- a/modules/integerconfiguration/src/main/java/se/eelde/toggles/integerconfiguration/IntegerValueViewModel.kt +++ b/modules/integerconfiguration/src/main/java/se/eelde/toggles/integerconfiguration/IntegerValueViewModel.kt @@ -12,8 +12,8 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import se.eelde.toggles.core.TogglesProviderContract -import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationValue +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.provider.notifyUpdate import java.util.Date diff --git a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt index 8ec49c3e..85cada32 100644 --- a/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt +++ b/modules/provider/src/main/java/se/eelde/toggles/provider/TogglesProvider.kt @@ -15,15 +15,10 @@ import dagger.hilt.components.SingletonComponent import se.eelde.toggles.core.Toggle import se.eelde.toggles.core.TogglesConfiguration import se.eelde.toggles.database.WrenchApplication -import se.eelde.toggles.database.dao.application.TogglesApplicationDao import se.eelde.toggles.database.WrenchConfiguration -import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationValue -import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.database.WrenchPredefinedConfigurationValue -import se.eelde.toggles.database.dao.application.TogglesPredefinedConfigurationValueDao import se.eelde.toggles.database.WrenchScope -import se.eelde.toggles.database.dao.application.TogglesScopeDao import se.eelde.toggles.database.dao.provider.ProviderApplicationDao import se.eelde.toggles.database.dao.provider.ProviderConfigurationDao import se.eelde.toggles.database.dao.provider.ProviderConfigurationValueDao @@ -187,6 +182,7 @@ class TogglesProvider : ContentProvider() { return callingApplication.packageName == context!!.packageName } + @Suppress("LongMethod") override fun insert(uri: Uri, values: ContentValues?): Uri { val callingApplication = getCallingApplication(applicationDao) @@ -476,7 +472,7 @@ class TogglesProvider : ContentProvider() { if (strictApiVersion) { throw IllegalArgumentException( "This content provider requires you to provide a " + - "valid api-version in a queryParameter" + "valid api-version in a queryParameter" ) } } diff --git a/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt b/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt index 198f5ca0..d7aed4b8 100644 --- a/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt +++ b/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt @@ -12,8 +12,8 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import se.eelde.toggles.core.TogglesProviderContract -import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.WrenchConfigurationValue +import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.provider.notifyUpdate import java.util.Date diff --git a/toggles-app/build.gradle.kts b/toggles-app/build.gradle.kts index fd07caa1..7c7303a6 100644 --- a/toggles-app/build.gradle.kts +++ b/toggles-app/build.gradle.kts @@ -136,6 +136,7 @@ dependencies { implementation(libs.androidx.compose.runtime) implementation(libs.androidx.compose.ui) implementation(libs.androidx.activity.activity.compose) + implementation(libs.androidx.core.core.splashscreen) implementation(libs.androidx.compose.foundation.foundation.layout) implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material.material.icons.extended) diff --git a/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt b/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt index 0c794b15..b78a1c8d 100644 --- a/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt +++ b/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt @@ -3,6 +3,7 @@ package se.eelde.toggles import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack @@ -14,6 +15,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost @@ -143,10 +145,13 @@ fun Navigation( @AndroidEntryPoint class MainActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { + installSplashScreen() + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { TogglesTheme { val navController: NavHostController = rememberNavController() diff --git a/toggles-app/src/main/java/se/eelde/toggles/di/DaoModule.kt b/toggles-app/src/main/java/se/eelde/toggles/di/ApplicationDaoModule.kt similarity index 99% rename from toggles-app/src/main/java/se/eelde/toggles/di/DaoModule.kt rename to toggles-app/src/main/java/se/eelde/toggles/di/ApplicationDaoModule.kt index 9c05fc64..4a671540 100644 --- a/toggles-app/src/main/java/se/eelde/toggles/di/DaoModule.kt +++ b/toggles-app/src/main/java/se/eelde/toggles/di/ApplicationDaoModule.kt @@ -27,4 +27,4 @@ object ApplicationDaoModule { @Provides fun providePredefinedConfigurationValueDao(wrenchDatabase: WrenchDatabase) = wrenchDatabase.togglesPredefinedConfigurationValueDao() -} \ No newline at end of file +} diff --git a/toggles-app/src/main/java/se/eelde/toggles/di/ProviderDaoModule.kt b/toggles-app/src/main/java/se/eelde/toggles/di/ProviderDaoModule.kt index 0c5677a0..f1b67320 100644 --- a/toggles-app/src/main/java/se/eelde/toggles/di/ProviderDaoModule.kt +++ b/toggles-app/src/main/java/se/eelde/toggles/di/ProviderDaoModule.kt @@ -6,7 +6,6 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import se.eelde.toggles.database.WrenchDatabase - @Module @InstallIn(SingletonComponent::class) object ProviderDaoModule { @@ -28,4 +27,4 @@ object ProviderDaoModule { @Provides fun provideProviderPredefinedConfigurationValueDao(wrenchDatabase: WrenchDatabase) = wrenchDatabase.providerPredefinedConfigurationValueDao() -} \ No newline at end of file +} diff --git a/toggles-app/src/main/res/values/styles.xml b/toggles-app/src/main/res/values/styles.xml index 7a084a9c..c453a356 100644 --- a/toggles-app/src/main/res/values/styles.xml +++ b/toggles-app/src/main/res/values/styles.xml @@ -1,3 +1,5 @@ - diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationKeyTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationKeyTest.kt index b7e2d049..42dff1c3 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationKeyTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationKeyTest.kt @@ -108,7 +108,7 @@ class TogglesProviderMatcherConfigurationKeyTest { @Test fun testQuery() { - val uri = togglesProvider.insert( + togglesProvider.insert( TogglesProviderContract.configurationUri(), togglesConfiguration.toContentValues(), ) @@ -125,7 +125,7 @@ class TogglesProviderMatcherConfigurationKeyTest { @Test fun testDelete() { - val uri = togglesProvider.insert( + togglesProvider.insert( TogglesProviderContract.configurationUri(), togglesConfiguration.toContentValues(), ) diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationTest.kt index e707852e..6033fac1 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/configuration/TogglesProviderMatcherConfigurationTest.kt @@ -86,7 +86,9 @@ class TogglesProviderMatcherConfigurationTest { fun testUpdate() { togglesProvider.update( TogglesProviderContract.configurationUri(), - null, null, null + null, + null, + null ) } diff --git a/toggles-app/src/test/java/se/eelde/toggles/provider/configurationValue/TogglesProviderMatcherConfigurationValueTest.kt b/toggles-app/src/test/java/se/eelde/toggles/provider/configurationValue/TogglesProviderMatcherConfigurationValueTest.kt index 6cbb5dd8..414e8518 100644 --- a/toggles-app/src/test/java/se/eelde/toggles/provider/configurationValue/TogglesProviderMatcherConfigurationValueTest.kt +++ b/toggles-app/src/test/java/se/eelde/toggles/provider/configurationValue/TogglesProviderMatcherConfigurationValueTest.kt @@ -86,7 +86,9 @@ class TogglesProviderMatcherConfigurationValueTest { fun testUpdate() { togglesProvider.update( TogglesProviderContract.configurationUri(), - null, null, null + null, + null, + null ) } diff --git a/toggles-sample/build.gradle.kts b/toggles-sample/build.gradle.kts index 1cf722de..ccc96ade 100644 --- a/toggles-sample/build.gradle.kts +++ b/toggles-sample/build.gradle.kts @@ -69,6 +69,7 @@ dependencies { implementation(libs.androidx.compose.foundation) implementation(libs.androidx.compose.animation) implementation(libs.androidx.compose.ui.ui.tooling) + implementation(libs.androidx.core.core.splashscreen) implementation(libs.androidx.lifecycle.lifecycle.viewmodel.compose) implementation(libs.androidx.hilt.hilt.navigation.compose) implementation(libs.androidx.navigation.navigation.compose) diff --git a/toggles-sample/src/main/java/com/example/toggles/MainActivity.kt b/toggles-sample/src/main/java/com/example/toggles/MainActivity.kt index c14d9773..462a8068 100644 --- a/toggles-sample/src/main/java/com/example/toggles/MainActivity.kt +++ b/toggles-sample/src/main/java/com/example/toggles/MainActivity.kt @@ -3,6 +3,7 @@ package com.example.toggles import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -17,6 +18,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost @@ -32,7 +34,12 @@ import se.eelde.toggles.oss.OssView class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { + installSplashScreen() + super.onCreate(savedInstanceState) + + enableEdgeToEdge() + setContent { TogglesTheme { val navController: NavHostController = rememberNavController() diff --git a/toggles-sample/src/main/res/values/styles.xml b/toggles-sample/src/main/res/values/styles.xml index 7a084a9c..66cad0b9 100644 --- a/toggles-sample/src/main/res/values/styles.xml +++ b/toggles-sample/src/main/res/values/styles.xml @@ -1,3 +1,5 @@ - From 13469bc334a2fd7edaf86a339715b3038d505cdf Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Mon, 16 Sep 2024 18:45:29 +0200 Subject: [PATCH 10/24] Start using typesafe navigation --- gradle/libs.versions.toml | 2 ++ modules/applications/build.gradle.kts | 1 + .../toggles/applications/ApplicationEntry.kt | 3 +- modules/configurations/build.gradle.kts | 1 + .../configurations/ConfigurationViewModel.kt | 14 +++++++-- .../configurations/ConfigurationsEntry.kt | 17 +++++----- modules/routes/.gitignore | 1 + modules/routes/OWNERSHIP.toml | 4 +++ modules/routes/build.gradle.kts | 13 ++++++++ modules/routes/lint-baseline.xml | 4 +++ modules/routes/src/main/AndroidManifest.xml | 2 ++ .../java/se/eelde/toggles/routes/Routes.kt | 31 +++++++++++++++++++ settings.gradle.kts | 1 + toggles-app/build.gradle.kts | 2 +- .../java/se/eelde/toggles/MainActivity.kt | 23 +++++++------- 15 files changed, 96 insertions(+), 23 deletions(-) create mode 100644 modules/routes/.gitignore create mode 100644 modules/routes/OWNERSHIP.toml create mode 100644 modules/routes/build.gradle.kts create mode 100644 modules/routes/lint-baseline.xml create mode 100644 modules/routes/src/main/AndroidManifest.xml create mode 100644 modules/routes/src/main/java/se/eelde/toggles/routes/Routes.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 96fef60d..aad7fcaa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -91,6 +91,7 @@ org-jetbrains-kotlinx-kotlinx-collections-immutable = "org.jetbrains.kotlinx:kot org-jetbrains-kotlinx-kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" } org-jetbrains-kotlinx-kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "org-jetbrains-kotlinx" } org-jetbrains-kotlinx-kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test" } +org-jetbrains-kotlinx-kotlinx-serialization-json = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1" org-robolectric = "org.robolectric:robolectric:4.13" #se-eelde-toggles-toggles-core = { module = "se.eelde.toggles:toggles-core" } #se-eelde-toggles-toggles-flow = { module = "se.eelde.toggles:toggles-flow" } @@ -113,3 +114,4 @@ nl-littlerobots-version-catalog-update = "nl.littlerobots.version-catalog-update org-jetbrains-dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } org-jetbrains-kotlinx-binary-compatibility-validator = "org.jetbrains.kotlinx.binary-compatibility-validator:0.16.3" se-premex-gross = "se.premex.gross:0.4.2" +org-jetbrains-kotlin-plugin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin"} diff --git a/modules/applications/build.gradle.kts b/modules/applications/build.gradle.kts index 4e8de7c3..f53e6f09 100644 --- a/modules/applications/build.gradle.kts +++ b/modules/applications/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { implementation(projects.modules.database) implementation(libs.androidx.core.core.ktx) implementation(libs.androidx.appcompat) + implementation(projects.modules.routes) implementation(libs.androidx.navigation.navigation.compose) implementation(libs.androidx.hilt.hilt.navigation.compose) implementation(libs.androidx.compose.runtime) diff --git a/modules/applications/src/main/java/se/eelde/toggles/applications/ApplicationEntry.kt b/modules/applications/src/main/java/se/eelde/toggles/applications/ApplicationEntry.kt index 3b705497..7641a835 100644 --- a/modules/applications/src/main/java/se/eelde/toggles/applications/ApplicationEntry.kt +++ b/modules/applications/src/main/java/se/eelde/toggles/applications/ApplicationEntry.kt @@ -37,6 +37,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import kotlinx.coroutines.launch +import se.eelde.toggles.routes.Applications @OptIn(ExperimentalMaterial3Api::class) fun NavGraphBuilder.applicationNavigations( @@ -45,7 +46,7 @@ fun NavGraphBuilder.applicationNavigations( navigateToOss: () -> Unit, navigateToHelp: () -> Unit, ) { - composable("applications") { + composable { val viewModel = hiltViewModel() val viewState = viewModel.state.collectAsState() diff --git a/modules/configurations/build.gradle.kts b/modules/configurations/build.gradle.kts index d56019e5..d78c6596 100644 --- a/modules/configurations/build.gradle.kts +++ b/modules/configurations/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { implementation(libs.androidx.core.core.ktx) implementation(libs.androidx.lifecycle.lifecycle.runtime.compose) implementation(libs.androidx.appcompat) + implementation(projects.modules.routes) implementation(libs.androidx.navigation.navigation.compose) implementation(libs.androidx.hilt.hilt.navigation.compose) implementation(libs.androidx.compose.runtime) diff --git a/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt b/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt index bc812215..ac76baf5 100644 --- a/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt +++ b/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationViewModel.kt @@ -7,6 +7,9 @@ import android.text.TextUtils import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -42,16 +45,20 @@ internal sealed class PartialViewState { } @OptIn(ExperimentalCoroutinesApi::class) -@HiltViewModel +@HiltViewModel(assistedFactory = ConfigurationViewModel.Factory::class) @Suppress("StaticFieldLeak") -class ConfigurationViewModel @Inject internal constructor( +class ConfigurationViewModel @AssistedInject internal constructor( @ApplicationContext private val context: Context, private val applicationDao: TogglesApplicationDao, configurationDao: TogglesConfigurationDao, scopeDao: TogglesScopeDao, val savedStateHandle: SavedStateHandle, + @Assisted val applicationId: Long, ) : ViewModel() { - private val applicationId: Long = savedStateHandle.get("applicationId")!! + @AssistedFactory + interface Factory { + fun create(applicationId: Long): ConfigurationViewModel + } private val _state = MutableStateFlow(reduce(ViewState(), PartialViewState.Empty)) @@ -107,6 +114,7 @@ class ConfigurationViewModel @Inject internal constructor( is PartialViewState.Configurations -> viewState.copy( configurations = partialViewState.configurations ) + PartialViewState.Empty -> viewState is PartialViewState.DefaultScope -> viewState.copy(defaultScope = partialViewState.scope) is PartialViewState.SelectedScope -> viewState.copy(selectedScope = partialViewState.scope) diff --git a/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationsEntry.kt b/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationsEntry.kt index 7665b9a7..a7af114d 100644 --- a/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationsEntry.kt +++ b/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationsEntry.kt @@ -32,9 +32,9 @@ import androidx.compose.ui.Modifier import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavType import androidx.navigation.compose.composable -import androidx.navigation.navArgument +import androidx.navigation.toRoute +import se.eelde.toggles.routes.Configurations @Suppress("LongMethod", "LongParameterList") @OptIn(ExperimentalMaterial3Api::class) @@ -46,11 +46,14 @@ fun NavGraphBuilder.configurationsNavigations( navigateToScopeView: (Long) -> Unit, back: () -> Unit, ) { - composable( - "configurations/{applicationId}", - arguments = listOf(navArgument("applicationId") { type = NavType.LongType }) - ) { - val viewModel: ConfigurationViewModel = hiltViewModel() + composable { backStackEntry -> + val configurations: Configurations = backStackEntry.toRoute() + + val viewModel: ConfigurationViewModel = + hiltViewModel( + creationCallback = { factory -> factory.create(applicationId = configurations.applicationId) } + ) + val uiState = viewModel.state.collectAsStateWithLifecycle() val launcher = diff --git a/modules/routes/.gitignore b/modules/routes/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/modules/routes/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/modules/routes/OWNERSHIP.toml b/modules/routes/OWNERSHIP.toml new file mode 100644 index 00000000..9e1cee56 --- /dev/null +++ b/modules/routes/OWNERSHIP.toml @@ -0,0 +1,4 @@ +version = 1 + +[owner] +user = "@erikeelde" \ No newline at end of file diff --git a/modules/routes/build.gradle.kts b/modules/routes/build.gradle.kts new file mode 100644 index 00000000..7011d3ca --- /dev/null +++ b/modules/routes/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("toggles.android.module-conventions") + id("toggles.ownership-conventions") + alias(libs.plugins.org.jetbrains.kotlin.plugin.serialization) +} + +android { + namespace = "se.eelde.toggles.routes" +} + +dependencies { + implementation(libs.org.jetbrains.kotlinx.kotlinx.serialization.json) +} \ No newline at end of file diff --git a/modules/routes/lint-baseline.xml b/modules/routes/lint-baseline.xml new file mode 100644 index 00000000..f32fed49 --- /dev/null +++ b/modules/routes/lint-baseline.xml @@ -0,0 +1,4 @@ + + + + diff --git a/modules/routes/src/main/AndroidManifest.xml b/modules/routes/src/main/AndroidManifest.xml new file mode 100644 index 00000000..8072ee00 --- /dev/null +++ b/modules/routes/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/modules/routes/src/main/java/se/eelde/toggles/routes/Routes.kt b/modules/routes/src/main/java/se/eelde/toggles/routes/Routes.kt new file mode 100644 index 00000000..af07141b --- /dev/null +++ b/modules/routes/src/main/java/se/eelde/toggles/routes/Routes.kt @@ -0,0 +1,31 @@ +package se.eelde.toggles.routes + +import kotlinx.serialization.Serializable + +@Serializable +object Applications + +@Serializable +object Oss + +@Serializable +object Help + +@Serializable +data class Configurations(val applicationId: Long) + +@Serializable +object BooleanConfiguration + +@Serializable +object IntegerConfiguration + +@Serializable +object EnumConfiguration + +@Serializable +object StringConfiguration + +@Serializable +object Scope + diff --git a/settings.gradle.kts b/settings.gradle.kts index 2141f0f3..4fb19046 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -81,6 +81,7 @@ include( ":modules:configurations", ":modules:oss", ":modules:help", + ":modules:routes", ":modules:stringconfiguration", ":modules:booleanconfiguration", ":modules:integerconfiguration", diff --git a/toggles-app/build.gradle.kts b/toggles-app/build.gradle.kts index 7c7303a6..8073e3ed 100644 --- a/toggles-app/build.gradle.kts +++ b/toggles-app/build.gradle.kts @@ -148,7 +148,7 @@ dependencies { implementation(libs.androidx.hilt.hilt.navigation.compose) implementation(libs.androidx.navigation.navigation.compose) implementation(libs.androidx.startup.startup.runtime) - + implementation(projects.modules.routes) implementation(libs.com.google.dagger.hilt.android) ksp(libs.com.google.dagger.hilt.android.compiler) ksp(libs.androidx.hilt.hilt.compiler) diff --git a/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt b/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt index b78a1c8d..40920e2d 100644 --- a/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt +++ b/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt @@ -32,8 +32,13 @@ import se.eelde.toggles.enumconfiguration.EnumValueView import se.eelde.toggles.help.HelpView import se.eelde.toggles.integerconfiguration.IntegerValueView import se.eelde.toggles.oss.OssView +import se.eelde.toggles.routes.Applications +import se.eelde.toggles.routes.Configurations +import se.eelde.toggles.routes.Help +import se.eelde.toggles.routes.Oss import se.eelde.toggles.stringconfiguration.StringValueView + @OptIn(ExperimentalMaterial3Api::class) @Suppress("LongMethod") @Composable @@ -43,16 +48,16 @@ fun Navigation( ) { NavHost( navController = navController, - startDestination = "applications", + startDestination = Applications, modifier = modifier ) { applicationNavigations( navigateToConfigurations = { applicationId -> - navController.navigate("configurations/$applicationId") + navController.navigate(Configurations(applicationId)) }, - navigateToApplications = { navController.navigate("applications") }, - navigateToOss = { navController.navigate("oss") }, - navigateToHelp = { navController.navigate("help") }, + navigateToApplications = { navController.navigate(Applications) }, + navigateToOss = { navController.navigate(Oss) }, + navigateToHelp = { navController.navigate(Help) }, ) configurationsNavigations( navigateToBooleanConfiguration = { scopeId: Long, configurationId: Long -> @@ -113,9 +118,7 @@ fun Navigation( ) { EnumValueView { navController.popBackStack() } } - composable( - "oss", - ) { + composable { Scaffold( topBar = { TopAppBar( @@ -135,9 +138,7 @@ fun Navigation( OssView(modifier = Modifier.padding(paddingValues)) } } - composable( - "help", - ) { + composable { HelpView { navController.popBackStack() } } } From 76cf46c5f89e8ecbcbcaf671df7a9eba9856490e Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Mon, 16 Sep 2024 19:10:48 +0200 Subject: [PATCH 11/24] Start using typesafe navigation in StringConfiguration --- modules/booleanconfiguration/build.gradle.kts | 1 + .../configurations/ConfigurationsEntry.kt | 11 +- modules/enumconfiguration/build.gradle.kts | 1 + modules/integerconfiguration/build.gradle.kts | 1 + .../java/se/eelde/toggles/routes/Routes.kt | 10 +- modules/stringconfiguration/build.gradle.kts | 1 + .../stringconfiguration/StringValueView.kt | 123 ++++++++++-------- .../StringValueViewModel.kt | 32 +++-- .../java/se/eelde/toggles/MainActivity.kt | 21 +-- 9 files changed, 117 insertions(+), 84 deletions(-) diff --git a/modules/booleanconfiguration/build.gradle.kts b/modules/booleanconfiguration/build.gradle.kts index f77927d4..fac5cf1d 100644 --- a/modules/booleanconfiguration/build.gradle.kts +++ b/modules/booleanconfiguration/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { implementation(projects.modules.composeTheme) implementation(projects.modules.database) implementation(projects.modules.provider) + implementation(projects.modules.routes) implementation(libs.androidx.core.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.androidx.navigation.navigation.compose) diff --git a/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationsEntry.kt b/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationsEntry.kt index a7af114d..7f86da13 100644 --- a/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationsEntry.kt +++ b/modules/configurations/src/main/java/se/eelde/toggles/configurations/ConfigurationsEntry.kt @@ -7,13 +7,13 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.automirrored.outlined.List import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.outlined.Cyclone import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Info -import androidx.compose.material.icons.outlined.List import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api @@ -94,7 +94,10 @@ fun NavGraphBuilder.configurationsNavigations( navigationIcon = { IconButton(onClick = { back() }) { - Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = null) + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = null + ) } }, actions = { @@ -141,7 +144,7 @@ fun NavGraphBuilder.configurationsNavigations( onClick = { navigateToScopeView(uiState.value.application!!.id) }, leadingIcon = { Icon( - imageVector = Icons.Outlined.List, + imageVector = Icons.AutoMirrored.Outlined.List, contentDescription = null ) } diff --git a/modules/enumconfiguration/build.gradle.kts b/modules/enumconfiguration/build.gradle.kts index 815e0ebe..91f2cc1f 100644 --- a/modules/enumconfiguration/build.gradle.kts +++ b/modules/enumconfiguration/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { implementation(projects.modules.composeTheme) implementation(projects.modules.database) implementation(projects.modules.provider) + implementation(projects.modules.routes) implementation(libs.androidx.core.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.androidx.navigation.navigation.compose) diff --git a/modules/integerconfiguration/build.gradle.kts b/modules/integerconfiguration/build.gradle.kts index 68723e79..0e491618 100644 --- a/modules/integerconfiguration/build.gradle.kts +++ b/modules/integerconfiguration/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { implementation(projects.modules.composeTheme) implementation(projects.modules.database) implementation(projects.modules.provider) + implementation(projects.modules.routes) implementation(libs.androidx.core.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.androidx.navigation.navigation.compose) diff --git a/modules/routes/src/main/java/se/eelde/toggles/routes/Routes.kt b/modules/routes/src/main/java/se/eelde/toggles/routes/Routes.kt index af07141b..8a60dc8b 100644 --- a/modules/routes/src/main/java/se/eelde/toggles/routes/Routes.kt +++ b/modules/routes/src/main/java/se/eelde/toggles/routes/Routes.kt @@ -15,17 +15,17 @@ object Help data class Configurations(val applicationId: Long) @Serializable -object BooleanConfiguration +data class BooleanConfiguration(val configurationId: Long, val scopeId: Long) @Serializable -object IntegerConfiguration +data class IntegerConfiguration(val configurationId: Long, val scopeId: Long) @Serializable -object EnumConfiguration +data class EnumConfiguration(val configurationId: Long, val scopeId: Long) @Serializable -object StringConfiguration +data class StringConfiguration(val configurationId: Long, val scopeId: Long) @Serializable -object Scope +data class Scope(val applicationId: Long) diff --git a/modules/stringconfiguration/build.gradle.kts b/modules/stringconfiguration/build.gradle.kts index 107e0dc3..380b048e 100644 --- a/modules/stringconfiguration/build.gradle.kts +++ b/modules/stringconfiguration/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { implementation(projects.modules.composeTheme) implementation(projects.modules.database) implementation(projects.modules.provider) + implementation(projects.modules.routes) implementation(libs.androidx.core.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.androidx.navigation.navigation.compose) diff --git a/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueView.kt b/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueView.kt index 1fc72214..16dc6e94 100644 --- a/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueView.kt +++ b/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueView.kt @@ -1,12 +1,13 @@ package se.eelde.toggles.stringconfiguration +import android.annotation.SuppressLint import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -27,10 +28,11 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.launch import se.eelde.toggles.composetheme.TogglesTheme +import se.eelde.toggles.routes.StringConfiguration @Preview @Composable -fun StringValueViewPreview() { +private fun StringValueViewPreview() { TogglesTheme { StringValueView( state = ViewState(title = "The title", stringValue = "This is value"), @@ -42,23 +44,52 @@ fun StringValueViewPreview() { } } -@OptIn(ExperimentalMaterial3Api::class) +@SuppressLint("ComposeViewModelInjection") @Composable fun StringValueView( - modifier: Modifier = Modifier, - viewModel: FragmentStringValueViewModel = hiltViewModel(), + stringConfiguration: StringConfiguration, back: () -> Unit, ) { + val viewModel: StringValueViewModel = + hiltViewModel( + creationCallback = { factory -> + factory.create(stringConfiguration) + } + ) + val viewState by viewModel.state.collectAsStateWithLifecycle() + + val scope = rememberCoroutineScope() + + StringValueView( + state = viewState, + setStringValue = { viewModel.setStringValue(it) }, + save = { scope.launch { viewModel.saveClick() } }, + revert = { viewModel.revertClick() }, + popBackStack = back, + ) +} + +@Composable +@OptIn(ExperimentalMaterial3Api::class) +@Suppress("LongParameterList") +internal fun StringValueView( + state: ViewState, + setStringValue: (String) -> Unit, + save: suspend () -> Unit, + revert: suspend () -> Unit, + popBackStack: () -> Unit, + modifier: Modifier = Modifier, +) { Scaffold( topBar = { TopAppBar( title = { Text("String configuration") }, navigationIcon = { - IconButton(onClick = { back() }) { + IconButton(onClick = { popBackStack() }) { Icon( - imageVector = Icons.Filled.ArrowBack, + imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null ) } @@ -66,59 +97,39 @@ fun StringValueView( ) }, ) { paddingValues -> - StringValueView( - state = viewState, - popBackStack = { back() }, - revert = { viewModel.revertClick() }, - save = { viewModel.saveClick() }, - setStringValue = { viewModel.setStringValue(it) }, - modifier = modifier.padding(paddingValues), - ) - } -} - -@Composable -@Suppress("LongParameterList") -internal fun StringValueView( - state: ViewState, - setStringValue: (String) -> Unit, - save: suspend () -> Unit, - revert: suspend () -> Unit, - popBackStack: () -> Unit, - modifier: Modifier = Modifier, -) { - val scope = rememberCoroutineScope() + val scope = rememberCoroutineScope() - Surface(modifier = modifier.padding(16.dp)) { - Column { - Text( - modifier = Modifier.padding(8.dp), - style = MaterialTheme.typography.headlineMedium, - text = state.title ?: "" - ) - OutlinedTextField( - modifier = Modifier - .fillMaxWidth(), - value = state.stringValue ?: "", - onValueChange = { setStringValue(it) }, - ) - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { - Button(modifier = Modifier.padding(8.dp), onClick = { - scope.launch { - revert() - popBackStack() + Surface(modifier = modifier.padding(paddingValues)) { + Column { + Text( + modifier = Modifier.padding(8.dp), + style = MaterialTheme.typography.headlineMedium, + text = state.title ?: "" + ) + OutlinedTextField( + modifier = Modifier + .fillMaxWidth(), + value = state.stringValue ?: "", + onValueChange = { setStringValue(it) }, + ) + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { + Button(modifier = Modifier.padding(8.dp), onClick = { + scope.launch { + revert() + popBackStack() + } + }) { + Text("Revert") } - }) { - Text("Revert") - } - Button(modifier = Modifier.padding(8.dp), onClick = { - scope.launch { - save() - popBackStack() + Button(modifier = Modifier.padding(8.dp), onClick = { + scope.launch { + save() + popBackStack() + } + }) { + Text("Save") } - }) { - Text("Save") } } } diff --git a/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt b/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt index d7aed4b8..f2399287 100644 --- a/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt +++ b/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueViewModel.kt @@ -1,9 +1,11 @@ package se.eelde.toggles.stringconfiguration import android.app.Application -import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -16,8 +18,8 @@ import se.eelde.toggles.database.WrenchConfigurationValue import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.provider.notifyUpdate +import se.eelde.toggles.routes.StringConfiguration import java.util.Date -import javax.inject.Inject data class ViewState( val title: String? = null, @@ -34,23 +36,29 @@ private sealed class PartialViewState { object Reverting : PartialViewState() } -@HiltViewModel -class FragmentStringValueViewModel -@Inject internal constructor( - savedStateHandle: SavedStateHandle, +@HiltViewModel(assistedFactory = StringValueViewModel.Factory::class) +class StringValueViewModel +@AssistedInject internal constructor( private val application: Application, private val configurationDao: TogglesConfigurationDao, - private val configurationValueDao: TogglesConfigurationValueDao + private val configurationValueDao: TogglesConfigurationValueDao, + @Assisted stringConfiguration: StringConfiguration ) : ViewModel() { + @AssistedFactory + interface Factory { + fun create( + stringConfiguration: StringConfiguration + ): StringValueViewModel + } + + private val configurationId = stringConfiguration.configurationId + private val scopeId = stringConfiguration.scopeId private val _state = MutableStateFlow(reduce(ViewState(), PartialViewState.Empty)) val state: StateFlow get() = _state - private val configurationId: Long = savedStateHandle.get("configurationId")!! - private val scopeId: Long = savedStateHandle.get("scopeId")!! - private var selectedConfigurationValue: WrenchConfigurationValue? = null init { @@ -78,15 +86,19 @@ class FragmentStringValueViewModel is PartialViewState.NewConfiguration -> { previousState.copy(title = partialViewState.title) } + is PartialViewState.Empty -> { previousState } + is PartialViewState.Saving -> { previousState.copy(saving = true) } + is PartialViewState.Reverting -> { previousState.copy(reverting = true) } + is PartialViewState.NewConfigurationValue -> { previousState.copy(stringValue = partialViewState.value) } diff --git a/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt b/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt index 40920e2d..b74b9afb 100644 --- a/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt +++ b/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt @@ -22,6 +22,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument +import androidx.navigation.toRoute import dagger.hilt.android.AndroidEntryPoint import se.eelde.toggles.applications.applicationNavigations import se.eelde.toggles.booleanconfiguration.BooleanValueView @@ -36,6 +37,7 @@ import se.eelde.toggles.routes.Applications import se.eelde.toggles.routes.Configurations import se.eelde.toggles.routes.Help import se.eelde.toggles.routes.Oss +import se.eelde.toggles.routes.StringConfiguration import se.eelde.toggles.stringconfiguration.StringValueView @@ -67,7 +69,12 @@ fun Navigation( navController.navigate("configuration/$configurationId/$scopeId/integer") }, navigateToStringConfiguration = { scopeId: Long, configurationId: Long -> - navController.navigate("configuration/$configurationId/$scopeId/string") + navController.navigate( + StringConfiguration( + configurationId = configurationId, + scopeId = scopeId + ) + ) }, navigateToEnumConfiguration = { scopeId: Long, configurationId: Long -> navController.navigate("configuration/$configurationId/$scopeId/enum") @@ -100,14 +107,10 @@ fun Navigation( ) { IntegerValueView { navController.popBackStack() } } - composable( - "configuration/{configurationId}/{scopeId}/string", - arguments = listOf( - navArgument("configurationId") { type = NavType.LongType }, - navArgument("scopeId") { type = NavType.LongType } - ) - ) { - StringValueView { navController.popBackStack() } + composable { backStackEntry -> + val stringConfiguration: StringConfiguration = backStackEntry.toRoute() + + StringValueView(stringConfiguration) { navController.popBackStack() } } composable( "configuration/{configurationId}/{scopeId}/enum", From bf96e08a8680729d70a802d8917884cebe8b8710 Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Mon, 16 Sep 2024 19:23:05 +0200 Subject: [PATCH 12/24] Start using typesafe navigation in BooleanConfiguration --- .../booleanconfiguration/BooleanValueView.kt | 126 ++++++++++-------- .../BooleanValueViewModel.kt | 27 ++-- .../stringconfiguration/StringValueView.kt | 10 +- .../java/se/eelde/toggles/MainActivity.kt | 15 +-- 4 files changed, 100 insertions(+), 78 deletions(-) diff --git a/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueView.kt b/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueView.kt index a94a76e9..6f92aa4f 100644 --- a/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueView.kt +++ b/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueView.kt @@ -1,5 +1,6 @@ package se.eelde.toggles.booleanconfiguration +import android.annotation.SuppressLint import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -26,22 +27,53 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.launch +import se.eelde.toggles.routes.BooleanConfiguration +import se.eelde.toggles.routes.StringConfiguration -@OptIn(ExperimentalMaterial3Api::class) +@SuppressLint("ComposeViewModelInjection") @Composable fun BooleanValueView( - modifier: Modifier = Modifier, - viewModel: FragmentBooleanValueViewModel = hiltViewModel(), + booleanConfiguration: BooleanConfiguration, back: () -> Unit, ) { + val viewModel: BooleanValueViewModel = + hiltViewModel( + creationCallback = { factory -> + factory.create(booleanConfiguration) + } + ) + val viewState by viewModel.state.collectAsStateWithLifecycle() + + val scope = rememberCoroutineScope() + + BooleanValueView( + viewState = viewState, + save = { scope.launch { viewModel.saveClick() } }, + revert = { scope.launch { viewModel.revertClick() } }, + checkedChanged = { viewModel.checkedChanged(it) }, + popBackStack = back, + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +@Suppress("LongParameterList") +fun BooleanValueView( + viewState: ViewState, + save: () -> Unit, + revert: () -> Unit, + checkedChanged: (Boolean) -> Unit, + popBackStack: () -> Unit, + modifier: Modifier = Modifier, +) { Scaffold( topBar = { TopAppBar( title = { Text("Boolean configuration") }, navigationIcon = { - IconButton(onClick = { back() }) { + IconButton(onClick = { popBackStack() }) { Icon( imageVector = Icons.Filled.ArrowBack, contentDescription = null @@ -51,63 +83,47 @@ fun BooleanValueView( ) }, ) { paddingValues -> - BooleanValueView( - uiState = viewState, - popBackStack = { back() }, - revert = { viewModel.revertClick() }, - save = { viewModel.saveClick() }, - setBooleanValue = { viewModel.checkedChanged(it) }, - modifier = modifier.padding(paddingValues), - ) - } -} - -@Composable -@Suppress("LongParameterList") -internal fun BooleanValueView( - uiState: ViewState, - popBackStack: () -> Unit, - setBooleanValue: (Boolean) -> Unit, - revert: suspend () -> Unit, - save: suspend () -> Unit, - modifier: Modifier = Modifier, -) { - val scope = rememberCoroutineScope() - - Surface(modifier = modifier.padding(16.dp)) { - Column { - Text( - modifier = Modifier.padding(8.dp), - style = MaterialTheme.typography.headlineMedium, - text = uiState.title ?: "" - ) + val scope = rememberCoroutineScope() + Surface(modifier = modifier + .padding(paddingValues) + .padding(16.dp)) { + Column { + Text( + modifier = Modifier.padding(8.dp), + style = MaterialTheme.typography.headlineMedium, + text = viewState.title ?: "" + ) - Switch( - modifier = Modifier - .padding(8.dp) - .align(End), - checked = uiState.checked ?: false, - onCheckedChange = { setBooleanValue(it) } - ) - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { - Button(modifier = Modifier.padding(8.dp), onClick = { - scope.launch { - revert() - popBackStack() + Switch( + modifier = Modifier + .padding(8.dp) + .align(End), + checked = viewState.checked ?: false, + onCheckedChange = { + checkedChanged(it) + } + ) + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { + Button(modifier = Modifier.padding(8.dp), onClick = { + scope.launch { + revert() + popBackStack() + } + }) { + Text("Revert") } - }) { - Text("Revert") - } - Button(modifier = Modifier.padding(8.dp), onClick = { - scope.launch { - save() - popBackStack() + Button(modifier = Modifier.padding(8.dp), onClick = { + scope.launch { + save() + popBackStack() + } + }) { + Text("Save") } - }) { - Text("Save") } } } } } + diff --git a/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt b/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt index 9389741d..a3e2a9e3 100644 --- a/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt +++ b/modules/booleanconfiguration/src/main/java/se/eelde/toggles/booleanconfiguration/BooleanValueViewModel.kt @@ -1,9 +1,11 @@ package se.eelde.toggles.booleanconfiguration import android.app.Application -import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -16,8 +18,8 @@ import se.eelde.toggles.database.WrenchConfigurationValue import se.eelde.toggles.database.dao.application.TogglesConfigurationDao import se.eelde.toggles.database.dao.application.TogglesConfigurationValueDao import se.eelde.toggles.provider.notifyUpdate +import se.eelde.toggles.routes.BooleanConfiguration import java.util.Date -import javax.inject.Inject data class ViewState( val title: String? = null, @@ -34,22 +36,29 @@ private sealed class PartialViewState { object Reverting : PartialViewState() } -@HiltViewModel -class FragmentBooleanValueViewModel @Inject internal constructor( - savedStateHandle: SavedStateHandle, +@HiltViewModel(assistedFactory = BooleanValueViewModel.Factory::class) +class BooleanValueViewModel @AssistedInject internal constructor( private val application: Application, private val configurationDao: TogglesConfigurationDao, - private val configurationValueDao: TogglesConfigurationValueDao + private val configurationValueDao: TogglesConfigurationValueDao, + @Assisted booleanConfiguration: BooleanConfiguration ) : ViewModel() { + @AssistedFactory + interface Factory { + fun create( + booleanConfiguration: BooleanConfiguration + ): BooleanValueViewModel + } + + private val configurationId = booleanConfiguration.configurationId + private val scopeId = booleanConfiguration.scopeId + private val _state = MutableStateFlow(reduce(ViewState(), PartialViewState.Empty)) val state: StateFlow get() = _state - private val configurationId: Long = savedStateHandle.get("configurationId")!! - private val scopeId: Long = savedStateHandle.get("scopeId")!! - private var selectedConfigurationValue: WrenchConfigurationValue? = null init { diff --git a/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueView.kt b/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueView.kt index 16dc6e94..c735d796 100644 --- a/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueView.kt +++ b/modules/stringconfiguration/src/main/java/se/eelde/toggles/stringconfiguration/StringValueView.kt @@ -35,7 +35,7 @@ import se.eelde.toggles.routes.StringConfiguration private fun StringValueViewPreview() { TogglesTheme { StringValueView( - state = ViewState(title = "The title", stringValue = "This is value"), + viewState = ViewState(title = "The title", stringValue = "This is value"), setStringValue = {}, save = {}, revert = {}, @@ -62,7 +62,7 @@ fun StringValueView( val scope = rememberCoroutineScope() StringValueView( - state = viewState, + viewState = viewState, setStringValue = { viewModel.setStringValue(it) }, save = { scope.launch { viewModel.saveClick() } }, revert = { viewModel.revertClick() }, @@ -74,7 +74,7 @@ fun StringValueView( @OptIn(ExperimentalMaterial3Api::class) @Suppress("LongParameterList") internal fun StringValueView( - state: ViewState, + viewState: ViewState, setStringValue: (String) -> Unit, save: suspend () -> Unit, revert: suspend () -> Unit, @@ -104,12 +104,12 @@ internal fun StringValueView( Text( modifier = Modifier.padding(8.dp), style = MaterialTheme.typography.headlineMedium, - text = state.title ?: "" + text = viewState.title ?: "" ) OutlinedTextField( modifier = Modifier .fillMaxWidth(), - value = state.stringValue ?: "", + value = viewState.stringValue ?: "", onValueChange = { setStringValue(it) }, ) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { diff --git a/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt b/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt index b74b9afb..bf3c679b 100644 --- a/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt +++ b/toggles-app/src/main/java/se/eelde/toggles/MainActivity.kt @@ -34,6 +34,7 @@ import se.eelde.toggles.help.HelpView import se.eelde.toggles.integerconfiguration.IntegerValueView import se.eelde.toggles.oss.OssView import se.eelde.toggles.routes.Applications +import se.eelde.toggles.routes.BooleanConfiguration import se.eelde.toggles.routes.Configurations import se.eelde.toggles.routes.Help import se.eelde.toggles.routes.Oss @@ -63,7 +64,7 @@ fun Navigation( ) configurationsNavigations( navigateToBooleanConfiguration = { scopeId: Long, configurationId: Long -> - navController.navigate("configuration/$configurationId/$scopeId/boolean") + navController.navigate(BooleanConfiguration(configurationId, scopeId)) }, navigateToIntegerConfiguration = { scopeId: Long, configurationId: Long -> navController.navigate("configuration/$configurationId/$scopeId/integer") @@ -83,14 +84,10 @@ fun Navigation( navController.navigate("scopes/$applicationId") } ) { navController.popBackStack() } - composable( - "configuration/{configurationId}/{scopeId}/boolean", - arguments = listOf( - navArgument("configurationId") { type = NavType.LongType }, - navArgument("scopeId") { type = NavType.LongType } - ) - ) { - BooleanValueView { navController.popBackStack() } + composable { backStackEntry -> + val booleanConfiguration: BooleanConfiguration = backStackEntry.toRoute() + + BooleanValueView(booleanConfiguration) { navController.popBackStack() } } composable( "scopes/{applicationId}", From c13da79f2a0af0a852cddb2b6aedd8aed5c40071 Mon Sep 17 00:00:00 2001 From: Erik Eelde Date: Mon, 16 Sep 2024 19:24:51 +0200 Subject: [PATCH 13/24] Fix lint issue --- toggles-app/src/main/res/values-v31/styles.xml | 6 ++++++ toggles-app/src/main/res/values/styles.xml | 5 ++--- toggles-sample/src/main/res/values-v31/styles.xml | 7 +++++++ toggles-sample/src/main/res/values/styles.xml | 5 ++--- 4 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 toggles-app/src/main/res/values-v31/styles.xml create mode 100644 toggles-sample/src/main/res/values-v31/styles.xml diff --git a/toggles-app/src/main/res/values-v31/styles.xml b/toggles-app/src/main/res/values-v31/styles.xml new file mode 100644 index 00000000..0dc40ac5 --- /dev/null +++ b/toggles-app/src/main/res/values-v31/styles.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/toggles-app/src/main/res/values/styles.xml b/toggles-app/src/main/res/values/styles.xml index c453a356..c9ddd2f9 100644 --- a/toggles-app/src/main/res/values/styles.xml +++ b/toggles-app/src/main/res/values/styles.xml @@ -1,5 +1,4 @@ - + + + \ No newline at end of file diff --git a/toggles-sample/src/main/res/values/styles.xml b/toggles-sample/src/main/res/values/styles.xml index 66cad0b9..c9ddd2f9 100644 --- a/toggles-sample/src/main/res/values/styles.xml +++ b/toggles-sample/src/main/res/values/styles.xml @@ -1,5 +1,4 @@ - + +