diff --git a/src/main/res/layout/layout_main.xml b/src/main/res/layout/layout_main.xml
index 14858f5c..c42b69e8 100644
--- a/src/main/res/layout/layout_main.xml
+++ b/src/main/res/layout/layout_main.xml
@@ -3,14 +3,70 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
tools:ignore="UnknownIdInLayout" android:clipChildren="false">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml
index b794e265..e70d0301 100644
--- a/src/main/res/values-zh-rCN/strings.xml
+++ b/src/main/res/values-zh-rCN/strings.xml
@@ -1,7 +1,6 @@
影梭
- 全局设置
其他
服务器设置
配置名称
diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml
index e5e022d9..a5c98be5 100644
--- a/src/main/res/values-zh-rTW/strings.xml
+++ b/src/main/res/values-zh-rTW/strings.xml
@@ -2,7 +2,6 @@
Shadowsocks
- 全域設定
其他
伺服器設定
設定檔名稱
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 0dbca08b..d5c7ccc9 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -2,10 +2,9 @@
Shadowsocks
- shadowsocks
+ shadowsocks ▾
- Global Settings
Misc
Profile
Switch to another profile or add new profiles
diff --git a/src/main/res/xml/pref_all.xml b/src/main/res/xml/pref_all.xml
index 2064a822..8cb10dde 100644
--- a/src/main/res/xml/pref_all.xml
+++ b/src/main/res/xml/pref_all.xml
@@ -2,19 +2,6 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/scala/com/github/shadowsocks/AppManager.scala b/src/main/scala/com/github/shadowsocks/AppManager.scala
index 318dab8d..41fec006 100644
--- a/src/main/scala/com/github/shadowsocks/AppManager.scala
+++ b/src/main/scala/com/github/shadowsocks/AppManager.scala
@@ -172,7 +172,8 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
reloadApps()
return true
} catch {
- case _: java.lang.IllegalArgumentException => // ignore
+ case _: IllegalArgumentException =>
+ Toast.makeText(this, R.string.action_import_err, Toast.LENGTH_SHORT).show
}
}
}
diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala
index 0f985a50..a20f771e 100644
--- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala
+++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala
@@ -39,6 +39,8 @@
package com.github.shadowsocks
import java.io.{FileOutputStream, IOException, InputStream, OutputStream}
+import java.lang.System.currentTimeMillis
+import java.net.{HttpURLConnection, URL}
import java.util
import java.util.Locale
@@ -59,6 +61,7 @@ import android.widget._
import com.github.jorgecastilloprz.FABProgressCircle
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import com.github.shadowsocks.database._
+import com.github.shadowsocks.utils.CloseUtils._
import com.github.shadowsocks.utils._
import com.google.android.gms.ads.{AdRequest, AdSize, AdView}
@@ -105,7 +108,6 @@ class Shadowsocks
var progressTag = -1
var state = State.STOPPED
var currentProfile = new Profile
- var vpnEnabled = -1
// Services
var currentServiceName = classOf[ShadowsocksNatService].getName
@@ -119,6 +121,7 @@ class Shadowsocks
fab.setEnabled(false)
fabProgressCircle.show()
preferences.setEnabled(false)
+ stat.setVisibility(View.GONE)
case State.CONNECTED =>
fab.setBackgroundTintList(greenTint)
if (state == State.CONNECTING) {
@@ -129,6 +132,11 @@ class Shadowsocks
fab.setEnabled(true)
changeSwitch(checked = true)
preferences.setEnabled(false)
+ stat.setVisibility(View.VISIBLE)
+ if (ShadowsocksApplication.isVpnEnabled) {
+ connectionTest.setVisibility(View.VISIBLE)
+ connectionTest.setText(getString(R.string.connection_test_pending))
+ } else connectionTest.setVisibility(View.GONE)
case State.STOPPED =>
fab.setBackgroundTintList(greyTint)
fabProgressCircle.postDelayed(hideCircle, 1000)
@@ -143,12 +151,14 @@ class Shadowsocks
Log.e(Shadowsocks.TAG, "Error to start VPN service: " + m)
}
preferences.setEnabled(true)
+ stat.setVisibility(View.GONE)
case State.STOPPING =>
fab.setBackgroundTintList(greyTint)
fab.setImageResource(R.drawable.ic_start_busy)
fab.setEnabled(false)
if (state == State.CONNECTED) fabProgressCircle.show() // ignore for stopped
preferences.setEnabled(false)
+ stat.setVisibility(View.GONE)
}
state = s
})
@@ -158,9 +168,12 @@ class Shadowsocks
}
}
- def updateTraffic(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) = preferences.stat
- .setRate(TrafficMonitor.formatTraffic(txTotal), TrafficMonitor.formatTraffic(rxTotal),
- TrafficMonitor.formatTraffic(txRate) + "/s", TrafficMonitor.formatTraffic(rxRate) + "/s")
+ def updateTraffic(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) {
+ txText.setText(TrafficMonitor.formatTraffic(txTotal))
+ rxText.setText(TrafficMonitor.formatTraffic(rxTotal))
+ txRateText.setText(TrafficMonitor.formatTraffic(txRate))
+ rxRateText.setText(TrafficMonitor.formatTraffic(rxRate))
+ }
def attachService: Unit = attachService(callback)
@@ -202,11 +215,18 @@ class Shadowsocks
if (fab != null) fab.setEnabled(false)
}
- private lazy val preferences =
- getFragmentManager.findFragmentById(android.R.id.content).asInstanceOf[ShadowsocksSettings]
- private var adView: AdView = _
+ private var testCount: Int = _
+ private lazy val stat = findViewById(R.id.stat)
+ private lazy val connectionTest = findViewById(R.id.connection_test).asInstanceOf[TextView]
+ private var txText: TextView = _
+ private var rxText: TextView = _
+ private var txRateText: TextView = _
+ private var rxRateText: TextView = _
private lazy val greyTint = ContextCompat.getColorStateList(this, R.color.material_blue_grey_700)
private lazy val greenTint = ContextCompat.getColorStateList(this, R.color.material_green_700)
+ private var adView: AdView = _
+ private lazy val preferences =
+ getFragmentManager.findFragmentById(android.R.id.content).asInstanceOf[ShadowsocksSettings]
var handler = new Handler()
@@ -350,13 +370,65 @@ class Shadowsocks
toolbar.setTitleTextAppearance(toolbar.getContext, R.style.Toolbar_Logo)
val field = classOf[Toolbar].getDeclaredField("mTitleTextView")
field.setAccessible(true)
- val title: TextView = field.get(toolbar).asInstanceOf[TextView]
- val tf: Typeface = Typefaces.get(this, "fonts/Iceland.ttf")
+ val title = field.get(toolbar).asInstanceOf[TextView]
+ title.setOnClickListener(_ => startActivity(new Intent(this, classOf[ProfileManagerActivity])))
+ val typedArray = obtainStyledAttributes(Array(R.attr.selectableItemBackgroundBorderless))
+ title.setBackgroundResource(typedArray.getResourceId(0, 0))
+ typedArray.recycle
+ val tf = Typefaces.get(this, "fonts/Iceland.ttf")
if (tf != null) title.setTypeface(tf)
+ val connectionTestText = findViewById(R.id.connection_test).asInstanceOf[TextView]
+ txText = findViewById(R.id.tx).asInstanceOf[TextView]
+ txRateText = findViewById(R.id.txRate).asInstanceOf[TextView]
+ rxText = findViewById(R.id.rx).asInstanceOf[TextView]
+ rxRateText = findViewById(R.id.rxRate).asInstanceOf[TextView]
+ connectionTestText.setOnClickListener(_ => {
+ val id = synchronized {
+ testCount += 1
+ handler.post(() => connectionTestText.setText(R.string.connection_test_testing))
+ testCount
+ }
+ ThrowableFuture {
+ // Based on: https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/connectivity/NetworkMonitor.java#640
+ autoDisconnect(new URL("https", "www.google.com", "/generate_204").openConnection()
+ .asInstanceOf[HttpURLConnection]) { conn =>
+ conn.setConnectTimeout(5 * 1000)
+ conn.setReadTimeout(5 * 1000)
+ conn.setInstanceFollowRedirects(false)
+ conn.setUseCaches(false)
+ if (testCount == id) {
+ var result: String = null
+ var success = true
+ try {
+ val start = currentTimeMillis
+ conn.getInputStream
+ val elapsed = currentTimeMillis - start
+ val code = conn.getResponseCode
+ if (code == 204 || code == 200 && conn.getContentLength == 0)
+ result = getString(R.string.connection_test_available, elapsed: java.lang.Long)
+ else throw new Exception(getString(R.string.connection_test_error_status_code, code: Integer))
+ } catch {
+ case e: Exception =>
+ success = false
+ result = getString(R.string.connection_test_error, e.getMessage)
+ }
+ synchronized(if (testCount == id) {
+ if (ShadowsocksApplication.isVpnEnabled) handler.post(() => {
+ if (success) connectionTestText.setText(result) else {
+ connectionTestText.setText(R.string.connection_test_fail)
+ Snackbar.make(findViewById(android.R.id.content), result, Snackbar.LENGTH_LONG).show
+ }
+ })
+ })
+ }
+ }
+ }
+ })
+
fab = findViewById(R.id.fab).asInstanceOf[FloatingActionButton]
fabProgressCircle = findViewById(R.id.fabProgressCircle).asInstanceOf[FABProgressCircle]
- fab.setOnClickListener((v: View) => if (serviceStarted) serviceStop()
+ fab.setOnClickListener(_ => if (serviceStarted) serviceStop()
else if (checkText(Key.proxy) && checkText(Key.sitekey) && bgService != null) prepareStartService()
else changeSwitch(checked = false))
fab.setOnLongClickListener((v: View) => {
@@ -391,24 +463,32 @@ class Shadowsocks
fab.setImageResource(R.drawable.ic_start_busy)
preferences.setEnabled(false)
fabProgressCircle.show()
+ stat.setVisibility(View.GONE)
case State.CONNECTED =>
fab.setBackgroundTintList(greenTint)
serviceStarted = true
fab.setImageResource(R.drawable.ic_start_connected)
preferences.setEnabled(false)
fabProgressCircle.postDelayed(hideCircle, 100)
+ stat.setVisibility(View.VISIBLE)
+ if (ShadowsocksApplication.isVpnEnabled) {
+ connectionTest.setVisibility(View.VISIBLE)
+ connectionTest.setText(getString(R.string.connection_test_pending))
+ } else connectionTest.setVisibility(View.GONE)
case State.STOPPING =>
fab.setBackgroundTintList(greyTint)
serviceStarted = false
fab.setImageResource(R.drawable.ic_start_busy)
preferences.setEnabled(false)
fabProgressCircle.show()
+ stat.setVisibility(View.GONE)
case _ =>
fab.setBackgroundTintList(greyTint)
serviceStarted = false
fab.setImageResource(R.drawable.ic_start_idle)
preferences.setEnabled(true)
fabProgressCircle.postDelayed(hideCircle, 100)
+ stat.setVisibility(View.GONE)
}
state = bgService.getState
}
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala
index 5e9218fc..c4d56bf9 100644
--- a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala
@@ -1,7 +1,5 @@
package com.github.shadowsocks
-import java.lang.System.currentTimeMillis
-import java.net.{HttpURLConnection, URL}
import java.util.Locale
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
@@ -9,12 +7,10 @@ import android.content.{DialogInterface, Intent, SharedPreferences}
import android.net.Uri
import android.os.{Build, Bundle}
import android.preference.{Preference, PreferenceFragment, SwitchPreference}
-import android.support.design.widget.Snackbar
import android.support.v7.app.AlertDialog
import android.webkit.{WebView, WebViewClient}
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.preferences._
-import com.github.shadowsocks.utils.CloseUtils._
import com.github.shadowsocks.utils.{Key, Utils}
object ShadowsocksSettings {
@@ -69,62 +65,14 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
private def activity = getActivity.asInstanceOf[Shadowsocks]
lazy val natSwitch = findPreference(Key.isNAT).asInstanceOf[SwitchPreference]
- var stat: StatusPreference = _
private var isProxyApps: SwitchPreference = _
- private var testCount: Int = _
override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.pref_all)
getPreferenceManager.getSharedPreferences.registerOnSharedPreferenceChangeListener(this)
- stat = findPreference(Key.stat).asInstanceOf[StatusPreference]
- stat.setOnPreferenceClickListener(_ => {
- val id = synchronized {
- testCount += 1
- activity.handler.post(() => stat.setSummary(R.string.connection_test_testing))
- testCount
- }
- ThrowableFuture {
- // Based on: https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/connectivity/NetworkMonitor.java#640
- autoDisconnect(new URL("https", "www.google.com", "/generate_204").openConnection()
- .asInstanceOf[HttpURLConnection]) { conn =>
- conn.setConnectTimeout(5 * 1000)
- conn.setReadTimeout(5 * 1000)
- conn.setInstanceFollowRedirects(false)
- conn.setUseCaches(false)
- if (testCount == id) {
- var result: String = null
- var success = true
- try {
- val start = currentTimeMillis
- conn.getInputStream
- val elapsed = currentTimeMillis - start
- val code = conn.getResponseCode
- if (code == 204 || code == 200 && conn.getContentLength == 0)
- result = getString(R.string.connection_test_available, elapsed: java.lang.Long)
- else throw new Exception(getString(R.string.connection_test_error_status_code, code: Integer))
- } catch {
- case e: Exception =>
- success = false
- result = getString(R.string.connection_test_error, e.getMessage)
- }
- synchronized(if (testCount == id) {
- if (ShadowsocksApplication.isVpnEnabled) activity.handler.post(() => {
- if (success) stat.setSummary(result) else {
- stat.setSummary(R.string.connection_test_fail)
- Snackbar.make(activity.findViewById(android.R.id.content), result, Snackbar.LENGTH_LONG).show
- }
- })
- })
- }
- }
- }
- true
- })
- stat.setSummary(if (ShadowsocksApplication.isVpnEnabled) getString(R.string.connection_test_pending) else null)
-
isProxyApps = findPreference(Key.isProxyApps).asInstanceOf[SwitchPreference]
isProxyApps.setOnPreferenceClickListener((preference: Preference) => {
startActivity(new Intent(activity, classOf[AppManager]))
@@ -189,7 +137,6 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
activity.deattachService
activity.attachService
})
- stat.setSummary(if (ShadowsocksApplication.isVpnEnabled) getString(R.string.connection_test_pending) else null)
case _ =>
}
diff --git a/src/main/scala/com/github/shadowsocks/preferences/StatusPreference.scala b/src/main/scala/com/github/shadowsocks/preferences/StatusPreference.scala
deleted file mode 100644
index 0db2bd09..00000000
--- a/src/main/scala/com/github/shadowsocks/preferences/StatusPreference.scala
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.github.shadowsocks.preferences
-
-import android.content.Context
-import android.preference.Preference
-import android.util.AttributeSet
-import android.view.{LayoutInflater, View, ViewGroup}
-import android.widget.TextView
-import com.github.shadowsocks.R
-
-/**
- * @author madeye
- */
-final class StatusPreference(context: Context, attrs: AttributeSet = null)
- extends Preference(context, attrs) {
- var tx: String = _
- var rx: String = _
- var txRate: String = _
- var rxRate: String = _
- var txView: TextView = _
- var rxView: TextView = _
- var txRateView: TextView = _
- var rxRateView: TextView = _
-
- override def onCreateView(parent: ViewGroup): View = {
- val view = getContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE).asInstanceOf[LayoutInflater]
- .inflate(R.layout.status_pref, parent, false)
-
- txView = view.findViewById(R.id.tx).asInstanceOf[TextView]
- rxView = view.findViewById(R.id.rx).asInstanceOf[TextView]
- txRateView = view.findViewById(R.id.txRate).asInstanceOf[TextView]
- rxRateView = view.findViewById(R.id.rxRate).asInstanceOf[TextView]
-
- view
- }
-
- override def onBindView(view: View) = {
- super.onBindView(view)
- txView.setText(tx)
- rxView.setText(rx)
- txRateView.setText(txRate)
- rxRateView.setText(rxRate)
- }
-
- def setRate(tx: String, rx: String, txRate: String, rxRate: String) {
- this.tx = tx
- if (txView != null) {
- txView.setText(tx)
- }
- this.rx = rx
- if (rxView != null) {
- rxView.setText(rx)
- }
- this.txRate = txRate
- if (txRateView != null) {
- txRateView.setText(txRate)
- }
- this.rxRate = rxRate
- if (rxRateView != null) {
- rxRateView.setText(rxRate)
- }
- }
-}
diff --git a/src/main/scala/com/github/shadowsocks/utils/Constants.scala b/src/main/scala/com/github/shadowsocks/utils/Constants.scala
index 5aeb7959..c6a0d811 100644
--- a/src/main/scala/com/github/shadowsocks/utils/Constants.scala
+++ b/src/main/scala/com/github/shadowsocks/utils/Constants.scala
@@ -61,7 +61,6 @@ object Key {
val proxied = "Proxyed"
- val profiles = "profiles"
val isNAT = "isNAT"
val route = "route"
val stat = "stat"