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.dao.AccountDao
|
||||||
import me.ash.reader.data.model.preference.AccountSettingsProvider
|
import me.ash.reader.data.model.preference.AccountSettingsProvider
|
||||||
import me.ash.reader.data.model.preference.LanguagesPreference
|
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.data.model.preference.SettingsProvider
|
||||||
import me.ash.reader.ui.ext.languages
|
import me.ash.reader.ui.ext.languages
|
||||||
|
import me.ash.reader.ui.ext.proxy
|
||||||
import me.ash.reader.ui.page.common.HomeEntry
|
import me.ash.reader.ui.page.common.HomeEntry
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -25,7 +27,6 @@ import javax.inject.Inject
|
||||||
*/
|
*/
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
private lateinit var ouinet: Ouinet
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var imageLoader: ImageLoader
|
lateinit var imageLoader: ImageLoader
|
||||||
|
@ -44,20 +45,27 @@ class MainActivity : ComponentActivity() {
|
||||||
it.setLocale(this)
|
it.setLocale(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable OuiNet client
|
// If a proxy config is set, start ouinet service
|
||||||
val config = Config.ConfigBuilder(this)
|
if (ProxyPreferences.fromValue(proxy) != ProxyPreferences.NoProxy) {
|
||||||
|
val ouinetBuilder = Config.ConfigBuilder(this)
|
||||||
.setCacheType("bep5-http")
|
.setCacheType("bep5-http")
|
||||||
.setCacheHttpPubKey(BuildConfig.CACHE_PUB_KEY)
|
.setCacheHttpPubKey(BuildConfig.CACHE_PUB_KEY)
|
||||||
.setInjectorCredentials(BuildConfig.INJECTOR_CREDENTIALS)
|
.setInjectorCredentials(BuildConfig.INJECTOR_CREDENTIALS)
|
||||||
.setInjectorTlsCert(BuildConfig.INJECTOR_TLS_CERT)
|
.setInjectorTlsCert(BuildConfig.INJECTOR_TLS_CERT)
|
||||||
.setTlsCaCertStorePath("file:///android_asset/cacert.pem")
|
.setTlsCaCertStorePath("file:///android_asset/cacert.pem")
|
||||||
.setDisableOriginAccess(true)
|
val proxyConfig = when (ProxyPreferences.fromValue(proxy)) {
|
||||||
//.setDisableProxyAccess(true)
|
ProxyPreferences.OuinetBasic -> ouinetBuilder.build()
|
||||||
//.setDisableInjectorAccess(true)
|
ProxyPreferences.OuinetP2P -> {
|
||||||
.build()
|
ouinetBuilder.setDisableOriginAccess(true).build()
|
||||||
|
}
|
||||||
ouinet = Ouinet(this, config)
|
ProxyPreferences.NoProxy -> {
|
||||||
|
// Cannot happen, but when should be exhaustive
|
||||||
|
ouinetBuilder.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val ouinet = Ouinet(this, proxyConfig)
|
||||||
ouinet.start()
|
ouinet.start()
|
||||||
|
}
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
|
|
|
@ -77,5 +77,8 @@ fun Preferences.toSettings(): Settings {
|
||||||
|
|
||||||
// Languages
|
// Languages
|
||||||
languages = LanguagesPreference.fromPreferences(this),
|
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
|
// Languages
|
||||||
val languages: LanguagesPreference = LanguagesPreference.default,
|
val languages: LanguagesPreference = LanguagesPreference.default,
|
||||||
|
|
||||||
|
// Proxy
|
||||||
|
val proxy: ProxyPreferences = ProxyPreferences.default
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version
|
// Version
|
||||||
|
@ -172,6 +175,10 @@ val LocalInitialFilter =
|
||||||
val LocalLanguages =
|
val LocalLanguages =
|
||||||
compositionLocalOf<LanguagesPreference> { LanguagesPreference.default }
|
compositionLocalOf<LanguagesPreference> { LanguagesPreference.default }
|
||||||
|
|
||||||
|
// Ouinet settings
|
||||||
|
val Proxy =
|
||||||
|
compositionLocalOf<ProxyPreferences> { ProxyPreferences.default }
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsProvider(
|
fun SettingsProvider(
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
|
@ -249,6 +256,9 @@ fun SettingsProvider(
|
||||||
|
|
||||||
// Languages
|
// Languages
|
||||||
LocalLanguages provides settings.languages,
|
LocalLanguages provides settings.languages,
|
||||||
|
|
||||||
|
// Proxy
|
||||||
|
Proxy provides settings.proxy,
|
||||||
) {
|
) {
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.ash.reader.data.module
|
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 android.content.Context
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
@ -56,7 +58,8 @@ object OkHttpClientModule {
|
||||||
fun provideOkHttpClient(
|
fun provideOkHttpClient(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
): OkHttpClient = cachingHttpClient(
|
): OkHttpClient = cachingHttpClient(
|
||||||
cacheDirectory = context.cacheDir.resolve("http")
|
cacheDirectory = context.cacheDir.resolve("http"),
|
||||||
|
proxy = ProxyPreferences.fromValue(context.proxy)
|
||||||
).newBuilder()
|
).newBuilder()
|
||||||
.addNetworkInterceptor(UserAgentInterceptor)
|
.addNetworkInterceptor(UserAgentInterceptor)
|
||||||
.build()
|
.build()
|
||||||
|
@ -68,9 +71,16 @@ fun cachingHttpClient(
|
||||||
trustAllCerts: Boolean = true,
|
trustAllCerts: Boolean = true,
|
||||||
connectTimeoutSecs: Long = 30L,
|
connectTimeoutSecs: Long = 30L,
|
||||||
readTimeoutSecs: Long = 30L,
|
readTimeoutSecs: Long = 30L,
|
||||||
|
proxy: ProxyPreferences
|
||||||
): OkHttpClient {
|
): OkHttpClient {
|
||||||
|
|
||||||
|
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))
|
val ouinetService = Proxy(Proxy.Type.HTTP, InetSocketAddress("127.0.0.1", 8077))
|
||||||
val builder: OkHttpClient.Builder = OkHttpClient.Builder().proxy(ouinetService)
|
OkHttpClient.Builder().proxy(ouinetService)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cacheDirectory != null) {
|
if (cacheDirectory != null) {
|
||||||
builder.cache(Cache(cacheDirectory, cacheSize))
|
builder.cache(Cache(cacheDirectory, cacheSize))
|
||||||
|
|
|
@ -32,6 +32,9 @@ val Context.initialFilter: Int
|
||||||
val Context.languages: Int
|
val Context.languages: Int
|
||||||
get() = this.dataStore.get(DataStoreKeys.Languages) ?: 0
|
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) {
|
suspend fun <T> DataStore<Preferences>.put(dataStoreKeys: DataStoreKeys<T>, value: T) {
|
||||||
this.edit {
|
this.edit {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
@ -398,4 +401,10 @@ sealed class DataStoreKeys<T> {
|
||||||
override val key: Preferences.Key<Int>
|
override val key: Preferences.Key<Int>
|
||||||
get() = intPreferencesKey("languages")
|
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.color.reading.*
|
||||||
import me.ash.reader.ui.page.settings.interaction.InteractionPage
|
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.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.settings.tips.TipsAndSupportPage
|
||||||
import me.ash.reader.ui.page.startup.StartupPage
|
import me.ash.reader.ui.page.startup.StartupPage
|
||||||
import me.ash.reader.ui.theme.AppTheme
|
import me.ash.reader.ui.theme.AppTheme
|
||||||
|
@ -198,6 +199,11 @@ fun HomeEntry(
|
||||||
LanguagesPage(navController = navController)
|
LanguagesPage(navController = navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Proxy
|
||||||
|
animatedComposable(route = RouteName.PROXY) {
|
||||||
|
ProxyPage(navController = navController)
|
||||||
|
}
|
||||||
|
|
||||||
// Tips & Support
|
// Tips & Support
|
||||||
animatedComposable(route = RouteName.TIPS_AND_SUPPORT) {
|
animatedComposable(route = RouteName.TIPS_AND_SUPPORT) {
|
||||||
TipsAndSupportPage(navController)
|
TipsAndSupportPage(navController)
|
||||||
|
|
|
@ -38,4 +38,7 @@ object RouteName {
|
||||||
|
|
||||||
// Tips & Support
|
// Tips & Support
|
||||||
const val TIPS_AND_SUPPORT = "tips_and_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 {
|
item {
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
|
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="basque" translatable="false">Euskara</string>
|
||||||
<string name="indonesian" translatable="false">Bahasa Indonesia</string>
|
<string name="indonesian" translatable="false">Bahasa Indonesia</string>
|
||||||
<string name="chinese_traditional" translatable="false">繁體中文</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">Tips & support</string>
|
||||||
<string name="tips_and_support_desc">About, open source licenses</string>
|
<string name="tips_and_support_desc">About, open source licenses</string>
|
||||||
<string name="welcome">Welcome</string>
|
<string name="welcome">Welcome</string>
|
||||||
|
|
|
@ -42,8 +42,8 @@ buildscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application' version '7.1.1' apply false
|
id 'com.android.application' version '7.3.1' apply false
|
||||||
id 'com.android.library' version '7.1.1' apply false
|
id 'com.android.library' version '7.3.1' apply false
|
||||||
id 'org.jetbrains.kotlin.android' version '1.6.21' 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
|
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
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
Loading…
Reference in New Issue
Block a user