Add proxy settings

This commit is contained in:
ceno challenge 2022-11-29 20:24:58 +01:00
parent 9311f90fc9
commit 89c0822158
13 changed files with 222 additions and 17 deletions

View File

@ -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(

View File

@ -77,5 +77,8 @@ fun Preferences.toSettings(): Settings {
// Languages
languages = LanguagesPreference.fromPreferences(this),
// Proxy
proxy = ProxyPreferences.fromPreferences(this),
)
}

View File

@ -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
}
}
}

View File

@ -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()
}

View File

@ -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))

View File

@ -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")
}
}

View File

@ -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)

View File

@ -38,4 +38,7 @@ object RouteName {
// Tips & Support
const val TIPS_AND_SUPPORT = "tips_and_support"
// Proxy
const val PROXY = "proxy"
}

View File

@ -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))

View File

@ -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))
}
}
}
)
}

View File

@ -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 &amp; support</string>
<string name="tips_and_support_desc">About, open source licenses</string>
<string name="welcome">Welcome</string>

View File

@ -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
}

View File

@ -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