Add proxy settings
This commit is contained in:
parent
9311f90fc9
commit
89c0822158
|
@ -15,8 +15,10 @@ import ie.equalit.ouinet.Config
|
|||
import me.ash.reader.data.dao.AccountDao
|
||||
import me.ash.reader.data.model.preference.AccountSettingsProvider
|
||||
import me.ash.reader.data.model.preference.LanguagesPreference
|
||||
import me.ash.reader.data.model.preference.ProxyPreferences
|
||||
import me.ash.reader.data.model.preference.SettingsProvider
|
||||
import me.ash.reader.ui.ext.languages
|
||||
import me.ash.reader.ui.ext.proxy
|
||||
import me.ash.reader.ui.page.common.HomeEntry
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -25,7 +27,6 @@ import javax.inject.Inject
|
|||
*/
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
private lateinit var ouinet: Ouinet
|
||||
|
||||
@Inject
|
||||
lateinit var imageLoader: ImageLoader
|
||||
|
@ -44,20 +45,27 @@ class MainActivity : ComponentActivity() {
|
|||
it.setLocale(this)
|
||||
}
|
||||
|
||||
// Enable OuiNet client
|
||||
val config = Config.ConfigBuilder(this)
|
||||
// If a proxy config is set, start ouinet service
|
||||
if (ProxyPreferences.fromValue(proxy) != ProxyPreferences.NoProxy) {
|
||||
val ouinetBuilder = Config.ConfigBuilder(this)
|
||||
.setCacheType("bep5-http")
|
||||
.setCacheHttpPubKey(BuildConfig.CACHE_PUB_KEY)
|
||||
.setInjectorCredentials(BuildConfig.INJECTOR_CREDENTIALS)
|
||||
.setInjectorTlsCert(BuildConfig.INJECTOR_TLS_CERT)
|
||||
.setTlsCaCertStorePath("file:///android_asset/cacert.pem")
|
||||
.setDisableOriginAccess(true)
|
||||
//.setDisableProxyAccess(true)
|
||||
//.setDisableInjectorAccess(true)
|
||||
.build()
|
||||
|
||||
ouinet = Ouinet(this, config)
|
||||
ouinet.start()
|
||||
val proxyConfig = when (ProxyPreferences.fromValue(proxy)) {
|
||||
ProxyPreferences.OuinetBasic -> ouinetBuilder.build()
|
||||
ProxyPreferences.OuinetP2P -> {
|
||||
ouinetBuilder.setDisableOriginAccess(true).build()
|
||||
}
|
||||
ProxyPreferences.NoProxy -> {
|
||||
// Cannot happen, but when should be exhaustive
|
||||
ouinetBuilder.build()
|
||||
}
|
||||
}
|
||||
val ouinet = Ouinet(this, proxyConfig)
|
||||
ouinet.start()
|
||||
}
|
||||
|
||||
setContent {
|
||||
CompositionLocalProvider(
|
||||
|
|
|
@ -77,5 +77,8 @@ fun Preferences.toSettings(): Settings {
|
|||
|
||||
// Languages
|
||||
languages = LanguagesPreference.fromPreferences(this),
|
||||
|
||||
// Proxy
|
||||
proxy = ProxyPreferences.fromPreferences(this),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package me.ash.reader.data.model.preference
|
||||
|
||||
import android.content.Context
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.page.settings.accounts.AccountViewModel
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.ui.ext.DataStoreKeys
|
||||
import me.ash.reader.ui.ext.dataStore
|
||||
import me.ash.reader.ui.ext.put
|
||||
|
||||
sealed class ProxyPreferences(
|
||||
val value: Int,
|
||||
) {
|
||||
object NoProxy : ProxyPreferences(0)
|
||||
object OuinetBasic : ProxyPreferences(1)
|
||||
object OuinetP2P : ProxyPreferences(2)
|
||||
|
||||
fun put(context: Context, scope: CoroutineScope) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
context.dataStore.put(DataStoreKeys.Proxy, value)
|
||||
}
|
||||
}
|
||||
|
||||
fun toDesc(context: Context): String =
|
||||
when (this) {
|
||||
NoProxy -> context.getString(R.string.proxy_system)
|
||||
OuinetBasic -> context.getString(R.string.proxy_ouinet_basic)
|
||||
OuinetP2P -> context.getString(R.string.proxy_ouinet_p2p)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val default = NoProxy
|
||||
val values = listOf(
|
||||
NoProxy,
|
||||
OuinetBasic,
|
||||
OuinetP2P,
|
||||
)
|
||||
|
||||
fun fromPreferences(preferences: Preferences): ProxyPreferences =
|
||||
fromValue(preferences[DataStoreKeys.Proxy.key])
|
||||
|
||||
fun fromValue(value: Int?): ProxyPreferences =
|
||||
when (value) {
|
||||
0 -> ProxyPreferences.NoProxy
|
||||
1 -> ProxyPreferences.OuinetBasic
|
||||
2 -> ProxyPreferences.OuinetP2P
|
||||
else -> ProxyPreferences.default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -76,6 +76,9 @@ data class Settings(
|
|||
|
||||
// Languages
|
||||
val languages: LanguagesPreference = LanguagesPreference.default,
|
||||
|
||||
// Proxy
|
||||
val proxy: ProxyPreferences = ProxyPreferences.default
|
||||
)
|
||||
|
||||
// Version
|
||||
|
@ -172,6 +175,10 @@ val LocalInitialFilter =
|
|||
val LocalLanguages =
|
||||
compositionLocalOf<LanguagesPreference> { LanguagesPreference.default }
|
||||
|
||||
// Ouinet settings
|
||||
val Proxy =
|
||||
compositionLocalOf<ProxyPreferences> { ProxyPreferences.default }
|
||||
|
||||
@Composable
|
||||
fun SettingsProvider(
|
||||
content: @Composable () -> Unit,
|
||||
|
@ -249,6 +256,9 @@ fun SettingsProvider(
|
|||
|
||||
// Languages
|
||||
LocalLanguages provides settings.languages,
|
||||
|
||||
// Proxy
|
||||
Proxy provides settings.proxy,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
*/
|
||||
|
||||
package me.ash.reader.data.module
|
||||
import me.ash.reader.data.model.preference.ProxyPreferences
|
||||
import me.ash.reader.ui.ext.proxy
|
||||
|
||||
import android.content.Context
|
||||
import dagger.Module
|
||||
|
@ -56,7 +58,8 @@ object OkHttpClientModule {
|
|||
fun provideOkHttpClient(
|
||||
@ApplicationContext context: Context,
|
||||
): OkHttpClient = cachingHttpClient(
|
||||
cacheDirectory = context.cacheDir.resolve("http")
|
||||
cacheDirectory = context.cacheDir.resolve("http"),
|
||||
proxy = ProxyPreferences.fromValue(context.proxy)
|
||||
).newBuilder()
|
||||
.addNetworkInterceptor(UserAgentInterceptor)
|
||||
.build()
|
||||
|
@ -68,9 +71,16 @@ fun cachingHttpClient(
|
|||
trustAllCerts: Boolean = true,
|
||||
connectTimeoutSecs: Long = 30L,
|
||||
readTimeoutSecs: Long = 30L,
|
||||
proxy: ProxyPreferences
|
||||
): OkHttpClient {
|
||||
val ouinetService = Proxy(Proxy.Type.HTTP, InetSocketAddress("127.0.0.1", 8077))
|
||||
val builder: OkHttpClient.Builder = OkHttpClient.Builder().proxy(ouinetService)
|
||||
|
||||
val builder: OkHttpClient.Builder = when(proxy) {
|
||||
ProxyPreferences.NoProxy -> OkHttpClient.Builder()
|
||||
ProxyPreferences.OuinetBasic, ProxyPreferences.OuinetP2P -> {
|
||||
val ouinetService = Proxy(Proxy.Type.HTTP, InetSocketAddress("127.0.0.1", 8077))
|
||||
OkHttpClient.Builder().proxy(ouinetService)
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheDirectory != null) {
|
||||
builder.cache(Cache(cacheDirectory, cacheSize))
|
||||
|
|
|
@ -32,6 +32,9 @@ val Context.initialFilter: Int
|
|||
val Context.languages: Int
|
||||
get() = this.dataStore.get(DataStoreKeys.Languages) ?: 0
|
||||
|
||||
val Context.proxy: Int
|
||||
get() = this.dataStore.get(DataStoreKeys.Proxy) ?: 0
|
||||
|
||||
suspend fun <T> DataStore<Preferences>.put(dataStoreKeys: DataStoreKeys<T>, value: T) {
|
||||
this.edit {
|
||||
withContext(Dispatchers.IO) {
|
||||
|
@ -398,4 +401,10 @@ sealed class DataStoreKeys<T> {
|
|||
override val key: Preferences.Key<Int>
|
||||
get() = intPreferencesKey("languages")
|
||||
}
|
||||
|
||||
// Proxy
|
||||
object Proxy : DataStoreKeys<Int>() {
|
||||
override val key: Preferences.Key<Int>
|
||||
get() = intPreferencesKey("proxy")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import me.ash.reader.ui.page.settings.color.flow.FlowPageStylePage
|
|||
import me.ash.reader.ui.page.settings.color.reading.*
|
||||
import me.ash.reader.ui.page.settings.interaction.InteractionPage
|
||||
import me.ash.reader.ui.page.settings.languages.LanguagesPage
|
||||
import me.ash.reader.ui.page.settings.proxy.ProxyPage
|
||||
import me.ash.reader.ui.page.settings.tips.TipsAndSupportPage
|
||||
import me.ash.reader.ui.page.startup.StartupPage
|
||||
import me.ash.reader.ui.theme.AppTheme
|
||||
|
@ -198,6 +199,11 @@ fun HomeEntry(
|
|||
LanguagesPage(navController = navController)
|
||||
}
|
||||
|
||||
// Proxy
|
||||
animatedComposable(route = RouteName.PROXY) {
|
||||
ProxyPage(navController = navController)
|
||||
}
|
||||
|
||||
// Tips & Support
|
||||
animatedComposable(route = RouteName.TIPS_AND_SUPPORT) {
|
||||
TipsAndSupportPage(navController)
|
||||
|
|
|
@ -38,4 +38,7 @@ object RouteName {
|
|||
|
||||
// Tips & Support
|
||||
const val TIPS_AND_SUPPORT = "tips_and_support"
|
||||
|
||||
// Proxy
|
||||
const val PROXY = "proxy"
|
||||
}
|
||||
|
|
|
@ -142,6 +142,17 @@ fun SettingsPage(
|
|||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
SelectableSettingGroupItem(
|
||||
title = stringResource(R.string.proxy),
|
||||
desc = stringResource(R.string.proxy_desc),
|
||||
icon = Icons.Outlined.SettingsEthernet,
|
||||
) {
|
||||
navController.navigate(RouteName.PROXY) {
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package me.ash.reader.ui.page.settings.proxy
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.outlined.Lightbulb
|
||||
import androidx.compose.material.icons.rounded.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.data.model.preference.Proxy
|
||||
import me.ash.reader.data.model.preference.ProxyPreferences
|
||||
import me.ash.reader.ui.component.base.Banner
|
||||
import me.ash.reader.ui.component.base.DisplayText
|
||||
import me.ash.reader.ui.component.base.FeedbackIconButton
|
||||
import me.ash.reader.ui.component.base.RYScaffold
|
||||
import me.ash.reader.ui.page.settings.SettingItem
|
||||
import me.ash.reader.ui.theme.palette.onLight
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ProxyPage(
|
||||
navController: NavHostController,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val proxy = Proxy.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
RYScaffold(
|
||||
containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
|
||||
navigationIcon = {
|
||||
FeedbackIconButton(
|
||||
imageVector = Icons.Rounded.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back),
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
) {
|
||||
navController.popBackStack()
|
||||
}
|
||||
},
|
||||
content = {
|
||||
LazyColumn {
|
||||
item(key = proxy.value) {
|
||||
DisplayText(text = stringResource(R.string.proxy), desc = "")
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
item {
|
||||
ProxyPreferences.values.map {
|
||||
SettingItem(
|
||||
title = it.toDesc(context),
|
||||
onClick = {
|
||||
it.put(context, scope)
|
||||
},
|
||||
) {
|
||||
RadioButton(selected = it == proxy, onClick = {
|
||||
it.put(context, scope)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
|
@ -117,6 +117,11 @@
|
|||
<string name="basque" translatable="false">Euskara</string>
|
||||
<string name="indonesian" translatable="false">Bahasa Indonesia</string>
|
||||
<string name="chinese_traditional" translatable="false">繁體中文</string>
|
||||
<string name="proxy">Proxy</string>
|
||||
<string name="proxy_desc">Network access settings (requires restart)</string>
|
||||
<string name="proxy_system">System settings</string>
|
||||
<string name="proxy_ouinet_basic">Ouinet (default settings)</string>
|
||||
<string name="proxy_ouinet_p2p">Ouinet (no direct connection)</string>
|
||||
<string name="tips_and_support">Tips & support</string>
|
||||
<string name="tips_and_support_desc">About, open source licenses</string>
|
||||
<string name="welcome">Welcome</string>
|
||||
|
|
|
@ -42,8 +42,8 @@ buildscript {
|
|||
}
|
||||
|
||||
plugins {
|
||||
id 'com.android.application' version '7.1.1' apply false
|
||||
id 'com.android.library' version '7.1.1' apply false
|
||||
id 'com.android.application' version '7.3.1' apply false
|
||||
id 'com.android.library' version '7.3.1' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.6.21' apply false
|
||||
}
|
||||
|
||||
|
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
#Wed Apr 20 21:38:37 CST 2022
|
||||
#Tue Nov 29 14:07:40 CET 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
Loading…
Reference in New Issue
Block a user