diff --git a/build.sbt b/build.sbt index a1aece97..fcd92b70 100644 --- a/build.sbt +++ b/build.sbt @@ -37,6 +37,7 @@ libraryDependencies ++= Seq( "com.google.android.gms" % "play-services-ads" % "8.4.0", "com.google.android.gms" % "play-services-analytics" % "8.4.0", "com.android.support" % "design" % "23.1.1", + "com.android.support" % "gridlayout-v7" % "23.1.1", "com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01", "com.j256.ormlite" % "ormlite-core" % "4.48", "com.j256.ormlite" % "ormlite-android" % "4.48", diff --git a/src/main/res/drawable-anydpi-v21/ic_cloud.xml b/src/main/res/drawable-anydpi-v21/ic_cloud.xml deleted file mode 100644 index c5d6a7d4..00000000 --- a/src/main/res/drawable-anydpi-v21/ic_cloud.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/src/main/res/drawable-anydpi-v21/ic_cloud_off.xml b/src/main/res/drawable-anydpi-v21/ic_cloud_off.xml deleted file mode 100644 index 575f4ef9..00000000 --- a/src/main/res/drawable-anydpi-v21/ic_cloud_off.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/src/main/res/drawable-anydpi-v21/ic_cloud_queue.xml b/src/main/res/drawable-anydpi-v21/ic_cloud_queue.xml deleted file mode 100644 index f30db90d..00000000 --- a/src/main/res/drawable-anydpi-v21/ic_cloud_queue.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/src/main/res/layout/status_pref.xml b/src/main/res/layout/status_pref.xml index 80e7fa5c..f5c50fd6 100644 --- a/src/main/res/layout/status_pref.xml +++ b/src/main/res/layout/status_pref.xml @@ -4,33 +4,23 @@ android:layout_height="wrap_content" android:minHeight="?android:attr/listPreferredItemHeight" android:gravity="center_vertical" - android:paddingEnd="?android:attr/scrollbarSize" + android:paddingLeft="15dip" + android:paddingRight="15dip" + android:paddingTop="6dip" + android:paddingBottom="6dip" android:background="?android:attr/selectableItemBackground" android:orientation="vertical" > - - - - - + - + - + + + - + + android:text="▲" /> - + app:layout_columnWeight="1" + app:layout_gravity="fill_horizontal" + android:gravity="end" /> - + + android:text="▼" /> - + app:layout_columnWeight="1" + app:layout_gravity="fill_horizontal" + android:gravity="end" /> - + diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml index 0fbd0408..46db437b 100755 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -116,6 +116,5 @@ 发送: 接收: - 检查网络连接 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index b9d67918..3f5c51d4 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -124,5 +124,4 @@ Sent: Received: - Check Connectivity diff --git a/src/main/res/xml/pref_all.xml b/src/main/res/xml/pref_all.xml index 23447138..2064a822 100644 --- a/src/main/res/xml/pref_all.xml +++ b/src/main/res/xml/pref_all.xml @@ -10,7 +10,7 @@ diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index a9fa9092..6eb2ff04 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -58,7 +58,6 @@ import android.view.{View, ViewGroup, ViewParent} import android.widget._ import com.github.jorgecastilloprz.FABProgressCircle import com.github.shadowsocks.aidl.IShadowsocksServiceCallback -import com.github.shadowsocks.preferences._ import com.github.shadowsocks.database._ import com.github.shadowsocks.utils._ import com.google.android.gms.ads.{AdRequest, AdSize, AdView} @@ -107,9 +106,6 @@ class Shadowsocks var state = State.STOPPED var currentProfile = new Profile var vpnEnabled = -1 - var trafficCache: Array[String] = _ - var connectionTestResult: String = _ - var connectionTestSuccess: Boolean = true // Services var currentServiceName = classOf[ShadowsocksNatService].getName @@ -158,28 +154,13 @@ class Shadowsocks }) } def trafficUpdated(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { - trafficCache = Array( - "▲ " + TrafficMonitor.formatTraffic(txRate) + "/s", - "▼ " + TrafficMonitor.formatTraffic(rxRate) + "/s", - TrafficMonitor.formatTraffic(txTotal), TrafficMonitor.formatTraffic(rxTotal)) - handler.post(updateTraffic) + handler.post(() => updateTraffic(txRate, rxRate, txTotal, rxTotal)) } } - def updateTraffic(): Unit = if (trafficCache == null) callback.trafficUpdated(0, 0, 0, 0) else { - if (connectionTestResult == null) connectionTestResult = getString(R.string.connection_test_pending) - if (preferences.natSwitch.isChecked) { - preferences.stat.setSummary("") - } else { - if (connectionTestSuccess) { - preferences.stat.setSummary(connectionTestResult) - } else { - preferences.stat.setSummary(getString(R.string.connection_test_fail)) - Snackbar.make(findViewById(android.R.id.content), connectionTestResult, Snackbar.LENGTH_LONG).show - } - } - preferences.stat.asInstanceOf[StatusPreference].setRate(trafficCache(2), trafficCache(3), trafficCache(0), trafficCache(1)) - } + 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 attachService: Unit = attachService(callback) @@ -263,7 +244,7 @@ class Shadowsocks } else { in = assetManager.open(file) } - out = new FileOutputStream(getApplicationInfo().dataDir + "/" + file) + out = new FileOutputStream(getApplicationInfo.dataDir + "/" + file) copyFile(in, out) in.close() in = null @@ -293,8 +274,8 @@ class Shadowsocks val cmd = new ArrayBuffer[String]() for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks")) { - cmd.append("chmod 666 %s/%s-nat.pid".formatLocal(Locale.ENGLISH, getApplicationInfo().dataDir, task)) - cmd.append("chmod 666 %s/%s-vpn.pid".formatLocal(Locale.ENGLISH, getApplicationInfo().dataDir, task)) + cmd.append("chmod 666 %s/%s-nat.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task)) + cmd.append("chmod 666 %s/%s-vpn.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task)) } if (ShadowsocksApplication.isVpnEnabled) { @@ -307,8 +288,8 @@ class Shadowsocks for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks")) { try { - val pid_nat = scala.io.Source.fromFile(getApplicationInfo().dataDir + "/" + task + "-nat.pid").mkString.trim.toInt - val pid_vpn = scala.io.Source.fromFile(getApplicationInfo().dataDir + "/" + task + "-vpn.pid").mkString.trim.toInt + val pid_nat = scala.io.Source.fromFile(getApplicationInfo.dataDir + "/" + task + "-nat.pid").mkString.trim.toInt + val pid_vpn = scala.io.Source.fromFile(getApplicationInfo.dataDir + "/" + task + "-vpn.pid").mkString.trim.toInt cmd.append("kill -9 %d".formatLocal(Locale.ENGLISH, pid_nat)) cmd.append("kill -9 %d".formatLocal(Locale.ENGLISH, pid_vpn)) Process.killProcess(pid_nat) @@ -316,10 +297,10 @@ class Shadowsocks } catch { case e: Throwable => // Ignore } - cmd.append("rm -f %s/%s-nat.pid".formatLocal(Locale.ENGLISH, getApplicationInfo().dataDir, task)) - cmd.append("rm -f %s/%s-nat.conf".formatLocal(Locale.ENGLISH, getApplicationInfo().dataDir, task)) - cmd.append("rm -f %s/%s-vpn.pid".formatLocal(Locale.ENGLISH, getApplicationInfo().dataDir, task)) - cmd.append("rm -f %s/%s-vpn.conf".formatLocal(Locale.ENGLISH, getApplicationInfo().dataDir, task)) + cmd.append("rm -f %s/%s-nat.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task)) + cmd.append("rm -f %s/%s-nat.conf".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task)) + cmd.append("rm -f %s/%s-vpn.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task)) + cmd.append("rm -f %s/%s-vpn.conf".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task)) } if (ShadowsocksApplication.isVpnEnabled) Console.runCommand(cmd.toArray) else { Console.runRootCommand(cmd.toArray) @@ -389,6 +370,7 @@ class Shadowsocks Toast.LENGTH_SHORT), fab, getWindow, 0, Utils.dpToPx(this, 8)).show true }) + updateTraffic(0, 0, 0, 0) // Bind to the service handler.post(() => attachService) @@ -461,7 +443,6 @@ class Shadowsocks // Check if current profile changed if (ShadowsocksApplication.profileId != currentProfile.id) reloadProfile() - updateTraffic() updateState() } @@ -503,7 +484,7 @@ class Shadowsocks val ab = new ArrayBuffer[String] for (executable <- Shadowsocks.EXECUTABLES) { - ab.append("chmod 755 " + getApplicationInfo().dataDir + "/" + executable) + ab.append("chmod 755 " + getApplicationInfo.dataDir + "/" + executable) } Console.runCommand(ab.toArray) } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala index bd2902e0..ecdec22d 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala @@ -9,10 +9,11 @@ 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.{DropDownPreference, NumberPickerPreference, PasswordEditTextPreference, SummaryEditTextPreference} +import com.github.shadowsocks.preferences._ import com.github.shadowsocks.utils.CloseUtils._ import com.github.shadowsocks.utils.{Key, Utils} @@ -68,7 +69,7 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan private def activity = getActivity.asInstanceOf[Shadowsocks] lazy val natSwitch = findPreference(Key.isNAT).asInstanceOf[SwitchPreference] - var stat: Preference = _ + var stat: StatusPreference = _ private var isProxyApps: SwitchPreference = _ private var testCount: Int = _ @@ -78,13 +79,11 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan addPreferencesFromResource(R.xml.pref_all) getPreferenceManager.getSharedPreferences.registerOnSharedPreferenceChangeListener(this) - stat= findPreference(Key.stat) + stat = findPreference(Key.stat).asInstanceOf[StatusPreference] stat.setOnPreferenceClickListener(_ => { val id = synchronized { testCount += 1 - activity.connectionTestResult = getString(R.string.connection_test_testing) - activity.connectionTestSuccess = true - activity.updateTraffic() + activity.handler.post(() => stat.setSummary(R.string.connection_test_testing)) testCount } ThrowableFuture { @@ -97,7 +96,7 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan conn.setUseCaches(false) if (testCount == id) { var result: String = null - var success: Boolean = true + var success = true try { val start = currentTimeMillis conn.getInputStream @@ -107,21 +106,24 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan 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 => { + case e: Exception => success = false result = getString(R.string.connection_test_error, e.getMessage) - } } synchronized(if (testCount == id) { - activity.connectionTestSuccess = success - activity.connectionTestResult = result - activity.handler.post(activity.updateTraffic) + 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) => { @@ -182,10 +184,12 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan } def onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) = key match { - case Key.isNAT => activity.handler.post(() => { - activity.deattachService - activity.attachService - }) + case Key.isNAT => + activity.handler.post(() => { + 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 index 2b198e5d..0db2bd09 100644 --- a/src/main/scala/com/github/shadowsocks/preferences/StatusPreference.scala +++ b/src/main/scala/com/github/shadowsocks/preferences/StatusPreference.scala @@ -2,8 +2,8 @@ package com.github.shadowsocks.preferences import android.content.Context import android.preference.Preference -import android.view.{View, ViewGroup, LayoutInflater} import android.util.AttributeSet +import android.view.{LayoutInflater, View, ViewGroup} import android.widget.TextView import com.github.shadowsocks.R @@ -12,14 +12,18 @@ import com.github.shadowsocks.R */ final class StatusPreference(context: Context, attrs: AttributeSet = null) extends Preference(context, attrs) { - var txView: TextView = null - var rxView: TextView = null - var txRateView: TextView = null - var rxRateView: TextView = null + 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 li = getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE).asInstanceOf[LayoutInflater] - val view = li.inflate(R.layout.status_pref, parent, false) + 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] @@ -29,16 +33,28 @@ final class StatusPreference(context: Context, attrs: AttributeSet = null) 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) }