close #901
This commit is contained in:
Mariotaku Lee 2017-12-02 15:23:16 +08:00
parent e3f6ecda80
commit 9b46b0a41b
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
10 changed files with 262 additions and 69 deletions

View File

@ -125,10 +125,10 @@ public interface SharedPreferenceConstants {
@ExportablePreference(BOOLEAN)
String KEY_ENABLE_PROXY = "enable_proxy";
@ExportablePreference(STRING)
String KEY_PROXY_HOST = "proxy_host";
@ExportablePreference(STRING)
String KEY_PROXY_TYPE = "proxy_type";
@ExportablePreference(STRING)
String KEY_PROXY_ADDRESS = "proxy_address";
String KEY_PROXY_HOST = "proxy_host";
String KEY_PROXY_PORT = "proxy_port";
@ExportablePreference(STRING)
String KEY_PROXY_USERNAME = "proxy_username";

View File

@ -0,0 +1,32 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.annotation;
import android.support.annotation.StringDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.SOURCE)
@StringDef({ProxyType.HTTP, ProxyType.REVERSE})
public @interface ProxyType {
String HTTP = "http";
String REVERSE = "reverse";
}

View File

@ -7,16 +7,15 @@ import android.support.v4.util.ArraySet
import android.text.TextUtils
import org.mariotaku.kpreferences.*
import org.mariotaku.ktextension.bcp47Tag
import org.mariotaku.ktextension.toIntOr
import org.mariotaku.ktextension.toLongOr
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.*
import org.mariotaku.twidere.TwidereConstants.KEY_MEDIA_PRELOAD
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.annotation.ImageShapeStyle
import org.mariotaku.twidere.annotation.NavbarStyle
import org.mariotaku.twidere.annotation.PreviewStyle
import org.mariotaku.twidere.annotation.*
import org.mariotaku.twidere.extension.getNonEmptyString
import org.mariotaku.twidere.model.CustomAPIConfig
import org.mariotaku.twidere.model.ProxySettings
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.account.cred.Credentials
import org.mariotaku.twidere.model.timeline.UserTimelineFilter
@ -255,7 +254,7 @@ object defaultAccountKey : KSimpleKey<UserKey?>(KEY_DEFAULT_ACCOUNT_KEY, null) {
}
}
object userTimelineFilterKey : KSimpleKey<UserTimelineFilter>("user_timeline_filter", UserTimelineFilter()) {
val userTimelineFilterKey = object : KSimpleKey<UserTimelineFilter>("user_timeline_filter", UserTimelineFilter()) {
override fun read(preferences: SharedPreferences): UserTimelineFilter {
val rawString = preferences.getString(key, null) ?: return def
val options = rawString.split(",")
@ -278,4 +277,58 @@ object userTimelineFilterKey : KSimpleKey<UserTimelineFilter>("user_timeline_fil
return true
}
}
val proxyKey = object : KPreferenceKey<ProxySettings?> {
override fun contains(preferences: SharedPreferences): Boolean {
return read(preferences) != null
}
override fun read(preferences: SharedPreferences): ProxySettings? {
when (preferences.getString(KEY_PROXY_TYPE, null)) {
ProxyType.HTTP -> {
val address = preferences.getString(KEY_PROXY_ADDRESS, null)
val host: String
val port: Int
if (address == null) {
host = preferences.getString(KEY_PROXY_HOST, null) ?: return null
port = preferences.getString(KEY_PROXY_PORT, null).toIntOr(-1)
} else {
host = address.substringBefore(':', "")
port = address.substringAfter(':', "").toIntOr(-1)
}
// Simple validation against wrong values
if (host.isEmpty() || port !in 0..65535) return null
val username = preferences.getString(KEY_PROXY_USERNAME, null)
val password = preferences.getString(KEY_PROXY_PASSWORD, null)
return ProxySettings.Http(host, port, username, password)
}
ProxyType.REVERSE -> {
val address = preferences.getString(KEY_PROXY_ADDRESS, null) ?:
preferences.getString(KEY_PROXY_HOST, null) ?: return null
val username = preferences.getString(KEY_PROXY_USERNAME, null)
val password = preferences.getString(KEY_PROXY_PASSWORD, null)
return ProxySettings.Reverse(address, username, password)
}
else -> return null
}
}
override fun write(editor: SharedPreferences.Editor, value: ProxySettings?): Boolean {
if (value == null) {
remove(editor)
} else {
value.write(editor)
}
return true
}
fun remove(editor: SharedPreferences.Editor) {
editor.remove(KEY_PROXY_ADDRESS)
editor.remove(KEY_PROXY_HOST)
editor.remove(KEY_PROXY_PORT)
editor.remove(KEY_PROXY_USERNAME)
editor.remove(KEY_PROXY_PASSWORD)
}
}

View File

@ -158,7 +158,7 @@ class ApplicationModule(private val application: Application) {
cache: Cache, notifier: PreferenceChangeNotifier): RestHttpClient {
val conf = HttpClientFactory.HttpClientConfiguration(prefs)
val client = HttpClientFactory.createRestHttpClient(conf, dns, connectionPool, cache)
notifier.register(KEY_ENABLE_PROXY, KEY_PROXY_HOST, KEY_PROXY_PORT, KEY_PROXY_TYPE,
notifier.register(KEY_ENABLE_PROXY, KEY_PROXY_ADDRESS, KEY_PROXY_TYPE,
KEY_PROXY_USERNAME, KEY_PROXY_PASSWORD, KEY_CONNECTION_TIMEOUT,
KEY_RETRY_ON_NETWORK_ISSUE) changed@ {
if (client !is OkHttpRestClient) return@changed

View File

@ -0,0 +1,95 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.model
import android.content.SharedPreferences
import android.support.annotation.IntRange
import okhttp3.Credentials
import okhttp3.OkHttpClient
import org.mariotaku.twidere.annotation.ProxyType
import org.mariotaku.twidere.constant.SharedPreferenceConstants.*
import org.mariotaku.twidere.util.HttpClientFactory
import java.net.InetSocketAddress
import java.net.Proxy
interface ProxySettings {
@ProxyType
val type: String
fun apply(builder: OkHttpClient.Builder)
fun write(editor: SharedPreferences.Editor)
class Http(
val host: String,
@IntRange(from = 0, to = 65535) val port: Int,
val username: String?,
val password: String?
) : ProxySettings {
@ProxyType
override val type: String = ProxyType.HTTP
override fun apply(builder: OkHttpClient.Builder) {
val address = InetSocketAddress.createUnresolved(host, port)
builder.proxy(Proxy(Proxy.Type.HTTP, address))
builder.authenticator { _, response ->
val b = response.request().newBuilder()
if (response.code() == 407) {
if (username != null && password != null) {
val credential = Credentials.basic(username, password)
b.header("Proxy-Authorization", credential)
}
}
b.build()
}
}
override fun write(editor: SharedPreferences.Editor) {
editor.putString(KEY_PROXY_ADDRESS, "$host:$port")
editor.putString(KEY_PROXY_USERNAME, username)
editor.putString(KEY_PROXY_PASSWORD, password)
}
}
class Reverse(
val url: String,
val username: String?,
val password: String?
) : ProxySettings {
@ProxyType
override val type: String = ProxyType.REVERSE
override fun apply(builder: OkHttpClient.Builder) {
builder.addInterceptor(HttpClientFactory.ReverseProxyInterceptor(url, username, password))
}
override fun write(editor: SharedPreferences.Editor) {
editor.putString(KEY_PROXY_TYPE, type)
editor.putString(KEY_PROXY_ADDRESS, url)
editor.putString(KEY_PROXY_USERNAME, username)
editor.putString(KEY_PROXY_PASSWORD, password)
}
}
}

View File

@ -27,10 +27,7 @@ import android.util.AttributeSet
import org.mariotaku.twidere.fragment.preference.ThemedEditTextPreferenceDialogFragmentCompat
import org.mariotaku.twidere.preference.iface.IDialogPreference
/**
* Created by mariotaku on 16/3/15.
*/
class ThemedEditTextPreference(context: Context, attrs: AttributeSet? = null) :
open class ThemedEditTextPreference(context: Context, attrs: AttributeSet? = null) :
EditTextPreference(context, attrs), IDialogPreference {
override fun displayDialog(fragment: PreferenceFragmentCompat) {

View File

@ -0,0 +1,56 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.preference.network
import android.content.Context
import android.support.v7.preference.PreferenceManager
import android.util.AttributeSet
import org.mariotaku.ktextension.toIntOr
import org.mariotaku.twidere.annotation.ProxyType
import org.mariotaku.twidere.constant.SharedPreferenceConstants.*
import org.mariotaku.twidere.preference.ThemedEditTextPreference
class ProxyAddressPreference(context: Context, attrs: AttributeSet?) : ThemedEditTextPreference(context, attrs) {
override fun onAttachedToHierarchy(preferenceManager: PreferenceManager) {
val prefs = preferenceManager.sharedPreferences
if (KEY_PROXY_ADDRESS !in prefs) {
when (prefs.getString(KEY_PROXY_TYPE, null)) {
ProxyType.HTTP -> {
val host = prefs.getString(KEY_PROXY_HOST, null)?.takeIf(String::isNotEmpty)
val port = prefs.getString(KEY_PROXY_PORT, null).toIntOr(-1)
if (host != null && port in 0..65535) {
setDefaultValue("$host:$port")
}
}
ProxyType.REVERSE -> {
val address = prefs.getString(KEY_PROXY_HOST, null)
setDefaultValue(address)
}
}
}
super.onAttachedToHierarchy(preferenceManager)
}
override fun setText(text: String?) {
super.setText(text)
summary = text
}
}

View File

@ -8,15 +8,14 @@ import android.util.Log
import okhttp3.*
import okhttp3.internal.platform.Platform
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.toIntOr
import org.mariotaku.restfu.http.RestHttpClient
import org.mariotaku.restfu.okhttp3.OkHttpRestClient
import org.mariotaku.twidere.constant.SharedPreferenceConstants.*
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_CONNECTION_TIMEOUT
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_ENABLE_PROXY
import org.mariotaku.twidere.constant.cacheSizeLimitKey
import org.mariotaku.twidere.constant.proxyKey
import org.mariotaku.twidere.util.net.TLSSocketFactory
import java.io.IOException
import java.net.InetSocketAddress
import java.net.Proxy
import java.security.KeyStore
import java.security.NoSuchAlgorithmException
import java.util.*
@ -26,9 +25,6 @@ import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
/**
* Created by mariotaku on 16/1/27.
*/
object HttpClientFactory {
fun createRestHttpClient(conf: HttpClientConfiguration, dns: Dns, connectionPool: ConnectionPool,
@ -201,42 +197,10 @@ object HttpClientFactory {
builder.readTimeout(readTimeoutSecs, TimeUnit.SECONDS)
}
if (prefs.getBoolean(KEY_ENABLE_PROXY, false)) {
configProxy(builder)
prefs[proxyKey]?.apply(builder)
}
}
private fun configProxy(builder: OkHttpClient.Builder) {
val proxyType = prefs.getString(KEY_PROXY_TYPE, null) ?: return
val proxyHost = prefs.getString(KEY_PROXY_HOST, null)?.takeIf(String::isNotEmpty) ?: return
val proxyPort = prefs.getString(KEY_PROXY_PORT, null).toIntOr(-1)
val username = prefs.getString(KEY_PROXY_USERNAME, null)?.takeIf(String::isNotEmpty)
val password = prefs.getString(KEY_PROXY_PASSWORD, null)?.takeIf(String::isNotEmpty)
when (proxyType) {
"http" -> {
if (proxyPort !in (0..65535)) {
return
}
val address = InetSocketAddress.createUnresolved(proxyHost, proxyPort)
builder.proxy(Proxy(Proxy.Type.HTTP, address))
builder.authenticator { _, response ->
val b = response.request().newBuilder()
if (response.code() == 407) {
if (username != null && password != null) {
val credential = Credentials.basic(username, password)
b.header("Proxy-Authorization", credential)
}
}
b.build()
}
}
"reverse" -> {
builder.addInterceptor(ReverseProxyInterceptor(proxyHost, username, password))
}
}
}
}
/**

View File

@ -268,11 +268,11 @@
<string name="content">Content</string>
<string name="content_and_storage">Content &amp; Storage</string>
<string name="content_description_accounts_selector_current">Selected account is <xliff:g id="name">%1$s</xliff:g></string>
<string name="content_description_item_status"><xliff:g id="name">%1$s</xliff:g> <xliff:g id="time_with_preposition">%2$s</xliff:g>: <xliff:g id="content">%3$s</xliff:g></string>
<string name="content_description_item_status_reply"><xliff:g id="name">%1$s</xliff:g> replying to <xliff:g id="in_reply_to">%2$s</xliff:g> <xliff:g id="time_with_preposition">%3$s</xliff:g>: <xliff:g id="content">%4$s</xliff:g></string>
<string name="content_description_item_status_retweet">Retweeted by <xliff:g id="retweeter">%1$s</xliff:g>. <xliff:g id="name">%2$s</xliff:g> <xliff:g id="time_with_preposition">%3$s</xliff:g>: <xliff:g id="content">%4$s</xliff:g></string>
<string name="content_description_sticker">Sticker</string>
<string name="content_description_item_status"><xliff:g id="name">%1$s</xliff:g><xliff:g id="time_with_preposition">%2$s</xliff:g>: <xliff:g id="content">%3$s</xliff:g></string>
<string name="content_description_item_status_reply"><xliff:g id="name">%1$s</xliff:g> replying to <xliff:g id="in_reply_to">%2$s</xliff:g><xliff:g id="time_with_preposition">%3$s</xliff:g>: <xliff:g id="content">%4$s</xliff:g></string>
<string name="content_description_item_status_retweet">Retweeted by <xliff:g id="retweeter">%1$s</xliff:g>. <xliff:g id="name">%2$s</xliff:g><xliff:g id="time_with_preposition">%3$s</xliff:g>: <xliff:g id="content">%4$s</xliff:g></string>
<string name="content_description_open_user_name_profile">Open <xliff:g id="name">%1$s</xliff:g>\'s profile</string>
<string name="content_description_sticker">Sticker</string>
<string name="content_to_notify">Content to notify</string>
<string name="content_to_refresh">Content to refresh</string>
@ -585,6 +585,7 @@
<string name="label_statuses_retweets">Tweets and retweets</string>
<string name="label_statuses_retweets_replies">Tweets, retweets and replies</string>
<string name="label_streaming_service">Streaming service</string>
<string name="label_timeline_style">Timeline style</string>
<string name="label_translate_from_language">Translate from <xliff:g id="language">%s</xliff:g></string>
<string name="label_translated_to_language">Translated to <xliff:g id="language">%s</xliff:g></string>
<string name="label_translation">Translation</string>
@ -931,6 +932,7 @@
<string name="preference_title_notification_ringtone">Ringtone</string>
<string name="preference_title_override_language">App language</string>
<string name="preference_title_portrait">Portrait</string>
<string name="preference_title_proxy_address">Proxy address</string>
<string name="preference_title_storage">Storage</string>
<string name="preference_title_streaming_content">Streaming content</string>
<string name="preference_title_streaming_enabled">Enable streaming</string>
@ -1214,6 +1216,9 @@
<string name="time_source"><xliff:g id="time">%1$s</xliff:g> · <xliff:g id="source">%2$s</xliff:g></string>
<string name="timeline_streaming_running">Timeline streaming running</string>
<string name="timeline_style_gallery">Gallery</string>
<string name="timeline_style_normal">Normal</string>
<string name="timeline_style_staggered">Staggered</string>
<string name="timeline_sync_service">Timeline sync service</string>
<string name="title_about">About</string>
@ -1380,8 +1385,4 @@
<string name="users_blocked">Blocked these users.</string>
<string name="users_lists_with_name"><xliff:g id="name">%s</xliff:g>\'s lists</string>
<string name="label_timeline_style">Timeline style</string>
<string name="timeline_style_normal">Normal</string>
<string name="timeline_style_staggered">Staggered</string>
<string name="timeline_style_gallery">Gallery</string>
</resources>

View File

@ -53,17 +53,12 @@
android:entryValues="@array/values_proxy_type"
android:key="proxy_type"
android:title="@string/proxy_type"/>
<org.mariotaku.twidere.preference.ThemedEditTextPreference
<org.mariotaku.twidere.preference.network.ProxyAddressPreference
android:dependency="enable_proxy"
android:key="proxy_host"
android:inputType="textEmailAddress"
android:key="proxy_address"
android:singleLine="true"
android:title="@string/proxy_host"/>
<org.mariotaku.twidere.preference.ThemedEditTextPreference
android:dependency="enable_proxy"
android:inputType="number"
android:key="proxy_port"
android:singleLine="true"
android:title="@string/proxy_port"/>
android:title="@string/preference_title_proxy_address"/>
<org.mariotaku.twidere.preference.ThemedEditTextPreference
android:dependency="enable_proxy"
android:inputType="textEmailAddress"