diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 137ccdb922..522cd3257e 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -33,7 +33,6 @@ dependencies { implementation(deps.timber) /** Network */ - implementation(deps.krateSharedPref) implementation(deps.moshi) implementation(deps.moshi.kotlin) implementation(deps.okHttp) diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/AppPreferenceStorageModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/AppPreferenceStorageModule.kt deleted file mode 100644 index 2eefec13ed..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/di/AppPreferenceStorageModule.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.tangem.datasource.di - -import com.tangem.datasource.local.AppPreferenceStorage -import com.tangem.datasource.local.AppPreferenceStorageImpl -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal interface AppPreferenceStorageModule { - - @Binds - @Singleton - fun bindAppPreferenceStorage(appPreferenceStorageImpl: AppPreferenceStorageImpl): AppPreferenceStorage -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorage.kt b/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorage.kt deleted file mode 100644 index d56ca29fa0..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorage.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.datasource.local - -/** - * Application local storage - * -[REDACTED_AUTHOR] - */ -@Deprecated(message = "Use AppPreferencesStore", level = DeprecationLevel.WARNING) -interface AppPreferenceStorage { - - /** Json config with feature toggles 'ToggleName: String - Availability: Boolean' */ - var featureToggles: String -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorageImpl.kt b/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorageImpl.kt deleted file mode 100644 index 3ce9a8aca9..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorageImpl.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.datasource.local - -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext -import hu.autsoft.krate.SimpleKrate -import hu.autsoft.krate.default.withDefault -import hu.autsoft.krate.stringPref -import javax.inject.Inject -import javax.inject.Singleton - -/** - * Implementation of application local storage - * - * @param context application context - * -[REDACTED_AUTHOR] - */ -@Singleton -internal class AppPreferenceStorageImpl @Inject constructor( - @ApplicationContext context: Context, -) : SimpleKrate(context = context), AppPreferenceStorage { - - override var featureToggles: String by stringPref().withDefault("") -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index 02432a3d03..8295d9e9a9 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -48,6 +48,8 @@ object PreferencesKeys { val IS_WALLET_SWAP_PROMO_SHOW_KEY by lazy { booleanPreferencesKey(name = "isWalletSwapPromoShown") } val IS_TOKEN_SWAP_PROMO_SHOW_KEY by lazy { booleanPreferencesKey(name = "isTokenSwapPromoShown") } + + val FEATURE_TOGGLES_KEY by lazy { stringPreferencesKey(name = "featureToggles") } } /** Preferences keys set that should be migrated from "PreferencesDataSource" to a new DataStore */ diff --git a/core/featuretoggles/build.gradle.kts b/core/featuretoggles/build.gradle.kts index c5f9b07545..5354b55813 100644 --- a/core/featuretoggles/build.gradle.kts +++ b/core/featuretoggles/build.gradle.kts @@ -7,14 +7,22 @@ plugins { } dependencies { - implementation(project(":core:datasource")) + /** DI */ implementation(deps.hilt.android) kapt(deps.hilt.kapt) + + /** Local storages */ + implementation(deps.androidx.datastore) + + /** Other libraries */ implementation(deps.moshi) implementation(deps.moshi.kotlin) implementation(deps.timber) + /** Core modules */ + implementation(projects.core.datasource) + testImplementation(deps.test.coroutine) testImplementation(deps.test.junit) testImplementation(deps.test.mockk) diff --git a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/di/FeatureTogglesManagerModule.kt b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/di/FeatureTogglesManagerModule.kt index eadb4bf0fb..e67ca2a738 100644 --- a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/di/FeatureTogglesManagerModule.kt +++ b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/di/FeatureTogglesManagerModule.kt @@ -11,7 +11,7 @@ import com.tangem.core.featuretoggle.storage.LocalFeatureTogglesStorage import com.tangem.core.featuretoggle.version.DefaultVersionProvider import com.tangem.core.featuretoggles.BuildConfig import com.tangem.datasource.asset.AssetReader -import com.tangem.datasource.local.AppPreferenceStorage +import com.tangem.datasource.local.preferences.AppPreferencesStore import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -29,7 +29,7 @@ internal object FeatureTogglesManagerModule { fun provideFeatureTogglesManager( @ApplicationContext context: Context, assetReader: AssetReader, - appPreferenceStorage: AppPreferenceStorage, + appPreferencesStore: AppPreferencesStore, ): FeatureTogglesManager { val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build() val localFeatureTogglesStorage = LocalFeatureTogglesStorage( @@ -41,8 +41,7 @@ internal object FeatureTogglesManagerModule { return if (BuildConfig.TESTER_MENU_ENABLED) { DevFeatureTogglesManager( localFeatureTogglesStorage = localFeatureTogglesStorage, - appPreferenceStorage = appPreferenceStorage, - jsonAdapter = moshi.adapter(), + appPreferencesStore = appPreferencesStore, versionProvider = versionProvider, ) } else { diff --git a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManager.kt b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManager.kt index 12e0a496b6..9b29a65f66 100644 --- a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManager.kt +++ b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManager.kt @@ -1,25 +1,25 @@ package com.tangem.core.featuretoggle.manager import androidx.annotation.VisibleForTesting -import com.squareup.moshi.JsonAdapter import com.tangem.core.featuretoggle.storage.FeatureTogglesStorage import com.tangem.core.featuretoggle.utils.associateToggles import com.tangem.core.featuretoggle.version.VersionProvider -import com.tangem.datasource.local.AppPreferenceStorage +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull +import com.tangem.datasource.local.preferences.utils.storeObject import kotlin.properties.Delegates /** * Feature toggles manager implementation in DEV build * * @property localFeatureTogglesStorage local feature toggles storage - * @property appPreferenceStorage application local storage - * @property jsonAdapter adapter for parsing json + * @property appPreferencesStore application local store * @property versionProvider application version provider */ internal class DevFeatureTogglesManager( private val localFeatureTogglesStorage: FeatureTogglesStorage, - private val appPreferenceStorage: AppPreferenceStorage, - private val jsonAdapter: JsonAdapter>, + private val appPreferencesStore: AppPreferencesStore, private val versionProvider: VersionProvider, ) : MutableFeatureTogglesManager { @@ -28,11 +28,9 @@ internal class DevFeatureTogglesManager( override suspend fun init() { localFeatureTogglesStorage.init() - val savedFeatureToggles = if (appPreferenceStorage.featureToggles.isNotEmpty()) { - jsonAdapter.fromJson(appPreferenceStorage.featureToggles).orEmpty() - } else { - emptyMap() - } + val savedFeatureToggles = appPreferencesStore.getObjectSyncOrNull>( + key = PreferencesKeys.FEATURE_TOGGLES_KEY, + ) ?: emptyMap() featureTogglesMap = localFeatureTogglesStorage.featureToggles .associateToggles(currentVersion = versionProvider.get().orEmpty()) @@ -46,10 +44,10 @@ internal class DevFeatureTogglesManager( override fun getFeatureToggles(): Map = featureTogglesMap - override fun changeToggle(name: String, isEnabled: Boolean) { + override suspend fun changeToggle(name: String, isEnabled: Boolean) { featureTogglesMap[name] ?: return featureTogglesMap[name] = isEnabled - appPreferenceStorage.featureToggles = jsonAdapter.toJson(featureTogglesMap) + appPreferencesStore.storeObject(PreferencesKeys.FEATURE_TOGGLES_KEY, featureTogglesMap) } @VisibleForTesting(otherwise = VisibleForTesting.NONE) diff --git a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/MutableFeatureTogglesManager.kt b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/MutableFeatureTogglesManager.kt index abf6ff5b72..8d7a063c5d 100644 --- a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/MutableFeatureTogglesManager.kt +++ b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/MutableFeatureTogglesManager.kt @@ -11,5 +11,5 @@ interface MutableFeatureTogglesManager : FeatureTogglesManager { fun getFeatureToggles(): Map /** Change availability [isEnabled] of toggle with name [name] */ - fun changeToggle(name: String, isEnabled: Boolean) + suspend fun changeToggle(name: String, isEnabled: Boolean) } \ No newline at end of file diff --git a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/storage/FeatureToggle.kt b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/storage/FeatureToggle.kt index 69de7b76e7..b019bfc810 100644 --- a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/storage/FeatureToggle.kt +++ b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/storage/FeatureToggle.kt @@ -1,5 +1,8 @@ package com.tangem.core.featuretoggle.storage +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + /** * Data model with information about feature toggle * @@ -10,4 +13,8 @@ package com.tangem.core.featuretoggle.storage * [REDACTED_AUTHOR] */ -internal data class FeatureToggle(val name: String, val version: String) \ No newline at end of file +@JsonClass(generateAdapter = true) +internal data class FeatureToggle( + @Json(name = "name") val name: String, + @Json(name = "version") val version: String, +) \ No newline at end of file diff --git a/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManagerTest.kt b/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManagerTest.kt index 51ac267ee2..e0e8fbef2f 100644 --- a/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManagerTest.kt +++ b/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManagerTest.kt @@ -2,48 +2,42 @@ package com.tangem.core.featuretoggle.manager import android.annotation.SuppressLint import com.google.common.truth.Truth -import com.squareup.moshi.JsonAdapter import com.tangem.core.featuretoggle.storage.FeatureToggle import com.tangem.core.featuretoggle.storage.FeatureTogglesStorage import com.tangem.core.featuretoggle.utils.associateToggles import com.tangem.core.featuretoggle.version.VersionProvider -import com.tangem.datasource.local.AppPreferenceStorage -import io.mockk.Runs -import io.mockk.coEvery -import io.mockk.coVerifyOrder -import io.mockk.just -import io.mockk.mockk -import io.mockk.verifyAll -import io.mockk.verifyOrder -import kotlinx.coroutines.ExperimentalCoroutinesApi +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull +import com.tangem.datasource.local.preferences.utils.getSyncOrNull +import io.mockk.* import kotlinx.coroutines.test.runTest import org.junit.Test +import kotlin.collections.set /** [REDACTED_AUTHOR] */ -@OptIn(ExperimentalCoroutinesApi::class) @SuppressLint("CheckResult") internal class DevFeatureTogglesManagerTest { private val localFeatureTogglesStorage = mockk() - private val appPreferenceStorage = mockk(relaxed = true) - private val jsonAdapter = mockk>>() + private val appPreferenceStore = mockk(relaxed = true) private val versionProvider = mockk() private val manager = DevFeatureTogglesManager( localFeatureTogglesStorage = localFeatureTogglesStorage, - appPreferenceStorage = appPreferenceStorage, - jsonAdapter = jsonAdapter, + appPreferencesStore = appPreferenceStore, versionProvider = versionProvider, ) @Test fun `successfully initialize storage if shared prefs kept feature toggles`() = runTest { - val currentVersion = "1.0.0" + val currentVersion = "0.1.0" coEvery { localFeatureTogglesStorage.init() } just Runs - coEvery { appPreferenceStorage.featureToggles } returns savedFeatureToggles - coEvery { jsonAdapter.fromJson(savedFeatureToggles) } returns savedFeatureTogglesMap + coEvery { + appPreferenceStore.getObjectSyncOrNull>(PreferencesKeys.FEATURE_TOGGLES_KEY) + } returns savedFeatureTogglesMap coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { versionProvider.get() } returns currentVersion @@ -51,7 +45,6 @@ internal class DevFeatureTogglesManagerTest { coVerifyOrder { localFeatureTogglesStorage.init() - jsonAdapter.fromJson(savedFeatureToggles) versionProvider.get() } @@ -66,12 +59,12 @@ internal class DevFeatureTogglesManagerTest { @Test fun `successfully initialize storage if shared prefs kept empty list`() = runTest { - val currentVersion = "1.0.0" - val sharedPrefFeatureToggles = "[]" + val currentVersion = "0.1.0" coEvery { localFeatureTogglesStorage.init() } just Runs - coEvery { appPreferenceStorage.featureToggles } returns sharedPrefFeatureToggles - coEvery { jsonAdapter.fromJson(sharedPrefFeatureToggles) } returns savedFeatureTogglesMap + coEvery { + appPreferenceStore.getObjectSyncOrNull>(PreferencesKeys.FEATURE_TOGGLES_KEY) + } returns emptyMap() coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { versionProvider.get() } returns currentVersion @@ -79,7 +72,6 @@ internal class DevFeatureTogglesManagerTest { coVerifyOrder { localFeatureTogglesStorage.init() - jsonAdapter.fromJson(sharedPrefFeatureToggles) versionProvider.get() } @@ -94,10 +86,12 @@ internal class DevFeatureTogglesManagerTest { @Test fun `successfully initialize storage if shared prefs didn't keep feature toggles`() = runTest { - val currentVersion = "1.0.0" + val currentVersion = "0.1.0" coEvery { localFeatureTogglesStorage.init() } just Runs - coEvery { appPreferenceStorage.featureToggles } returns "" + coEvery { + appPreferenceStore.getObjectSyncOrNull>(PreferencesKeys.FEATURE_TOGGLES_KEY) + } returns null coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { versionProvider.get() } returns currentVersion @@ -107,7 +101,6 @@ internal class DevFeatureTogglesManagerTest { localFeatureTogglesStorage.init() versionProvider.get() } - verifyAll(inverse = true) { jsonAdapter.fromJson(any()) } val expected = localFeatureToggles .associateToggles(currentVersion) @@ -119,8 +112,7 @@ internal class DevFeatureTogglesManagerTest { @Test fun `successfully initialize storage if versionProvider returns null`() = runTest { coEvery { localFeatureTogglesStorage.init() } just Runs - coEvery { appPreferenceStorage.featureToggles } returns savedFeatureToggles - coEvery { jsonAdapter.fromJson(savedFeatureToggles) } returns savedFeatureTogglesMap + coEvery { appPreferenceStore.getSyncOrNull(PreferencesKeys.FEATURE_TOGGLES_KEY) } returns savedFeatureToggles coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { versionProvider.get() } returns null @@ -128,7 +120,6 @@ internal class DevFeatureTogglesManagerTest { coVerifyOrder { localFeatureTogglesStorage.init() - jsonAdapter.fromJson(savedFeatureToggles) versionProvider.get() } @@ -180,7 +171,7 @@ internal class DevFeatureTogglesManagerTest { } @Test - fun `change toggle that contains in map`() { + fun `change toggle that contains in map`() = runTest { val changeableToggleName = "INACTIVE_TEST_FEATURE_ENABLED" val resultMap = mutableMapOf( changeableToggleName to false, @@ -188,18 +179,16 @@ internal class DevFeatureTogglesManagerTest { ) manager.setFeatureToggles(resultMap) - coEvery { jsonAdapter.toJson(resultMap) } returns "" manager.changeToggle(changeableToggleName, true) resultMap[changeableToggleName] = true - verifyOrder { jsonAdapter.toJson(resultMap) } Truth.assertThat(manager.getFeatureToggles()).containsExactlyEntriesIn(resultMap) } @Test - fun `change toggle that doesn't contains in map`() { + fun `change toggle that doesn't contains in map`() = runTest { val resultMap = mutableMapOf( "INACTIVE_TEST_FEATURE_ENABLED" to false, "ACTIVE2_TEST_FEATURE_ENABLED" to false, @@ -209,8 +198,6 @@ internal class DevFeatureTogglesManagerTest { manager.changeToggle("FEATURE_TOGGLE", true) - verifyAll(inverse = true) { jsonAdapter.toJson(any()) } - Truth.assertThat(manager.getFeatureToggles()).containsExactlyEntriesIn(resultMap) } diff --git a/features/tester/impl/build.gradle.kts b/features/tester/impl/build.gradle.kts index 73011f1a76..9bc0022b59 100644 --- a/features/tester/impl/build.gradle.kts +++ b/features/tester/impl/build.gradle.kts @@ -27,17 +27,18 @@ dependencies { implementation(projects.domain.appTheme) implementation(projects.domain.appTheme.models) - /** Core modules */ - implementation(project(":core:featuretoggles")) - implementation(project(":core:ui")) - - /** Feature Apis */ - implementation(project(":features:tester:api")) - - /** Other modules */ - implementation(project(":libs:crypto")) - /** Other libraries */ implementation(deps.arrow.core) implementation(deps.timber) + + /** Core modules */ + implementation(projects.core.featuretoggles) + implementation(projects.core.ui) + implementation(projects.core.utils) + + /** Feature Apis */ + implementation(projects.features.tester.api) + + /** Other modules */ + implementation(projects.libs.crypto) } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/viewmodels/FeatureTogglesViewModel.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/viewmodels/FeatureTogglesViewModel.kt index 4c10030524..037946a145 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/viewmodels/FeatureTogglesViewModel.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/viewmodels/FeatureTogglesViewModel.kt @@ -4,24 +4,29 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.featuretoggle.manager.MutableFeatureTogglesManager import com.tangem.feature.tester.presentation.featuretoggles.models.TesterFeatureToggle import com.tangem.feature.tester.presentation.featuretoggles.state.FeatureTogglesContentState import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch import javax.inject.Inject /** * ViewModel for screen with list of feature toggles * * @property featureTogglesManager manager for getting information about the availability of feature toggles + * @property dispatchers coroutine dispatchers provider * [REDACTED_AUTHOR] */ @HiltViewModel internal class FeatureTogglesViewModel @Inject constructor( private val featureTogglesManager: FeatureTogglesManager, + private val dispatchers: CoroutineDispatcherProvider, ) : ViewModel() { /** Current ui state */ @@ -47,9 +52,11 @@ internal class FeatureTogglesViewModel @Inject constructor( } private fun onToggleValueChange(name: String, isEnabled: Boolean) { - mutableFeatureTogglesManager.changeToggle(name = name, isEnabled = isEnabled) + viewModelScope.launch(dispatchers.main) { + mutableFeatureTogglesManager.changeToggle(name = name, isEnabled = isEnabled) - uiState = uiState.copy(featureToggles = mutableFeatureTogglesManager.getTesterFeatureToggles()) + uiState = uiState.copy(featureToggles = mutableFeatureTogglesManager.getTesterFeatureToggles()) + } } private fun MutableFeatureTogglesManager.getTesterFeatureToggles(): List { diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 7616aa06f7..70d41d892b 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -53,7 +53,6 @@ googlePlayServicesWallet = "19.1.0" hilt = "2.44" hilt-navigation = "1.0.0" jodatime = "2.12.1" -krateSharedPref = "2.0.0" kotlin-immutable-collections = "0.3.5" kotsonGsonExt = "2.5.0" lottie = "3.4.0" @@ -206,7 +205,6 @@ hilt-core = { module = "com.google.dagger:hilt-core", version.ref = "hilt" } hilt-kapt = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" } hilt-navigation = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" } jodatime = { module = "joda-time:joda-time", version.ref = "jodatime" } -krateSharedPref = { module = "hu.autsoft:krate", version.ref = "krateSharedPref" } kotsonGson = { module = "com.github.salomonbrys.kotson:kotson", version.ref = "kotsonGsonExt" } lottie = { module = "com.airbnb.android:lottie", version.ref = "lottie" } material = { module = "com.google.android.material:material", version.ref = "googleMaterialComponent" }