mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-17 15:32:01 +00:00
feat(android): add DNS over HTTPS (DoH) support with multiple providers
This commit is contained in:
parent
5fb414ea2f
commit
8f12d32a33
16 changed files with 312 additions and 6 deletions
|
|
@ -225,6 +225,7 @@ kotlin {
|
||||||
implementation(libs.coil.gif)
|
implementation(libs.coil.gif)
|
||||||
implementation("androidx.recyclerview:recyclerview:1.4.0")
|
implementation("androidx.recyclerview:recyclerview:1.4.0")
|
||||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||||
|
implementation(libs.okhttp.dnsoverhttps)
|
||||||
implementation("com.google.code.gson:gson:2.11.0")
|
implementation("com.google.code.gson:gson:2.11.0")
|
||||||
implementation("io.github.peerless2012:ass-media:0.4.0-beta01")
|
implementation("io.github.peerless2012:ass-media:0.4.0-beta01")
|
||||||
implementation(libs.ktor.client.android)
|
implementation(libs.ktor.client.android)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ internal object TrailerExtractionPlatform {
|
||||||
)
|
)
|
||||||
|
|
||||||
private val httpClient = OkHttpClient.Builder()
|
private val httpClient = OkHttpClient.Builder()
|
||||||
.dns(IPv4FirstDns())
|
.dns(com.nuvio.app.core.network.AndroidDnsProvider)
|
||||||
.connectTimeout(TRAILER_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
.connectTimeout(TRAILER_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||||
.readTimeout(TRAILER_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
.readTimeout(TRAILER_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||||
.writeTimeout(TRAILER_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
.writeTimeout(TRAILER_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||||
|
|
@ -33,7 +33,7 @@ internal object TrailerExtractionPlatform {
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private val probeClient = OkHttpClient.Builder()
|
private val probeClient = OkHttpClient.Builder()
|
||||||
.dns(IPv4FirstDns())
|
.dns(com.nuvio.app.core.network.AndroidDnsProvider)
|
||||||
.connectTimeout(2, TimeUnit.SECONDS)
|
.connectTimeout(2, TimeUnit.SECONDS)
|
||||||
.readTimeout(2, TimeUnit.SECONDS)
|
.readTimeout(2, TimeUnit.SECONDS)
|
||||||
.followRedirects(true)
|
.followRedirects(true)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ object AndroidAppUpdaterPlatform {
|
||||||
private const val ignoredTagKey = "ignored_release_tag"
|
private const val ignoredTagKey = "ignored_release_tag"
|
||||||
|
|
||||||
private val httpClient = OkHttpClient.Builder()
|
private val httpClient = OkHttpClient.Builder()
|
||||||
.connectTimeout(60, TimeUnit.SECONDS)
|
.dns(com.nuvio.app.core.network.AndroidDnsProvider)
|
||||||
|
.connectTimeout(30, TimeUnit.SECONDS)
|
||||||
.readTimeout(60, TimeUnit.SECONDS)
|
.readTimeout(60, TimeUnit.SECONDS)
|
||||||
.writeTimeout(60, TimeUnit.SECONDS)
|
.writeTimeout(60, TimeUnit.SECONDS)
|
||||||
.followRedirects(true)
|
.followRedirects(true)
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ class MainActivity : ComponentActivity() {
|
||||||
SeasonViewModeStorage.initialize(applicationContext)
|
SeasonViewModeStorage.initialize(applicationContext)
|
||||||
ThemeSettingsStorage.initialize(applicationContext)
|
ThemeSettingsStorage.initialize(applicationContext)
|
||||||
PosterCardStyleStorage.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)
|
TmdbSettingsStorage.initialize(applicationContext)
|
||||||
MdbListSettingsStorage.initialize(applicationContext)
|
MdbListSettingsStorage.initialize(applicationContext)
|
||||||
TraktAuthStorage.initialize(applicationContext)
|
TraktAuthStorage.initialize(applicationContext)
|
||||||
|
|
|
||||||
|
|
@ -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<InetAddress> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -44,7 +44,7 @@ actual object AddonStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
private val addonHttpClient = OkHttpClient.Builder()
|
private val addonHttpClient = OkHttpClient.Builder()
|
||||||
.dns(IPv4FirstDns())
|
.dns(com.nuvio.app.core.network.AndroidDnsProvider)
|
||||||
.connectTimeout(60, TimeUnit.SECONDS)
|
.connectTimeout(60, TimeUnit.SECONDS)
|
||||||
.readTimeout(60, TimeUnit.SECONDS)
|
.readTimeout(60, TimeUnit.SECONDS)
|
||||||
.writeTimeout(60, TimeUnit.SECONDS)
|
.writeTimeout(60, TimeUnit.SECONDS)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@ import java.net.URI
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
private val downloadHttpClient = OkHttpClient.Builder()
|
private val downloadHttpClient = OkHttpClient.Builder()
|
||||||
.connectTimeout(60, TimeUnit.SECONDS)
|
.dns(com.nuvio.app.core.network.AndroidDnsProvider)
|
||||||
|
.connectTimeout(30, TimeUnit.SECONDS)
|
||||||
.readTimeout(60, TimeUnit.SECONDS)
|
.readTimeout(60, TimeUnit.SECONDS)
|
||||||
.writeTimeout(60, TimeUnit.SECONDS)
|
.writeTimeout(60, TimeUnit.SECONDS)
|
||||||
.followRedirects(true)
|
.followRedirects(true)
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ internal object PlayerPlaybackNetworking {
|
||||||
|
|
||||||
private val playbackHttpClient: OkHttpClient by lazy {
|
private val playbackHttpClient: OkHttpClient by lazy {
|
||||||
OkHttpClient.Builder()
|
OkHttpClient.Builder()
|
||||||
.dns(IPv4FirstDns())
|
.dns(com.nuvio.app.core.network.AndroidDnsProvider)
|
||||||
.sslSocketFactory(sslContext.socketFactory, trustAllManager)
|
.sslSocketFactory(sslContext.socketFactory, trustAllManager)
|
||||||
.hostnameVerifier(playbackHostnameVerifier)
|
.hostnameVerifier(playbackHostnameVerifier)
|
||||||
.connectTimeout(15, TimeUnit.SECONDS)
|
.connectTimeout(15, TimeUnit.SECONDS)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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> = _dnsProvider.asStateFlow()
|
||||||
|
|
||||||
|
fun setDnsProvider(provider: DnsProvider) {
|
||||||
|
storage.setDnsProvider(provider.name)
|
||||||
|
_dnsProvider.value = provider
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalNetworkSettingsRepository: NetworkSettingsRepository? = null
|
||||||
|
|
@ -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
|
@Composable
|
||||||
internal fun HomescreenCatalogRow(
|
internal fun HomescreenCatalogRow(
|
||||||
item: HomeCatalogSettingsItem,
|
item: HomeCatalogSettingsItem,
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,11 @@ internal enum class SettingsPage(
|
||||||
category = SettingsCategory.Account,
|
category = SettingsCategory.Account,
|
||||||
parentPage = Root,
|
parentPage = Root,
|
||||||
),
|
),
|
||||||
|
Network(
|
||||||
|
title = "Network",
|
||||||
|
category = SettingsCategory.General,
|
||||||
|
parentPage = Root,
|
||||||
|
),
|
||||||
SupportersContributors(
|
SupportersContributors(
|
||||||
title = "Supporters & Contributors",
|
title = "Supporters & Contributors",
|
||||||
category = SettingsCategory.About,
|
category = SettingsCategory.About,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import androidx.compose.material.icons.rounded.Notifications
|
||||||
import androidx.compose.material.icons.rounded.Palette
|
import androidx.compose.material.icons.rounded.Palette
|
||||||
import androidx.compose.material.icons.rounded.People
|
import androidx.compose.material.icons.rounded.People
|
||||||
import androidx.compose.material.icons.rounded.PlayArrow
|
import androidx.compose.material.icons.rounded.PlayArrow
|
||||||
|
import androidx.compose.material.icons.rounded.Settings
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -27,6 +28,7 @@ internal fun LazyListScope.settingsRootContent(
|
||||||
onAppearanceClick: () -> Unit,
|
onAppearanceClick: () -> Unit,
|
||||||
onNotificationsClick: () -> Unit,
|
onNotificationsClick: () -> Unit,
|
||||||
onContentDiscoveryClick: () -> Unit,
|
onContentDiscoveryClick: () -> Unit,
|
||||||
|
onNetworkClick: () -> Unit,
|
||||||
onIntegrationsClick: () -> Unit,
|
onIntegrationsClick: () -> Unit,
|
||||||
onTraktClick: () -> Unit,
|
onTraktClick: () -> Unit,
|
||||||
onSupportersContributorsClick: () -> Unit,
|
onSupportersContributorsClick: () -> Unit,
|
||||||
|
|
@ -112,6 +114,16 @@ internal fun LazyListScope.settingsRootContent(
|
||||||
isTablet = isTablet,
|
isTablet = isTablet,
|
||||||
onClick = onPlaybackClick,
|
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)
|
SettingsGroupDivider(isTablet = isTablet)
|
||||||
SettingsNavigationRow(
|
SettingsNavigationRow(
|
||||||
title = "Integrations",
|
title = "Integrations",
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,7 @@ private fun MobileSettingsScreen(
|
||||||
onAppearanceClick = { onPageChange(SettingsPage.Appearance) },
|
onAppearanceClick = { onPageChange(SettingsPage.Appearance) },
|
||||||
onNotificationsClick = { onPageChange(SettingsPage.Notifications) },
|
onNotificationsClick = { onPageChange(SettingsPage.Notifications) },
|
||||||
onContentDiscoveryClick = { onPageChange(SettingsPage.ContentDiscovery) },
|
onContentDiscoveryClick = { onPageChange(SettingsPage.ContentDiscovery) },
|
||||||
|
onNetworkClick = { onPageChange(SettingsPage.Network) },
|
||||||
onIntegrationsClick = { onPageChange(SettingsPage.Integrations) },
|
onIntegrationsClick = { onPageChange(SettingsPage.Integrations) },
|
||||||
onTraktClick = { onPageChange(SettingsPage.TraktAuthentication) },
|
onTraktClick = { onPageChange(SettingsPage.TraktAuthentication) },
|
||||||
onSupportersContributorsClick = onSupportersContributorsClick,
|
onSupportersContributorsClick = onSupportersContributorsClick,
|
||||||
|
|
@ -396,6 +397,9 @@ private fun MobileSettingsScreen(
|
||||||
commentsEnabled = traktCommentsEnabled,
|
commentsEnabled = traktCommentsEnabled,
|
||||||
onCommentsEnabledChange = TraktCommentsSettings::setEnabled,
|
onCommentsEnabledChange = TraktCommentsSettings::setEnabled,
|
||||||
)
|
)
|
||||||
|
SettingsPage.Network -> networkSettingsContent(
|
||||||
|
isTablet = false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -521,6 +525,7 @@ private fun TabletSettingsScreen(
|
||||||
onAppearanceClick = { openInlinePage(SettingsPage.Appearance) },
|
onAppearanceClick = { openInlinePage(SettingsPage.Appearance) },
|
||||||
onNotificationsClick = { openInlinePage(SettingsPage.Notifications) },
|
onNotificationsClick = { openInlinePage(SettingsPage.Notifications) },
|
||||||
onContentDiscoveryClick = { openInlinePage(SettingsPage.ContentDiscovery) },
|
onContentDiscoveryClick = { openInlinePage(SettingsPage.ContentDiscovery) },
|
||||||
|
onNetworkClick = { openInlinePage(SettingsPage.Network) },
|
||||||
onIntegrationsClick = { openInlinePage(SettingsPage.Integrations) },
|
onIntegrationsClick = { openInlinePage(SettingsPage.Integrations) },
|
||||||
onTraktClick = { openInlinePage(SettingsPage.TraktAuthentication) },
|
onTraktClick = { openInlinePage(SettingsPage.TraktAuthentication) },
|
||||||
onSupportersContributorsClick = { openInlinePage(SettingsPage.SupportersContributors) },
|
onSupportersContributorsClick = { openInlinePage(SettingsPage.SupportersContributors) },
|
||||||
|
|
@ -618,6 +623,9 @@ private fun TabletSettingsScreen(
|
||||||
commentsEnabled = traktCommentsEnabled,
|
commentsEnabled = traktCommentsEnabled,
|
||||||
onCommentsEnabledChange = TraktCommentsSettings::setEnabled,
|
onCommentsEnabledChange = TraktCommentsSettings::setEnabled,
|
||||||
)
|
)
|
||||||
|
SettingsPage.Network -> networkSettingsContent(
|
||||||
|
isTablet = true,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ quickjsKt = "1.0.1"
|
||||||
ksoup = "0.2.6"
|
ksoup = "0.2.6"
|
||||||
reorderable = "3.0.0"
|
reorderable = "3.0.0"
|
||||||
desugarJdkLibs = "2.1.5"
|
desugarJdkLibs = "2.1.5"
|
||||||
|
okhttp = "4.12.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
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" }
|
ksoup = { module = "com.fleeksoft.ksoup:ksoup", version.ref = "ksoup" }
|
||||||
reorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "reorderable" }
|
reorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "reorderable" }
|
||||||
desugar-jdk-libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" }
|
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]
|
[plugins]
|
||||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue