Move NAT/VPN switch to a more user-friendly place
This commit is contained in:
@@ -38,16 +38,16 @@
|
||||
<string name="service_running">已连接到「%s」</string>
|
||||
<string name="service_failed">无法连接远程服务器</string>
|
||||
<string name="stop">停止服务</string>
|
||||
<string name="stopping">正在关闭...</string>
|
||||
<string name="stopping">正在关闭……</string>
|
||||
<string name="vpn_error">后台服务启动失败:%s</string>
|
||||
<string name="close">关闭</string>
|
||||
<string name="port_alert">本地端口号应大于1024</string>
|
||||
<string name="port_empty">端口号不能为空</string>
|
||||
<string name="proxy_empty">代理服务器地址不能为空</string>
|
||||
<string name="connect">连接</string>
|
||||
<string name="initializing">正在初始化...</string>
|
||||
<string name="recovering">重置中...</string>
|
||||
<string name="loading">正在加载...</string>
|
||||
<string name="initializing">正在初始化……</string>
|
||||
<string name="recovering">重置中……</string>
|
||||
<string name="loading">正在加载……</string>
|
||||
<string name="profiles">配置文件</string>
|
||||
<string name="settings">设置选项</string>
|
||||
<string name="remove_profile">删除此配置文件「%s」?</string>
|
||||
@@ -55,13 +55,10 @@
|
||||
<string name="about">关于</string>
|
||||
<string name="about_title">影梭(shadowsocks)%s</string>
|
||||
<string name="flush_dnscache">刷新 DNS 缓存</string>
|
||||
<string name="flushing">刷新中...</string>
|
||||
<string name="flushing">刷新中……</string>
|
||||
<string name="qrcode">二维码</string>
|
||||
<string name="add_profile">添加配置文件</string>
|
||||
|
||||
<string name="enable_nat">启用 NAT 模式.</string>
|
||||
<string name="disable_nat">禁用 NAT 模式.</string>
|
||||
|
||||
<string name="udp_dns">UDP 转发</string>
|
||||
<string name="udp_dns_summary">由远程服务器转发 UDP 协议的数据包</string>
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
<string name="global_cat">Global Settings</string>
|
||||
<string name="profile">Profile</string>
|
||||
<string name="profile_summary">Switch to another profile</string>
|
||||
<string name="nat">NAT mode</string>
|
||||
<string name="nat_summary">Use NAT/VPN mode.</string>
|
||||
<string name="nat_summary_no_root">NAT mode is disabled without root.</string>
|
||||
|
||||
<!-- proxy category -->
|
||||
<string name="proxy_cat">Server Settings</string>
|
||||
@@ -55,10 +58,8 @@
|
||||
<string name="service_running">Connected to "%s"</string>
|
||||
<string name="service_failed">Failed to connect the remote server</string>
|
||||
<string name="stop">Stop the service</string>
|
||||
<string name="stopping">Shutting down...</string>
|
||||
<string name="stopping">Shutting down…</string>
|
||||
<string name="vpn_error">%s</string>
|
||||
<string name="enable_nat">Turn on NAT mode.</string>
|
||||
<string name="disable_nat">Turn off NAT mode.</string>
|
||||
|
||||
<!-- alert category -->
|
||||
<string name="close">Close</string>
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
<Preference android:summary="@string/profile_summary" android:title="@string/profile">
|
||||
<intent android:action="com.github.shadowsocks.ProfileManagerActivity"/>
|
||||
</Preference>
|
||||
<!-- TODO: VPN/NAT SwitchPreference -->
|
||||
<com.github.shadowsocks.preferences.NatSwitchPreference android:key="isNAT"
|
||||
android:title="@string/nat"
|
||||
android:summary="@string/nat_summary"/>
|
||||
<SwitchPreference
|
||||
android:key="isAutoConnect"
|
||||
android:summary="@string/auto_connect_summary"
|
||||
|
||||
@@ -197,8 +197,8 @@ class Shadowsocks
|
||||
if (fab != null) fab.setEnabled(true)
|
||||
stateUpdate()
|
||||
|
||||
if (!status.getBoolean(ShadowsocksApplication.getVersionName, false)) {
|
||||
status.edit.putBoolean(ShadowsocksApplication.getVersionName, true).apply()
|
||||
if (!ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)) {
|
||||
ShadowsocksApplication.settings.edit.putBoolean(ShadowsocksApplication.getVersionName, true).apply()
|
||||
recovery()
|
||||
}
|
||||
}
|
||||
@@ -216,7 +216,6 @@ class Shadowsocks
|
||||
|
||||
private lazy val preferences =
|
||||
getFragmentManager.findFragmentById(android.R.id.content).asInstanceOf[ShadowsocksSettings]
|
||||
private lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE)
|
||||
private lazy val greyTint = ContextCompat.getColorStateList(this, R.color.material_blue_grey_700)
|
||||
private lazy val greenTint = ContextCompat.getColorStateList(this, R.color.material_green_600)
|
||||
|
||||
@@ -340,7 +339,7 @@ class Shadowsocks
|
||||
|
||||
def prepareStartService() {
|
||||
Future {
|
||||
if (isVpnEnabled) {
|
||||
if (ShadowsocksApplication.isVpnEnabled) {
|
||||
val intent = VpnService.prepare(this)
|
||||
if (intent != null) {
|
||||
startActivityForResult(intent, Shadowsocks.REQUEST_CONNECT)
|
||||
@@ -374,7 +373,7 @@ class Shadowsocks
|
||||
}
|
||||
|
||||
// Update the profile
|
||||
if (!status.getBoolean(ShadowsocksApplication.getVersionName, false)) {
|
||||
if (!ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)) {
|
||||
currentProfile = ShadowsocksApplication.profileManager.create()
|
||||
}
|
||||
|
||||
@@ -396,21 +395,6 @@ class Shadowsocks
|
||||
ShadowsocksApplication.track(TAG, "about")
|
||||
showAbout()
|
||||
})
|
||||
title.setOnLongClickListener((v: View) => {
|
||||
if (Utils.isLollipopOrAbove && bgService != null
|
||||
&& (bgService.getState == State.INIT || bgService.getState == State.STOPPED)) {
|
||||
val natEnabled = !status.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove)
|
||||
status.edit().putBoolean(Key.isNAT, natEnabled).apply()
|
||||
deattachService()
|
||||
attachService()
|
||||
if (!Utils.isLollipopOrAbove) setPreferenceEnabled(State.isAvailable(bgService.getState))
|
||||
Toast.makeText(getBaseContext, if (natEnabled) R.string.enable_nat else R.string.disable_nat, Toast.LENGTH_LONG)
|
||||
.show()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
|
||||
fab = findViewById(R.id.fab).asInstanceOf[FloatingActionButton]
|
||||
fabProgressCircle = findViewById(R.id.fabProgressCircle).asInstanceOf[FABProgressCircle]
|
||||
@@ -446,7 +430,8 @@ class Shadowsocks
|
||||
|
||||
def attachService() {
|
||||
if (bgService == null) {
|
||||
val s = if (!isVpnEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService]
|
||||
val s = if (ShadowsocksApplication.isVpnEnabled) classOf[ShadowsocksVpnService]
|
||||
else classOf[ShadowsocksNatService]
|
||||
val intent = new Intent(this, s)
|
||||
intent.setAction(Action.SERVICE)
|
||||
bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
||||
@@ -526,6 +511,7 @@ class Shadowsocks
|
||||
}
|
||||
|
||||
private def setPreferenceEnabled(enabled: Boolean) {
|
||||
preferences.findPreference(Key.isNAT).setEnabled(enabled)
|
||||
for (name <- Shadowsocks.PROXY_PREFS) {
|
||||
val pref = preferences.findPreference(name)
|
||||
if (pref != null) {
|
||||
@@ -537,7 +523,7 @@ class Shadowsocks
|
||||
if (pref != null) {
|
||||
if (Seq(Key.isGlobalProxy, Key.proxyedApps)
|
||||
.contains(name)) {
|
||||
pref.setEnabled(enabled && (Utils.isLollipopOrAbove || !isVpnEnabled))
|
||||
pref.setEnabled(enabled && (Utils.isLollipopOrAbove || !ShadowsocksApplication.isVpnEnabled))
|
||||
} else {
|
||||
pref.setEnabled(enabled)
|
||||
}
|
||||
@@ -628,12 +614,6 @@ class Shadowsocks
|
||||
Log.e(Shadowsocks.TAG, "Failed to start VpnService")
|
||||
}
|
||||
|
||||
var isRoot: Option[Boolean] = None
|
||||
def isVpnEnabled: Boolean = {
|
||||
if (isRoot.isEmpty) isRoot = Some(Console.isRoot)
|
||||
!(isRoot.get && status.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove))
|
||||
}
|
||||
|
||||
def serviceStop() {
|
||||
if (bgService != null) bgService.stop()
|
||||
}
|
||||
@@ -666,7 +646,7 @@ class Shadowsocks
|
||||
def serviceStart() {
|
||||
bgService.start(ConfigUtils.load(ShadowsocksApplication.settings))
|
||||
|
||||
if (isVpnEnabled) {
|
||||
if (ShadowsocksApplication.isVpnEnabled) {
|
||||
changeSwitch(checked = false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ import android.app.Application
|
||||
import android.content.pm.PackageManager
|
||||
import android.preference.PreferenceManager
|
||||
import com.github.shadowsocks.database.{DBHelper, ProfileManager}
|
||||
import com.github.shadowsocks.utils.{Key, Utils}
|
||||
import com.google.android.gms.analytics.{HitBuilders, GoogleAnalytics}
|
||||
import com.github.shadowsocks.utils.{Console, Key, Utils}
|
||||
import com.google.android.gms.analytics.{GoogleAnalytics, HitBuilders}
|
||||
import com.google.android.gms.common.api.ResultCallback
|
||||
import com.google.android.gms.tagmanager.{ContainerHolder, TagManager}
|
||||
|
||||
@@ -60,6 +60,9 @@ object ShadowsocksApplication {
|
||||
lazy val settings = PreferenceManager.getDefaultSharedPreferences(instance)
|
||||
lazy val profileManager = new ProfileManager(settings, dbHelper)
|
||||
|
||||
val isRoot = Console.isRoot
|
||||
def isVpnEnabled = !(isRoot && settings.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove))
|
||||
|
||||
def getVersionName = try {
|
||||
instance.getPackageManager.getPackageInfo(instance.getPackageName, 0).versionName
|
||||
} catch {
|
||||
|
||||
@@ -39,12 +39,7 @@
|
||||
|
||||
package com.github.shadowsocks
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.preference.PreferenceManager
|
||||
import android.content.{BroadcastReceiver, Context, Intent}
|
||||
import com.github.shadowsocks.utils._
|
||||
|
||||
class ShadowsocksReceiver extends BroadcastReceiver {
|
||||
@@ -52,18 +47,8 @@ class ShadowsocksReceiver extends BroadcastReceiver {
|
||||
val TAG = "Shadowsocks"
|
||||
|
||||
def onReceive(context: Context, intent: Intent) {
|
||||
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val status = context.getSharedPreferences(Key.status, Context.MODE_PRIVATE)
|
||||
|
||||
var versionName: String = null
|
||||
try {
|
||||
versionName = context.getPackageManager.getPackageInfo(context.getPackageName, 0).versionName
|
||||
} catch {
|
||||
case e: PackageManager.NameNotFoundException =>
|
||||
versionName = "NONE"
|
||||
}
|
||||
val isAutoConnect: Boolean = settings.getBoolean(Key.isAutoConnect, false)
|
||||
val isInstalled: Boolean = status.getBoolean(versionName, false)
|
||||
val isAutoConnect: Boolean = ShadowsocksApplication.settings.getBoolean(Key.isAutoConnect, false)
|
||||
val isInstalled: Boolean = ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)
|
||||
if (isAutoConnect && isInstalled) {
|
||||
if (Utils.isLollipopOrAbove) {
|
||||
val intent = new Intent(context, classOf[ShadowsocksRunnerService])
|
||||
|
||||
@@ -40,19 +40,15 @@
|
||||
package com.github.shadowsocks
|
||||
|
||||
import android.app.{Activity, KeyguardManager}
|
||||
import android.os._
|
||||
import android.net.VpnService
|
||||
import android.content._
|
||||
import android.net.VpnService
|
||||
import android.os._
|
||||
import android.util.Log
|
||||
import android.preference.PreferenceManager
|
||||
import com.github.shadowsocks.utils._
|
||||
import com.github.shadowsocks.aidl.IShadowsocksService
|
||||
import com.github.shadowsocks.utils._
|
||||
|
||||
class ShadowsocksRunnerActivity extends Activity {
|
||||
|
||||
private lazy val settings = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
private lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE)
|
||||
|
||||
val handler = new Handler()
|
||||
val connection = new ServiceConnection {
|
||||
override def onServiceConnected(name: ComponentName, service: IBinder) {
|
||||
@@ -65,18 +61,12 @@ class ShadowsocksRunnerActivity extends Activity {
|
||||
}
|
||||
|
||||
// Variables
|
||||
var isRoot: Option[Boolean] = None
|
||||
var bgService: IShadowsocksService = _
|
||||
var receiver:BroadcastReceiver = _
|
||||
|
||||
def isVpnEnabled: Boolean = {
|
||||
if (isRoot.isEmpty) isRoot = Some(Console.isRoot)
|
||||
!(isRoot.get && status.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove))
|
||||
}
|
||||
|
||||
|
||||
def startBackgroundService() {
|
||||
if (isVpnEnabled) {
|
||||
if (ShadowsocksApplication.isVpnEnabled) {
|
||||
val intent = VpnService.prepare(ShadowsocksRunnerActivity.this)
|
||||
if (intent != null) {
|
||||
startActivityForResult(intent, Shadowsocks.REQUEST_CONNECT)
|
||||
@@ -84,14 +74,15 @@ class ShadowsocksRunnerActivity extends Activity {
|
||||
onActivityResult(Shadowsocks.REQUEST_CONNECT, Activity.RESULT_OK, null)
|
||||
}
|
||||
} else {
|
||||
bgService.start(ConfigUtils.load(settings))
|
||||
bgService.start(ConfigUtils.load(ShadowsocksApplication.settings))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
def attachService() {
|
||||
if (bgService == null) {
|
||||
val s = if (!isVpnEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService]
|
||||
val s = if (ShadowsocksApplication.isVpnEnabled) classOf[ShadowsocksVpnService]
|
||||
else classOf[ShadowsocksNatService]
|
||||
val intent = new Intent(this, s)
|
||||
intent.setAction(Action.SERVICE)
|
||||
bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
||||
@@ -137,7 +128,7 @@ class ShadowsocksRunnerActivity extends Activity {
|
||||
resultCode match {
|
||||
case Activity.RESULT_OK =>
|
||||
if (bgService != null) {
|
||||
bgService.start(ConfigUtils.load(settings))
|
||||
bgService.start(ConfigUtils.load(ShadowsocksApplication.settings))
|
||||
}
|
||||
case _ =>
|
||||
Log.e(Shadowsocks.TAG, "Failed to start VpnService")
|
||||
|
||||
@@ -43,15 +43,10 @@ import android.app.Service
|
||||
import android.content._
|
||||
import android.net.VpnService
|
||||
import android.os._
|
||||
import android.preference.PreferenceManager
|
||||
import com.github.shadowsocks.aidl.IShadowsocksService
|
||||
import com.github.shadowsocks.utils._
|
||||
|
||||
class ShadowsocksRunnerService extends Service {
|
||||
|
||||
private lazy val settings = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
private lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE)
|
||||
|
||||
val handler = new Handler()
|
||||
val connection = new ServiceConnection {
|
||||
override def onServiceConnected(name: ComponentName, service: IBinder) {
|
||||
@@ -64,35 +59,29 @@ class ShadowsocksRunnerService extends Service {
|
||||
}
|
||||
|
||||
// Variables
|
||||
var isRoot: Option[Boolean] = None
|
||||
var bgService: IShadowsocksService = _
|
||||
|
||||
def isVpnEnabled: Boolean = {
|
||||
if (isRoot.isEmpty) isRoot = Some(Console.isRoot)
|
||||
!(isRoot.get && status.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove))
|
||||
}
|
||||
|
||||
override def onBind(intent: Intent): IBinder = {
|
||||
null
|
||||
}
|
||||
|
||||
def startBackgroundService() {
|
||||
if (isVpnEnabled) {
|
||||
if (ShadowsocksApplication.isVpnEnabled) {
|
||||
val intent = VpnService.prepare(ShadowsocksRunnerService.this)
|
||||
if (intent == null) {
|
||||
if (bgService != null) {
|
||||
bgService.start(ConfigUtils.load(settings))
|
||||
bgService.start(ConfigUtils.load(ShadowsocksApplication.settings))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bgService.start(ConfigUtils.load(settings))
|
||||
bgService.start(ConfigUtils.load(ShadowsocksApplication.settings))
|
||||
}
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
def attachService() {
|
||||
if (bgService == null) {
|
||||
val s = if (!isVpnEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService]
|
||||
val s = if (!ShadowsocksApplication.isVpnEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService]
|
||||
val intent = new Intent(this, s)
|
||||
intent.setAction(Action.SERVICE)
|
||||
bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.github.shadowsocks
|
||||
|
||||
import android.os.Bundle
|
||||
import android.preference.{Preference, PreferenceFragment}
|
||||
import com.github.shadowsocks.utils.Key
|
||||
|
||||
// TODO: Move related logic here
|
||||
class ShadowsocksSettings extends PreferenceFragment {
|
||||
@@ -10,6 +11,12 @@ class ShadowsocksSettings extends PreferenceFragment {
|
||||
override def onCreate(savedInstanceState: Bundle) {
|
||||
super.onCreate(savedInstanceState)
|
||||
addPreferencesFromResource(R.xml.pref_all)
|
||||
findPreference(Key.isNAT).setOnPreferenceChangeListener((preference: Preference, newValue: Any) =>
|
||||
if (ShadowsocksApplication.isRoot) activity.handler.post(() => {
|
||||
activity.deattachService()
|
||||
activity.attachService()
|
||||
true
|
||||
}) else false)
|
||||
findPreference("recovery").setOnPreferenceClickListener((preference: Preference) => {
|
||||
ShadowsocksApplication.track(Shadowsocks.TAG, "reset")
|
||||
activity.recovery()
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.github.shadowsocks.preferences
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.preference.SwitchPreference
|
||||
import android.util.AttributeSet
|
||||
import com.github.shadowsocks.{R, ShadowsocksApplication}
|
||||
|
||||
/**
|
||||
* TODO: No animations?
|
||||
* @author Mygod
|
||||
*/
|
||||
final class NatSwitchPreference(context: Context, attrs: AttributeSet) extends SwitchPreference(context, attrs) {
|
||||
private var disabled = false
|
||||
|
||||
override def isEnabled = super.isEnabled && !disabled
|
||||
|
||||
override def onGetDefaultValue(a: TypedArray, index: Int) = {
|
||||
val result = ShadowsocksApplication.isVpnEnabled
|
||||
if (!ShadowsocksApplication.isRoot) {
|
||||
disabled = true
|
||||
setEnabled(false)
|
||||
setSummary(R.string.nat_summary_no_root)
|
||||
}
|
||||
result.asInstanceOf[AnyRef]
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,6 @@ object Key {
|
||||
val proxied = "Proxyed"
|
||||
|
||||
val isNAT = "isNAT"
|
||||
val status = "status"
|
||||
val proxyedApps = "proxyedApps"
|
||||
val route = "route"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user