From 8f12d32a339c674949110c30401cc68a92699096 Mon Sep 17 00:00:00 2001 From: paregi12 Date: Fri, 24 Apr 2026 14:33:20 +0530 Subject: [PATCH] feat(android): add DNS over HTTPS (DoH) support with multiple providers --- composeApp/build.gradle.kts | 1 + .../TrailerExtractionPlatform.android.kt | 4 +- .../updater/AndroidAppUpdaterPlatform.kt | 3 +- .../kotlin/com/nuvio/app/MainActivity.kt | 1 + .../app/core/network/AndroidDnsProvider.kt | 127 ++++++++++++++++++ .../features/addons/AddonPlatform.android.kt | 2 +- .../DownloadsPlatformDownloader.android.kt | 3 +- .../player/PlayerPlaybackNetworking.kt | 2 +- .../settings/AndroidNetworkSettingsStorage.kt | 16 +++ .../features/settings/NetworkSettingsPage.kt | 41 ++++++ .../settings/NetworkSettingsRepository.kt | 40 ++++++ .../features/settings/SettingsComponents.kt | 51 +++++++ .../app/features/settings/SettingsModels.kt | 5 + .../app/features/settings/SettingsRootPage.kt | 12 ++ .../app/features/settings/SettingsScreen.kt | 8 ++ gradle/libs.versions.toml | 2 + 16 files changed, 312 insertions(+), 6 deletions(-) create mode 100644 composeApp/src/androidMain/kotlin/com/nuvio/app/core/network/AndroidDnsProvider.kt create mode 100644 composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/AndroidNetworkSettingsStorage.kt create mode 100644 composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/NetworkSettingsPage.kt create mode 100644 composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/NetworkSettingsRepository.kt diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index ca333a1c..e3d8292c 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -225,6 +225,7 @@ kotlin { implementation(libs.coil.gif) implementation("androidx.recyclerview:recyclerview:1.4.0") implementation("com.squareup.okhttp3:okhttp:4.12.0") + implementation(libs.okhttp.dnsoverhttps) implementation("com.google.code.gson:gson:2.11.0") implementation("io.github.peerless2012:ass-media:0.4.0-beta01") implementation(libs.ktor.client.android) diff --git a/composeApp/src/androidFull/kotlin/com/nuvio/app/features/trailer/TrailerExtractionPlatform.android.kt b/composeApp/src/androidFull/kotlin/com/nuvio/app/features/trailer/TrailerExtractionPlatform.android.kt index c1b3aa97..1974d058 100644 --- a/composeApp/src/androidFull/kotlin/com/nuvio/app/features/trailer/TrailerExtractionPlatform.android.kt +++ b/composeApp/src/androidFull/kotlin/com/nuvio/app/features/trailer/TrailerExtractionPlatform.android.kt @@ -24,7 +24,7 @@ internal object TrailerExtractionPlatform { ) private val httpClient = OkHttpClient.Builder() - .dns(IPv4FirstDns()) + .dns(com.nuvio.app.core.network.AndroidDnsProvider) .connectTimeout(TRAILER_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS) .readTimeout(TRAILER_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS) .writeTimeout(TRAILER_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS) @@ -33,7 +33,7 @@ internal object TrailerExtractionPlatform { .build() private val probeClient = OkHttpClient.Builder() - .dns(IPv4FirstDns()) + .dns(com.nuvio.app.core.network.AndroidDnsProvider) .connectTimeout(2, TimeUnit.SECONDS) .readTimeout(2, TimeUnit.SECONDS) .followRedirects(true) diff --git a/composeApp/src/androidFull/kotlin/com/nuvio/app/features/updater/AndroidAppUpdaterPlatform.kt b/composeApp/src/androidFull/kotlin/com/nuvio/app/features/updater/AndroidAppUpdaterPlatform.kt index 72c2e50e..309c4d43 100644 --- a/composeApp/src/androidFull/kotlin/com/nuvio/app/features/updater/AndroidAppUpdaterPlatform.kt +++ b/composeApp/src/androidFull/kotlin/com/nuvio/app/features/updater/AndroidAppUpdaterPlatform.kt @@ -19,7 +19,8 @@ object AndroidAppUpdaterPlatform { private const val ignoredTagKey = "ignored_release_tag" private val httpClient = OkHttpClient.Builder() - .connectTimeout(60, TimeUnit.SECONDS) + .dns(com.nuvio.app.core.network.AndroidDnsProvider) + .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS) .followRedirects(true) diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/MainActivity.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/MainActivity.kt index fcd14cf3..2c81cdc4 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/MainActivity.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/MainActivity.kt @@ -68,6 +68,7 @@ class MainActivity : ComponentActivity() { SeasonViewModeStorage.initialize(applicationContext) ThemeSettingsStorage.initialize(applicationContext) PosterCardStyleStorage.initialize(applicationContext) + com.nuvio.app.features.settings.globalNetworkSettingsRepository = com.nuvio.app.features.settings.NetworkSettingsRepository(com.nuvio.app.features.settings.AndroidNetworkSettingsStorage(applicationContext)) TmdbSettingsStorage.initialize(applicationContext) MdbListSettingsStorage.initialize(applicationContext) TraktAuthStorage.initialize(applicationContext) diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/core/network/AndroidDnsProvider.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/core/network/AndroidDnsProvider.kt new file mode 100644 index 00000000..b68a1b77 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/core/network/AndroidDnsProvider.kt @@ -0,0 +1,127 @@ +package com.nuvio.app.core.network + +import com.nuvio.app.features.settings.DnsProvider +import com.nuvio.app.features.settings.globalNetworkSettingsRepository +import okhttp3.Dns +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.dnsoverhttps.DnsOverHttps +import java.net.InetAddress +import java.io.File +import okhttp3.Cache + +object AndroidDnsProvider : Dns { + + // A dedicated minimal client just for DoH resolutions + private val bootstrapClient by lazy { + OkHttpClient.Builder() + .dns(IPv4FirstDns(Dns.SYSTEM)) + .build() + } + + private val cloudflareDns by lazy { + DnsOverHttps.Builder().client(bootstrapClient) + .url("https://cloudflare-dns.com/dns-query".toHttpUrl()) + .bootstrapDnsHosts( + InetAddress.getByName("1.1.1.1"), + InetAddress.getByName("1.0.0.1"), + InetAddress.getByName("2606:4700:4700::1111"), + InetAddress.getByName("2606:4700:4700::1001") + ) + .build() + } + + private val googleDns by lazy { + DnsOverHttps.Builder().client(bootstrapClient) + .url("https://dns.google/dns-query".toHttpUrl()) + .bootstrapDnsHosts( + InetAddress.getByName("8.8.8.8"), + InetAddress.getByName("8.8.4.4"), + InetAddress.getByName("2001:4860:4860::8888"), + InetAddress.getByName("2001:4860:4860::8844") + ) + .build() + } + + private val quad9Dns by lazy { + DnsOverHttps.Builder().client(bootstrapClient) + .url("https://dns.quad9.net/dns-query".toHttpUrl()) + .bootstrapDnsHosts( + InetAddress.getByName("9.9.9.9"), + InetAddress.getByName("149.112.112.112"), + InetAddress.getByName("2620:fe::fe"), + InetAddress.getByName("2620:fe::9") + ) + .build() + } + + private val adGuardDns by lazy { + DnsOverHttps.Builder().client(bootstrapClient) + .url("https://dns.adguard-dns.com/dns-query".toHttpUrl()) + .bootstrapDnsHosts( + InetAddress.getByName("94.140.14.14"), + InetAddress.getByName("94.140.15.15"), + InetAddress.getByName("2a10:50c0::ad1"), + InetAddress.getByName("2a10:50c0::ad2") + ) + .build() + } + + private val nextDns by lazy { + DnsOverHttps.Builder().client(bootstrapClient) + .url("https://dns.nextdns.io".toHttpUrl()) + .bootstrapDnsHosts( + InetAddress.getByName("45.90.28.0"), + InetAddress.getByName("45.90.30.0"), + InetAddress.getByName("2a07:a8c0::"), + InetAddress.getByName("2a07:a8c1::") + ) + .build() + } + + private val mullvadDns by lazy { + DnsOverHttps.Builder().client(bootstrapClient) + .url("https://doh.mullvad.net/dns-query".toHttpUrl()) + .bootstrapDnsHosts( + InetAddress.getByName("194.242.2.4"), + InetAddress.getByName("2a07:e340::4") + ) + .build() + } + + private val openDns by lazy { + DnsOverHttps.Builder().client(bootstrapClient) + .url("https://doh.opendns.com/dns-query".toHttpUrl()) + .bootstrapDnsHosts( + InetAddress.getByName("208.67.222.222"), + InetAddress.getByName("208.67.220.220"), + InetAddress.getByName("2620:119:35::35"), + InetAddress.getByName("2620:119:53::53") + ) + .build() + } + + private val systemDns = IPv4FirstDns(Dns.SYSTEM) + + override fun lookup(hostname: String): List { + val currentProvider = runCatching { globalNetworkSettingsRepository?.dnsProvider?.value }.getOrNull() ?: DnsProvider.SYSTEM + + val activeDns = when (currentProvider) { + DnsProvider.CLOUDFLARE -> cloudflareDns + DnsProvider.GOOGLE -> googleDns + DnsProvider.QUAD9 -> quad9Dns + DnsProvider.ADGUARD -> adGuardDns + DnsProvider.NEXTDNS -> nextDns + DnsProvider.MULLVAD -> mullvadDns + DnsProvider.OPEN_DNS -> openDns + DnsProvider.SYSTEM -> systemDns + } + + return try { + activeDns.lookup(hostname) + } catch (e: Exception) { + // Fallback to system DNS if DoH fails + systemDns.lookup(hostname) + } + } +} diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/addons/AddonPlatform.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/addons/AddonPlatform.android.kt index 2c99ed0f..c8cdf9e0 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/addons/AddonPlatform.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/addons/AddonPlatform.android.kt @@ -44,7 +44,7 @@ actual object AddonStorage { } private val addonHttpClient = OkHttpClient.Builder() - .dns(IPv4FirstDns()) + .dns(com.nuvio.app.core.network.AndroidDnsProvider) .connectTimeout(60, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS) diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/downloads/DownloadsPlatformDownloader.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/downloads/DownloadsPlatformDownloader.android.kt index e1e3b60d..3714ca90 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/downloads/DownloadsPlatformDownloader.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/downloads/DownloadsPlatformDownloader.android.kt @@ -17,7 +17,8 @@ import java.net.URI import java.util.concurrent.TimeUnit private val downloadHttpClient = OkHttpClient.Builder() - .connectTimeout(60, TimeUnit.SECONDS) + .dns(com.nuvio.app.core.network.AndroidDnsProvider) + .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS) .followRedirects(true) diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerPlaybackNetworking.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerPlaybackNetworking.kt index 01653e47..19ea64f0 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerPlaybackNetworking.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerPlaybackNetworking.kt @@ -47,7 +47,7 @@ internal object PlayerPlaybackNetworking { private val playbackHttpClient: OkHttpClient by lazy { OkHttpClient.Builder() - .dns(IPv4FirstDns()) + .dns(com.nuvio.app.core.network.AndroidDnsProvider) .sslSocketFactory(sslContext.socketFactory, trustAllManager) .hostnameVerifier(playbackHostnameVerifier) .connectTimeout(15, TimeUnit.SECONDS) diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/AndroidNetworkSettingsStorage.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/AndroidNetworkSettingsStorage.kt new file mode 100644 index 00000000..f3068f3e --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/AndroidNetworkSettingsStorage.kt @@ -0,0 +1,16 @@ +package com.nuvio.app.features.settings + +import android.content.Context +import android.content.SharedPreferences + +class AndroidNetworkSettingsStorage(context: Context) : NetworkSettingsStorage { + private val prefs: SharedPreferences = context.getSharedPreferences("nuvio_network_settings", Context.MODE_PRIVATE) + private val DNS_PROVIDER_KEY = "dns_provider" + + override fun getDnsProvider(): String? = + prefs.getString(DNS_PROVIDER_KEY, null) + + override fun setDnsProvider(provider: String) { + prefs.edit().putString(DNS_PROVIDER_KEY, provider).apply() + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/NetworkSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/NetworkSettingsPage.kt new file mode 100644 index 00000000..e45be1a5 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/NetworkSettingsPage.kt @@ -0,0 +1,41 @@ +package com.nuvio.app.features.settings + +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +internal fun LazyListScope.networkSettingsContent( + isTablet: Boolean, +) { + item { + val repository = globalNetworkSettingsRepository ?: return@item + val currentProvider by repository.dnsProvider.collectAsState() + + androidx.compose.foundation.layout.Column( + modifier = Modifier.padding(horizontal = if (isTablet) 24.dp else 0.dp) + ) { + SettingsSection( + title = "DNS OVER HTTPS (ANDROID ONLY)", + isTablet = isTablet + ) { + SettingsGroup(isTablet = isTablet) { + DnsProvider.entries.forEachIndexed { index, provider -> + SettingsRadioRow( + title = provider.displayName, + description = if (provider == DnsProvider.SYSTEM) "Use the system's default DNS resolver." else "Encrypt DNS queries via ${provider.name.lowercase()}.", + selected = currentProvider == provider, + onClick = { repository.setDnsProvider(provider) }, + isTablet = isTablet + ) + if (index < DnsProvider.entries.lastIndex) { + SettingsGroupDivider(isTablet = isTablet) + } + } + } + } + } + } +} diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/NetworkSettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/NetworkSettingsRepository.kt new file mode 100644 index 00000000..2e6bde9a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/NetworkSettingsRepository.kt @@ -0,0 +1,40 @@ +package com.nuvio.app.features.settings + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +enum class DnsProvider(val displayName: String) { + SYSTEM("System Default (IPv4 Preferred)"), + CLOUDFLARE("Cloudflare (DoH)"), + GOOGLE("Google (DoH)"), + QUAD9("Quad9 (DoH)"), + ADGUARD("AdGuard (DoH)"), + NEXTDNS("NextDNS (DoH)"), + MULLVAD("Mullvad (DoH)"), + OPEN_DNS("OpenDNS (DoH)") +} + +interface NetworkSettingsStorage { + fun getDnsProvider(): String? + fun setDnsProvider(provider: String) +} + +class NetworkSettingsRepository( + private val storage: NetworkSettingsStorage +) { + private val _dnsProvider = MutableStateFlow( + runCatching { + val name = storage.getDnsProvider() ?: DnsProvider.SYSTEM.name + DnsProvider.valueOf(name) + }.getOrDefault(DnsProvider.SYSTEM) + ) + val dnsProvider: StateFlow = _dnsProvider.asStateFlow() + + fun setDnsProvider(provider: DnsProvider) { + storage.setDnsProvider(provider.name) + _dnsProvider.value = provider + } +} + +var globalNetworkSettingsRepository: NetworkSettingsRepository? = null diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsComponents.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsComponents.kt index e835a4da..5b819dd3 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsComponents.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsComponents.kt @@ -334,6 +334,57 @@ internal fun SettingsSwitchRow( } } +@Composable +internal fun SettingsRadioRow( + title: String, + description: String? = null, + selected: Boolean, + enabled: Boolean = true, + isTablet: Boolean, + onClick: () -> Unit, +) { + val verticalPadding = if (isTablet) 16.dp else 14.dp + val horizontalPadding = if (isTablet) 20.dp else 16.dp + + Row( + modifier = Modifier + .fillMaxWidth() + .clickable(enabled = enabled) { onClick() } + .padding(horizontal = horizontalPadding, vertical = verticalPadding), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically, + ) { + Column( + modifier = Modifier + .weight(1f) + .padding(end = 12.dp) + .widthIn(max = if (isTablet) 560.dp else androidx.compose.ui.unit.Dp.Unspecified) + .alpha(if (enabled) 1f else 0.55f), + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = title, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurface, + fontWeight = FontWeight.Medium, + ) + if (!description.isNullOrBlank()) { + Text( + text = description, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + androidx.compose.material3.RadioButton( + selected = selected, + onClick = null, + enabled = enabled, + modifier = Modifier.padding(start = 4.dp) + ) + } +} + @Composable internal fun HomescreenCatalogRow( item: HomeCatalogSettingsItem, diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsModels.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsModels.kt index ba432304..ef060e55 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsModels.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsModels.kt @@ -31,6 +31,11 @@ internal enum class SettingsPage( category = SettingsCategory.Account, parentPage = Root, ), + Network( + title = "Network", + category = SettingsCategory.General, + parentPage = Root, + ), SupportersContributors( title = "Supporters & Contributors", category = SettingsCategory.About, diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt index 47b341bc..0a55b697 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt @@ -14,6 +14,7 @@ import androidx.compose.material.icons.rounded.Notifications import androidx.compose.material.icons.rounded.Palette import androidx.compose.material.icons.rounded.People import androidx.compose.material.icons.rounded.PlayArrow +import androidx.compose.material.icons.rounded.Settings import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.ui.Modifier @@ -27,6 +28,7 @@ internal fun LazyListScope.settingsRootContent( onAppearanceClick: () -> Unit, onNotificationsClick: () -> Unit, onContentDiscoveryClick: () -> Unit, + onNetworkClick: () -> Unit, onIntegrationsClick: () -> Unit, onTraktClick: () -> Unit, onSupportersContributorsClick: () -> Unit, @@ -112,6 +114,16 @@ internal fun LazyListScope.settingsRootContent( isTablet = isTablet, onClick = onPlaybackClick, ) + if (com.nuvio.app.features.settings.globalNetworkSettingsRepository != null) { + SettingsGroupDivider(isTablet = isTablet) + SettingsNavigationRow( + title = "Network", + description = "Configure DNS over HTTPS.", + icon = Icons.Rounded.Settings, + isTablet = isTablet, + onClick = onNetworkClick, + ) + } SettingsGroupDivider(isTablet = isTablet) SettingsNavigationRow( title = "Integrations", diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt index 3b31e45f..39a14733 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt @@ -302,6 +302,7 @@ private fun MobileSettingsScreen( onAppearanceClick = { onPageChange(SettingsPage.Appearance) }, onNotificationsClick = { onPageChange(SettingsPage.Notifications) }, onContentDiscoveryClick = { onPageChange(SettingsPage.ContentDiscovery) }, + onNetworkClick = { onPageChange(SettingsPage.Network) }, onIntegrationsClick = { onPageChange(SettingsPage.Integrations) }, onTraktClick = { onPageChange(SettingsPage.TraktAuthentication) }, onSupportersContributorsClick = onSupportersContributorsClick, @@ -396,6 +397,9 @@ private fun MobileSettingsScreen( commentsEnabled = traktCommentsEnabled, onCommentsEnabledChange = TraktCommentsSettings::setEnabled, ) + SettingsPage.Network -> networkSettingsContent( + isTablet = false, + ) } } } @@ -521,6 +525,7 @@ private fun TabletSettingsScreen( onAppearanceClick = { openInlinePage(SettingsPage.Appearance) }, onNotificationsClick = { openInlinePage(SettingsPage.Notifications) }, onContentDiscoveryClick = { openInlinePage(SettingsPage.ContentDiscovery) }, + onNetworkClick = { openInlinePage(SettingsPage.Network) }, onIntegrationsClick = { openInlinePage(SettingsPage.Integrations) }, onTraktClick = { openInlinePage(SettingsPage.TraktAuthentication) }, onSupportersContributorsClick = { openInlinePage(SettingsPage.SupportersContributors) }, @@ -618,6 +623,9 @@ private fun TabletSettingsScreen( commentsEnabled = traktCommentsEnabled, onCommentsEnabledChange = TraktCommentsSettings::setEnabled, ) + SettingsPage.Network -> networkSettingsContent( + isTablet = true, + ) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d5929dc6..b25692d8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,6 +26,7 @@ quickjsKt = "1.0.1" ksoup = "0.2.6" reorderable = "3.0.0" desugarJdkLibs = "2.1.5" +okhttp = "4.12.0" [libraries] kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } @@ -75,6 +76,7 @@ quickjs-kt = { module = "io.github.dokar3:quickjs-kt", version.ref = "quickjsKt" ksoup = { module = "com.fleeksoft.ksoup:ksoup", version.ref = "ksoup" } reorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "reorderable" } desugar-jdk-libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" } +okhttp-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" }