From 9b81d26579b8b12583721f8a4d43378d3c5e9695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Lindstr=C3=B6m?= Date: Tue, 17 Dec 2024 15:08:44 +0100 Subject: [PATCH] Migrate from SharedPreferences to DataStore --- android/app/build.gradle.kts | 28 +++++-- .../net/mullvad/mullvadvpn/di/UiModule.kt | 26 ++++--- .../repository/PrivacyDisclaimerRepository.kt | 15 ---- .../repository/UserPreferencesMigration.kt | 30 ++++++++ .../repository/UserPreferencesRepository.kt | 32 ++++++++ .../repository/UserPreferencesSerializer.kt | 21 ++++++ .../net/mullvad/mullvadvpn/ui/MainActivity.kt | 14 ++-- .../viewmodel/PrivacyDisclaimerViewModel.kt | 7 +- .../mullvadvpn/viewmodel/SplashViewModel.kt | 6 +- android/app/src/main/proto/user_prefs.proto | 9 +++ android/gradle/libs.versions.toml | 14 ++-- android/gradle/verification-metadata.xml | 73 +++++++++++++++++++ android/lib/daemon-grpc/build.gradle.kts | 2 +- 13 files changed, 224 insertions(+), 53 deletions(-) delete mode 100644 android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/PrivacyDisclaimerRepository.kt create mode 100644 android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesMigration.kt create mode 100644 android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesRepository.kt create mode 100644 android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesSerializer.kt create mode 100644 android/app/src/main/proto/user_prefs.proto diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index cb5737e3ffbb..5fed7575d420 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -12,6 +12,7 @@ plugins { alias(libs.plugins.kotlin.parcelize) alias(libs.plugins.kotlin.ksp) alias(libs.plugins.compose) + alias(libs.plugins.protobuf.core) id(Dependencies.junit5AndroidPluginId) version Versions.junit5Plugin } @@ -55,11 +56,7 @@ android { } } - playConfigs { - register("playStagemoleRelease") { - enabled = true - } - } + playConfigs { register("playStagemoleRelease") { enabled = true } } androidResources { @Suppress("UnstableApiUsage") @@ -222,8 +219,7 @@ android { } val variantName = name - val capitalizedVariantName = - variantName.toString().capitalized() + val capitalizedVariantName = variantName.toString().capitalized() val artifactName = "MullvadVPN-${versionName}${artifactSuffix}" tasks.register("create${capitalizedVariantName}DistApk") { @@ -316,7 +312,8 @@ tasks.create("printVersion") { play { serviceAccountCredentials.set(file("$credentialsPath/play-api-key.json")) - // Disable for all flavors by default. Only specific flavors should be enabled using PlayConfigs. + // Disable for all flavors by default. Only specific flavors should be enabled using + // PlayConfigs. enabled = false // This property refers to the Publishing API (not git). commit = true @@ -326,6 +323,19 @@ play { userFraction = 1.0 } +protobuf { + protoc { artifact = libs.plugins.protobuf.protoc.get().toString() } + plugins { + create("java") { artifact = libs.plugins.grpc.protoc.gen.grpc.java.get().toString() } + } + generateProtoTasks { + all().forEach { + it.plugins { create("java") { option("lite") } } + it.builtins { create("kotlin") { option("lite") } } + } + } +} + dependencies { implementation(projects.lib.common) implementation(projects.lib.daemonGrpc) @@ -345,6 +355,7 @@ dependencies { implementation(libs.commons.validator) implementation(libs.androidx.activity.compose) + implementation(libs.androidx.datastore) implementation(libs.androidx.ktx) implementation(libs.androidx.coresplashscreen) implementation(libs.androidx.lifecycle.runtime) @@ -370,6 +381,7 @@ dependencies { implementation(libs.kotlin.reflect) implementation(libs.kotlin.stdlib) implementation(libs.kotlinx.coroutines.android) + implementation(libs.protobuf.kotlin.lite) // UI tooling implementation(libs.compose.ui.tooling.preview) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt index b111db10b207..650ee67eaa1b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt @@ -2,8 +2,9 @@ package net.mullvad.mullvadvpn.di import android.content.ComponentName import android.content.Context -import android.content.SharedPreferences import android.content.pm.PackageManager +import androidx.datastore.core.DataStore +import androidx.datastore.dataStore import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope import net.mullvad.mullvadvpn.BuildConfig @@ -20,7 +21,6 @@ import net.mullvad.mullvadvpn.repository.ChangelogRepository import net.mullvad.mullvadvpn.repository.CustomListsRepository import net.mullvad.mullvadvpn.repository.InAppNotificationController import net.mullvad.mullvadvpn.repository.NewDeviceRepository -import net.mullvad.mullvadvpn.repository.PrivacyDisclaimerRepository import net.mullvad.mullvadvpn.repository.ProblemReportRepository import net.mullvad.mullvadvpn.repository.RelayListFilterRepository import net.mullvad.mullvadvpn.repository.RelayListRepository @@ -28,6 +28,10 @@ import net.mullvad.mullvadvpn.repository.RelayOverridesRepository import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.repository.SplashCompleteRepository import net.mullvad.mullvadvpn.repository.SplitTunnelingRepository +import net.mullvad.mullvadvpn.repository.UserPreferences +import net.mullvad.mullvadvpn.repository.UserPreferencesMigration +import net.mullvad.mullvadvpn.repository.UserPreferencesRepository +import net.mullvad.mullvadvpn.repository.UserPreferencesSerializer import net.mullvad.mullvadvpn.repository.WireguardConstraintsRepository import net.mullvad.mullvadvpn.ui.MainActivity import net.mullvad.mullvadvpn.ui.serviceconnection.AppVersionInfoRepository @@ -99,16 +103,13 @@ import net.mullvad.mullvadvpn.viewmodel.location.SearchLocationViewModel import net.mullvad.mullvadvpn.viewmodel.location.SelectLocationListViewModel import net.mullvad.mullvadvpn.viewmodel.location.SelectLocationViewModel import org.apache.commons.validator.routines.InetAddressValidator -import org.koin.android.ext.koin.androidApplication import org.koin.android.ext.koin.androidContext import org.koin.core.module.dsl.viewModel import org.koin.core.qualifier.named import org.koin.dsl.module val uiModule = module { - single(named(APP_PREFERENCES_NAME)) { - androidApplication().getSharedPreferences(APP_PREFERENCES_NAME, Context.MODE_PRIVATE) - } + single> { androidContext().userPreferencesStore } single { androidContext().packageManager } single(named(SELF_PACKAGE_NAME)) { androidContext().packageName } @@ -126,11 +127,7 @@ val uiModule = module { single { androidContext().contentResolver } single { ChangelogRepository(get()) } - single { - PrivacyDisclaimerRepository( - androidContext().getSharedPreferences(APP_PREFERENCES_NAME, Context.MODE_PRIVATE) - ) - } + single { UserPreferencesRepository(get()) } single { SettingsRepository(get()) } single { MullvadProblemReport(get()) } single { RelayOverridesRepository(get()) } @@ -272,3 +269,10 @@ val uiModule = module { const val SELF_PACKAGE_NAME = "SELF_PACKAGE_NAME" const val APP_PREFERENCES_NAME = "${BuildConfig.APPLICATION_ID}.app_preferences" const val BOOT_COMPLETED_RECEIVER_COMPONENT_NAME = "BOOT_COMPLETED_RECEIVER_COMPONENT_NAME" + +private val Context.userPreferencesStore: DataStore by + dataStore( + fileName = APP_PREFERENCES_NAME, + serializer = UserPreferencesSerializer, + produceMigrations = UserPreferencesMigration::migrations, + ) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/PrivacyDisclaimerRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/PrivacyDisclaimerRepository.kt deleted file mode 100644 index db1ad220e346..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/PrivacyDisclaimerRepository.kt +++ /dev/null @@ -1,15 +0,0 @@ -package net.mullvad.mullvadvpn.repository - -import android.content.SharedPreferences - -private const val IS_PRIVACY_DISCLOSURE_ACCEPTED_KEY = "is_privacy_disclosure_accepted" - -class PrivacyDisclaimerRepository(private val sharedPreferences: SharedPreferences) { - fun hasAcceptedPrivacyDisclosure(): Boolean { - return sharedPreferences.getBoolean(IS_PRIVACY_DISCLOSURE_ACCEPTED_KEY, false) - } - - fun setPrivacyDisclosureAccepted() { - sharedPreferences.edit().putBoolean(IS_PRIVACY_DISCLOSURE_ACCEPTED_KEY, true).apply() - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesMigration.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesMigration.kt new file mode 100644 index 000000000000..2066f93b5def --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesMigration.kt @@ -0,0 +1,30 @@ +package net.mullvad.mullvadvpn.repository + +import android.content.Context +import androidx.datastore.core.DataMigration +import androidx.datastore.migrations.SharedPreferencesMigration +import androidx.datastore.migrations.SharedPreferencesView +import net.mullvad.mullvadvpn.di.APP_PREFERENCES_NAME + +private const val IS_PRIVACY_DISCLOSURE_ACCEPTED_KEY_SHARED_PREF_KEY = + "is_privacy_disclosure_accepted" + +data object UserPreferencesMigration { + fun migrations(context: Context): List> = + listOf( + SharedPreferencesMigration(context, sharedPreferencesName = APP_PREFERENCES_NAME) { + sharedPrefs: SharedPreferencesView, + currentData: UserPreferences -> + if ( + sharedPrefs.getBoolean( + IS_PRIVACY_DISCLOSURE_ACCEPTED_KEY_SHARED_PREF_KEY, + false, + ) + ) { + currentData.toBuilder().setIsPrivacyDisclosureAccepted(true).build() + } else { + currentData + } + } + ) +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesRepository.kt new file mode 100644 index 000000000000..f3e6a72b64cf --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesRepository.kt @@ -0,0 +1,32 @@ +package net.mullvad.mullvadvpn.repository + +import androidx.datastore.core.DataStore +import co.touchlab.kermit.Logger +import java.io.IOException +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.first + +class UserPreferencesRepository(private val userPreferences: DataStore) { + + // Note: this should not be made into a StateFlow. See: + // https://developer.android.com/reference/kotlin/androidx/datastore/core/DataStore#data() + val preferencesFlow: Flow = + userPreferences.data.catch { exception -> + // dataStore.data throws an IOException when an error is encountered when reading data + if (exception is IOException) { + Logger.e("Error reading user preferences file, falling back to default.", exception) + emit(UserPreferences.getDefaultInstance()) + } else { + throw exception + } + } + + suspend fun preferences(): UserPreferences = preferencesFlow.first() + + suspend fun setPrivacyDisclosureAccepted() { + userPreferences.updateData { prefs -> + prefs.toBuilder().setIsPrivacyDisclosureAccepted(true).build() + } + } +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesSerializer.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesSerializer.kt new file mode 100644 index 000000000000..97348fd0cc01 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/UserPreferencesSerializer.kt @@ -0,0 +1,21 @@ +package net.mullvad.mullvadvpn.repository + +import androidx.datastore.core.CorruptionException +import androidx.datastore.core.Serializer +import com.google.protobuf.InvalidProtocolBufferException +import java.io.InputStream +import java.io.OutputStream + +object UserPreferencesSerializer : Serializer { + override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance() + + override suspend fun readFrom(input: InputStream): UserPreferences { + try { + return UserPreferences.parseFrom(input) + } catch (exception: InvalidProtocolBufferException) { + throw CorruptionException("Cannot read proto", exception) + } + } + + override suspend fun writeTo(t: UserPreferences, output: OutputStream) = t.writeTo(output) +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt index 4007b09ecd1f..76ec06d6cf6f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt @@ -33,8 +33,8 @@ import net.mullvad.mullvadvpn.lib.endpoint.getApiEndpointConfigurationExtras import net.mullvad.mullvadvpn.lib.model.PrepareError import net.mullvad.mullvadvpn.lib.model.Prepared import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.repository.PrivacyDisclaimerRepository import net.mullvad.mullvadvpn.repository.SplashCompleteRepository +import net.mullvad.mullvadvpn.repository.UserPreferencesRepository import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.viewmodel.MullvadAppViewModel import org.koin.android.ext.android.inject @@ -55,7 +55,7 @@ class MainActivity : ComponentActivity(), AndroidScopeComponent { private val apiEndpointFromIntentHolder by inject() private val mullvadAppViewModel by inject() - private val privacyDisclaimerRepository by inject() + private val userPreferencesRepository by inject() private val serviceConnectionManager by inject() private val splashCompleteRepository by inject() private val managementService by inject() @@ -93,7 +93,7 @@ class MainActivity : ComponentActivity(), AndroidScopeComponent { // https://medium.com/@lepicekmichal/android-background-service-without-hiccup-501e4479110f lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - if (privacyDisclaimerRepository.hasAcceptedPrivacyDisclosure()) { + if (userPreferencesRepository.preferences().isPrivacyDisclosureAccepted) { bindService() } } @@ -103,7 +103,7 @@ class MainActivity : ComponentActivity(), AndroidScopeComponent { override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) lifecycleScope.launch { - if (privacyDisclaimerRepository.hasAcceptedPrivacyDisclosure()) { + if (userPreferencesRepository.preferences().isPrivacyDisclosureAccepted) { // If service is to be started wait for it to be connected before dismissing Splash // screen managementService.connectionState @@ -121,8 +121,10 @@ class MainActivity : ComponentActivity(), AndroidScopeComponent { override fun onStop() { super.onStop() - if (privacyDisclaimerRepository.hasAcceptedPrivacyDisclosure()) { - serviceConnectionManager.unbind() + lifecycleScope.launch { + if (userPreferencesRepository.preferences().isPrivacyDisclosureAccepted) { + serviceConnectionManager.unbind() + } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/PrivacyDisclaimerViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/PrivacyDisclaimerViewModel.kt index d2500bc94d30..11800791d26f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/PrivacyDisclaimerViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/PrivacyDisclaimerViewModel.kt @@ -5,18 +5,17 @@ import androidx.lifecycle.viewModelScope import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.WhileSubscribed import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.repository.PrivacyDisclaimerRepository +import net.mullvad.mullvadvpn.repository.UserPreferencesRepository data class PrivacyDisclaimerViewState(val isStartingService: Boolean, val isPlayBuild: Boolean) class PrivacyDisclaimerViewModel( - private val privacyDisclaimerRepository: PrivacyDisclaimerRepository, + private val userPreferencesRepository: UserPreferencesRepository, isPlayBuild: Boolean, ) : ViewModel() { @@ -40,8 +39,8 @@ class PrivacyDisclaimerViewModel( val uiSideEffect = _uiSideEffect.receiveAsFlow() fun setPrivacyDisclosureAccepted() { - privacyDisclaimerRepository.setPrivacyDisclosureAccepted() viewModelScope.launch { + userPreferencesRepository.setPrivacyDisclosureAccepted() if (!_isStartingService.value) { _isStartingService.update { true } _uiSideEffect.send(PrivacyDisclaimerUiSideEffect.StartService) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt index a196d4ae90fa..0ed85c94cd8b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt @@ -16,13 +16,13 @@ import net.mullvad.mullvadvpn.constant.ACCOUNT_EXPIRY_TIMEOUT_MS import net.mullvad.mullvadvpn.lib.model.DeviceState import net.mullvad.mullvadvpn.lib.shared.AccountRepository import net.mullvad.mullvadvpn.lib.shared.DeviceRepository -import net.mullvad.mullvadvpn.repository.PrivacyDisclaimerRepository import net.mullvad.mullvadvpn.repository.SplashCompleteRepository +import net.mullvad.mullvadvpn.repository.UserPreferencesRepository data class SplashScreenState(val splashComplete: Boolean = false) class SplashViewModel( - private val privacyDisclaimerRepository: PrivacyDisclaimerRepository, + private val userPreferencesRepository: UserPreferencesRepository, private val accountRepository: AccountRepository, private val deviceRepository: DeviceRepository, private val splashCompleteRepository: SplashCompleteRepository, @@ -37,7 +37,7 @@ class SplashViewModel( val uiState: StateFlow = _uiState private suspend fun getStartDestination(): SplashUiSideEffect { - if (!privacyDisclaimerRepository.hasAcceptedPrivacyDisclosure()) { + if (!userPreferencesRepository.preferences().isPrivacyDisclosureAccepted) { return SplashUiSideEffect.NavigateToPrivacyDisclaimer } diff --git a/android/app/src/main/proto/user_prefs.proto b/android/app/src/main/proto/user_prefs.proto new file mode 100644 index 000000000000..7da4e9662724 --- /dev/null +++ b/android/app/src/main/proto/user_prefs.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +option java_package = "net.mullvad.mullvadvpn.repository"; +option java_multiple_files = true; + +message UserPreferences { + bool is_privacy_disclosure_accepted = 1; + bool is_latest_changelog_seen = 2; +} diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 0a4b42d80490..16a900284e89 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -13,6 +13,7 @@ androidx-activitycompose = "1.9.3" androidx-appcompat = "1.7.0" androidx-ktx = "1.15.0" androidx-coresplashscreen = "1.1.0-rc01" +androidx-datastore = "1.1.1" androidx-espresso = "3.6.1" androidx-lifecycle = "2.8.7" androidx-test = "1.6.1" @@ -33,7 +34,6 @@ compose-material3 = "1.3.1" grpc = "1.69.0" grpc-kotlin = "1.4.1" grpc-kotlin-jar = "1.4.1:jdk8@jar" -grpc-protobuf = "4.29.1" # Koin koin = "4.0.0" @@ -51,7 +51,8 @@ kotlinx = "1.9.0" kotlinx-serialization = "2.1.0" # Protobuf -protobuf = "0.9.4" +protobuf-gradle-plugin = "0.9.4" +protobuf = "4.29.1" # Misc commonsvalidator = "1.9.0" @@ -81,6 +82,7 @@ android-volley = { module = "com.android.volley:volley", version.ref = "android- androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activitycompose" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } androidx-coresplashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidx-coresplashscreen" } +androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "androidx-datastore" } androidx-espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" } androidx-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-ktx" } androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" } @@ -119,7 +121,9 @@ grpc-okhttp = { module = "io.grpc:grpc-okhttp", version.ref = "grpc" } grpc-android = { module = "io.grpc:grpc-android", version.ref = "grpc" } grpc-kotlin-stub = { module = "io.grpc:grpc-kotlin-stub", version.ref = "grpc-kotlin" } grpc-protobuf-lite = { module = "io.grpc:grpc-protobuf-lite", version.ref = "grpc" } -grpc-protobuf-kotlin-lite = { module = "com.google.protobuf:protobuf-kotlin-lite", version.ref = "grpc-protobuf" } + +# Protobuf +protobuf-kotlin-lite = { module = "com.google.protobuf:protobuf-kotlin-lite", version.ref = "protobuf" } # Koin koin = { module = "io.insert-koin:koin-core", version.ref = "koin" } @@ -177,8 +181,8 @@ kotlin-ksp = { id = "com.google.devtools.ksp", version.ref = "kotlin-ksp" } kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinx-serialization" } # Protobuf -protobuf-core = { id = "com.google.protobuf", version.ref = "protobuf" } -protobuf-protoc = { id = "com.google.protobuf:protoc", version.ref = "grpc-protobuf" } +protobuf-core = { id = "com.google.protobuf", version.ref = "protobuf-gradle-plugin" } +protobuf-protoc = { id = "com.google.protobuf:protoc", version.ref = "protobuf" } # gRPC grpc-protoc-gen-grpc-java = { id = "io.grpc:protoc-gen-grpc-java", version.ref = "grpc" } diff --git a/android/gradle/verification-metadata.xml b/android/gradle/verification-metadata.xml index f4f8e291093c..bf6ad40f17da 100644 --- a/android/gradle/verification-metadata.xml +++ b/android/gradle/verification-metadata.xml @@ -177,6 +177,7 @@ + @@ -1478,6 +1479,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3707,6 +3772,9 @@ + + + @@ -5810,6 +5878,11 @@ + + + + + diff --git a/android/lib/daemon-grpc/build.gradle.kts b/android/lib/daemon-grpc/build.gradle.kts index e1807c9d340d..9383121547b9 100644 --- a/android/lib/daemon-grpc/build.gradle.kts +++ b/android/lib/daemon-grpc/build.gradle.kts @@ -73,7 +73,7 @@ dependencies { implementation(libs.grpc.android) implementation(libs.grpc.kotlin.stub) implementation(libs.grpc.protobuf.lite) - implementation(libs.grpc.protobuf.kotlin.lite) + implementation(libs.protobuf.kotlin.lite) implementation(libs.arrow) implementation(libs.arrow.optics)