diff --git a/project/build.properties b/project/build.properties
deleted file mode 100644
index a6e117b6..00000000
--- a/project/build.properties
+++ /dev/null
@@ -1 +0,0 @@
-sbt.version=0.13.8
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 217b0486..5388c040 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,5 +1,5 @@
resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-snapshots"))(Resolver.ivyStylePatterns)
-addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.5.9")
+addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.5.10")
resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.7.0-SNAPSHOT")
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 1a347e07..4a41d8a0 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -109,6 +109,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml
index dcf31c04..c3910a87 100755
--- a/src/main/res/values-zh/strings.xml
+++ b/src/main/res/values-zh/strings.xml
@@ -33,7 +33,6 @@
自动连接
随系统启动后台服务
后台服务已开始运行。
- %s
无法连接远程服务器
停止服务
正在关闭……
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 018d7cc2..7c154a9f 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -60,7 +60,6 @@
Shadowsocks started.
- %s
Failed to connect the remote server
Stop the service
Shutting down…
diff --git a/src/main/scala/com/github/shadowsocks/AppManager.scala b/src/main/scala/com/github/shadowsocks/AppManager.scala
index 3d5da43d..d08e8acb 100644
--- a/src/main/scala/com/github/shadowsocks/AppManager.scala
+++ b/src/main/scala/com/github/shadowsocks/AppManager.scala
@@ -40,7 +40,7 @@
package com.github.shadowsocks
import android.content.pm.PackageManager
-import android.content.{ClipData, ClipboardManager, Context, SharedPreferences}
+import android.content.{ClipData, ClipboardManager, Context}
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.os.{Bundle, Handler}
@@ -50,101 +50,49 @@ import android.support.v7.widget.Toolbar
import android.support.v7.widget.Toolbar.OnMenuItemClickListener
import android.view.View.OnClickListener
import android.view.ViewGroup.LayoutParams
-import android.view.{MenuItem, View, ViewGroup, WindowManager}
+import android.view._
import android.widget.AbsListView.OnScrollListener
import android.widget.CompoundButton.OnCheckedChangeListener
import android.widget._
import com.github.shadowsocks.utils.{Key, Utils}
+import scala.collection.JavaConversions._
+import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.language.implicitConversions
-case class ProxiedApp(uid: Int, name: String, packageName: String, icon: Drawable, var proxied: Boolean)
-
-class ObjectArrayTools[T <: AnyRef](a: Array[T]) {
- def binarySearch(key: T) = {
- java.util.Arrays.binarySearch(a.asInstanceOf[Array[AnyRef]], key)
- }
-}
-
-case class ListEntry(switch: Switch, text: TextView, icon: ImageView)
-
object AppManager {
+ case class ProxiedApp(uid: Int, name: String, packageName: String, icon: Drawable)
+ private case class ListEntry(switch: Switch, text: TextView, icon: ImageView)
- implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]): ObjectArrayTools[T] = new ObjectArrayTools(a)
-
- def getProxiedApps(context: Context, proxiedAppString: String): Array[ProxiedApp] = {
-
- val proxiedApps = proxiedAppString.split('|').sortWith(_ < _)
-
- import scala.collection.JavaConversions._
-
- val packageManager: PackageManager = context.getPackageManager
- val appList = packageManager.getInstalledApplications(0)
-
- appList.filter(_.uid >= 10000).map {
- case a =>
- val uid = a.uid
- val userName = uid.toString
- val name = packageManager.getApplicationLabel(a).toString
- val packageName = a.packageName
- val proxied = proxiedApps.binarySearch(userName) >= 0
- new ProxiedApp(uid, name, packageName, null, proxied)
- }.toArray
+ var cachedApps: Array[ProxiedApp] = _
+ private def getApps(pm: PackageManager) = {
+ if (cachedApps == null) cachedApps = pm.getInstalledApplications(0).filter(_.uid >= 10000)
+ .map(a => new ProxiedApp(a.uid, pm.getApplicationLabel(a).toString, a.packageName, a.loadIcon(pm))).toArray
+ cachedApps
}
}
class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnClickListener
with OnMenuItemClickListener {
+ import AppManager._
- val MSG_LOAD_START = 1
- val MSG_LOAD_FINISH = 2
- val STUB = android.R.drawable.sym_def_app_icon
-
- implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]): ObjectArrayTools[T] = new ObjectArrayTools(a)
-
- var apps: Array[ProxiedApp] = _
- var appListView: ListView = _
- var loadingView: View = _
- var overlay: TextView = _
- var adapter: ListAdapter = _
- @volatile var appsLoading: Boolean = _
-
- def loadApps(context: Context): Array[ProxiedApp] = {
- val proxiedAppString = ShadowsocksApplication.settings.getString(Key.proxied, "")
- val proxiedApps = proxiedAppString.split('|').sortWith(_ < _)
-
- import scala.collection.JavaConversions._
-
- val packageManager: PackageManager = context.getPackageManager
- val appList = packageManager.getInstalledApplications(0)
-
- appList.filter(a => a.uid >= 10000
- && packageManager.getApplicationLabel(a) != null
- && packageManager.getApplicationIcon(a) != null).map {
- a =>
- val uid = a.uid
- val userName = uid.toString
- val name = packageManager.getApplicationLabel(a).toString
- val packageName = a.packageName
- val proxied = (proxiedApps binarySearch userName) >= 0
- new ProxiedApp(uid, name, packageName, a.loadIcon(packageManager), proxied)
- }.toArray
- }
+ private var apps: Array[ProxiedApp] = _
+ private var proxiedApps: mutable.HashSet[Int] = _
+ private var toolbar: Toolbar = _
+ private var appListView: ListView = _
+ private var loadingView: View = _
+ private var overlay: TextView = _
+ private var adapter: ListAdapter = _
+ @volatile private var appsLoading: Boolean = _
def loadApps() {
appsLoading = true
- apps = loadApps(this).sortWith((a, b) => {
- if (a == null || b == null || a.name == null || b.name == null) {
- true
- } else if (a.proxied == b.proxied) {
- a.name < b.name
- } else if (a.proxied) {
- true
- } else {
- false
- }
+ proxiedApps = ShadowsocksApplication.settings.getString(Key.proxied, "").split('|').map(_.toInt).to[mutable.HashSet]
+ apps = getApps(getPackageManager).sortWith((a, b) => {
+ val aProxied = proxiedApps.contains(a.uid)
+ if (aProxied ^ proxiedApps.contains(b.uid)) aProxied else a.name < b.name
})
adapter = new ArrayAdapter[ProxiedApp](this, R.layout.layout_apps_item, R.id.itemtext, apps) {
override def getView(position: Int, view: View, parent: ViewGroup): View = {
@@ -152,10 +100,9 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
var entry: ListEntry = null
if (convertView == null) {
convertView = getLayoutInflater.inflate(R.layout.layout_apps_item, parent, false)
- val icon = convertView.findViewById(R.id.itemicon).asInstanceOf[ImageView]
- val switch = convertView.findViewById(R.id.itemcheck).asInstanceOf[Switch]
- val text = convertView.findViewById(R.id.itemtext).asInstanceOf[TextView]
- entry = new ListEntry(switch, text, icon)
+ entry = new ListEntry(convertView.findViewById(R.id.itemcheck).asInstanceOf[Switch],
+ convertView.findViewById(R.id.itemtext).asInstanceOf[TextView],
+ convertView.findViewById(R.id.itemicon).asInstanceOf[ImageView])
convertView.setOnClickListener(AppManager.this)
convertView.setTag(entry)
entry.switch.setOnCheckedChangeListener(AppManager.this)
@@ -169,19 +116,19 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
entry.icon.setImageDrawable(app.icon)
val switch = entry.switch
switch.setTag(app)
- switch.setChecked(app.proxied)
+ switch.setChecked(proxiedApps.contains(app.uid))
entry.text.setTag(switch)
convertView
}
}
}
+ private def setProxied(uid: Int, proxied: Boolean) = if (proxied) proxiedApps.add(uid) else proxiedApps.remove(uid)
+
/** Called an application is check/unchecked */
def onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
val app: ProxiedApp = buttonView.getTag.asInstanceOf[ProxiedApp]
- if (app != null) {
- app.proxied = isChecked
- }
+ if (app != null) setProxied(app.uid, isChecked)
saveAppSettings(this)
}
@@ -189,8 +136,9 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
val switch = v.getTag.asInstanceOf[ListEntry].switch
val app: ProxiedApp = switch.getTag.asInstanceOf[ProxiedApp]
if (app != null) {
- app.proxied = !app.proxied
- switch.setChecked(app.proxied)
+ val proxied = !proxiedApps.contains(app.uid)
+ setProxied(app.uid, proxied)
+ switch.setChecked(proxied)
}
saveAppSettings(this)
}
@@ -250,7 +198,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
handler = new Handler()
this.setContentView(R.layout.layout_apps)
- val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
+ toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
toolbar.setTitle(R.string.proxied_apps)
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha)
toolbar.setNavigationOnClickListener((v: View) => {
@@ -285,12 +233,8 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
def onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int,
totalItemCount: Int) {
if (visible) {
- val name: String = apps(firstVisibleItem).name
- if (name != null && name.length > 1) {
- overlay.setText(apps(firstVisibleItem).name.substring(0, 1))
- } else {
- overlay.setText("*")
- }
+ val name = apps(firstVisibleItem).name
+ overlay.setText(if (name != null && name.length > 1) name(0).toString else "*")
overlay.setVisibility(View.VISIBLE)
}
}
@@ -316,18 +260,14 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
}
def saveAppSettings(context: Context) {
- if (apps == null) return
- val proxiedApps = new StringBuilder
- apps.foreach(app =>
- if (app.proxied) {
- proxiedApps ++= app.uid.toString
- proxiedApps += '|'
- })
- val edit: SharedPreferences.Editor = ShadowsocksApplication.settings.edit
- edit.putString(Key.proxied, proxiedApps.toString())
- edit.apply
+ if (!appsLoading) ShadowsocksApplication.settings.edit.putString(Key.proxied, proxiedApps.mkString("|")).apply
}
var handler: Handler = null
+ override def onKeyUp(keyCode: Int, event: KeyEvent) = keyCode match {
+ case KeyEvent.KEYCODE_MENU =>
+ if (toolbar.isOverflowMenuShowing) toolbar.hideOverflowMenu else toolbar.showOverflowMenu
+ case _ => super.onKeyUp(keyCode, event)
+ }
}
diff --git a/src/main/scala/com/github/shadowsocks/AppManagerReceiver.scala b/src/main/scala/com/github/shadowsocks/AppManagerReceiver.scala
new file mode 100644
index 00000000..f754f11a
--- /dev/null
+++ b/src/main/scala/com/github/shadowsocks/AppManagerReceiver.scala
@@ -0,0 +1,11 @@
+package com.github.shadowsocks
+
+import android.content.{Intent, Context, BroadcastReceiver}
+
+/**
+ * @author Mygod
+ */
+class AppManagerReceiver extends BroadcastReceiver {
+ override def onReceive(context: Context, intent: Intent) = if (intent.getAction != Intent.ACTION_PACKAGE_REMOVED ||
+ !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) AppManager.cachedApps = null
+}
diff --git a/src/main/scala/com/github/shadowsocks/ServiceBoundContext.scala b/src/main/scala/com/github/shadowsocks/ServiceBoundContext.scala
index e6cbbeaf..c304f559 100644
--- a/src/main/scala/com/github/shadowsocks/ServiceBoundContext.scala
+++ b/src/main/scala/com/github/shadowsocks/ServiceBoundContext.scala
@@ -67,11 +67,13 @@ trait ServiceBoundContext extends Context {
} catch {
case ignored: RemoteException => // Nothing
}
+ callback = null
}
if (connection != null) {
unbindService(connection)
connection = null
}
+ bgService = null
}
}
}
diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala
index 9b652522..37895f30 100644
--- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala
+++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala
@@ -157,6 +157,54 @@ class Shadowsocks
// Services
var currentServiceName = classOf[ShadowsocksNatService].getName
+ private val callback = new IShadowsocksServiceCallback.Stub {
+ def stateChanged(s: Int, m: String) {
+ handler.post(() => if (state != s) {
+ s match {
+ case State.CONNECTING =>
+ fab.setBackgroundTintList(greyTint)
+ fab.setImageResource(R.drawable.ic_cloud_queue)
+ fab.setEnabled(false)
+ fabProgressCircle.show()
+ setPreferenceEnabled(enabled = false)
+ case State.CONNECTED =>
+ fab.setBackgroundTintList(greenTint)
+ if (state == State.CONNECTING) {
+ fabProgressCircle.beginFinalAnimation()
+ } else {
+ handler.postDelayed(() => fabProgressCircle.hide(), 1000)
+ }
+ fab.setEnabled(true)
+ changeSwitch(checked = true)
+ setPreferenceEnabled(enabled = false)
+ case State.STOPPED =>
+ fab.setBackgroundTintList(greyTint)
+ handler.postDelayed(() => fabProgressCircle.hide(), 1000)
+ fab.setEnabled(true)
+ changeSwitch(checked = false)
+ if (m != null) Snackbar.make(findViewById(android.R.id.content),
+ getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m), Snackbar.LENGTH_LONG).show
+ setPreferenceEnabled(enabled = true)
+ case State.STOPPING =>
+ fab.setBackgroundTintList(greyTint)
+ fab.setImageResource(R.drawable.ic_cloud_queue)
+ fab.setEnabled(false)
+ if (state == State.CONNECTED) fabProgressCircle.show() // ignore for stopped
+ setPreferenceEnabled(enabled = false)
+ }
+ state = s
+ })
+ }
+ def trafficUpdated(txRate: String, rxRate: String, txTotal: String, rxTotal: String) {
+ val trafficStat = getString(R.string.stat_summary)
+ .formatLocal(Locale.ENGLISH, txRate, rxRate, txTotal, rxTotal)
+ handler.post(() => {
+ preferences.findPreference(Key.stat).setSummary(trafficStat)
+ })
+ }
+ }
+
+ def attachService: Unit = attachService(callback)
override def onServiceConnected() {
// Update the UI
@@ -188,14 +236,6 @@ class Shadowsocks
if (fab != null) fab.setEnabled(false)
}
- def trafficUpdated(txRate: String, rxRate: String, txTotal: String, rxTotal: String) {
- val trafficStat = getString(R.string.stat_summary)
- .formatLocal(Locale.ENGLISH, txRate, rxRate, txTotal, rxTotal)
- handler.post(() => {
- preferences.findPreference(Key.stat).setSummary(trafficStat)
- })
- }
-
private lazy val preferences =
getFragmentManager.findFragmentById(android.R.id.content).asInstanceOf[ShadowsocksSettings]
private var adView: AdView = _
@@ -377,16 +417,7 @@ class Shadowsocks
})
// Bind to the service
- handler.post(() => {
- attachService(new IShadowsocksServiceCallback.Stub {
- override def stateChanged(state: Int, msg: String) {
- onStateChanged(state, msg)
- }
- override def trafficUpdated(txRate: String, rxRate: String, txTotal: String, rxTotal: String) {
- Shadowsocks.this.trafficUpdated(txRate, rxRate, txTotal, rxTotal)
- }
- })
- })
+ handler.post(() => attachService)
}
def reloadProfile() {
@@ -447,7 +478,7 @@ class Shadowsocks
// Check if current profile changed
if (ShadowsocksApplication.profileId != currentProfile.id) reloadProfile()
- trafficUpdated(TrafficMonitor.getTxRate, TrafficMonitor.getRxRate,
+ callback.trafficUpdated(TrafficMonitor.getTxRate, TrafficMonitor.getRxRate,
TrafficMonitor.getTxTotal, TrafficMonitor.getRxTotal)
}
@@ -584,42 +615,4 @@ class Shadowsocks
progressTag = -1
}
}
-
- def onStateChanged(s: Int, m: String) {
- handler.post(() => if (state != s) {
- s match {
- case State.CONNECTING =>
- fab.setBackgroundTintList(greyTint)
- fab.setImageResource(R.drawable.ic_cloud_queue)
- fab.setEnabled(false)
- fabProgressCircle.show()
- setPreferenceEnabled(enabled = false)
- case State.CONNECTED =>
- fab.setBackgroundTintList(greenTint)
- if (state == State.CONNECTING) {
- fabProgressCircle.beginFinalAnimation()
- } else {
- handler.postDelayed(() => fabProgressCircle.hide(), 1000)
- }
- fab.setEnabled(true)
- changeSwitch(checked = true)
- setPreferenceEnabled(enabled = false)
- case State.STOPPED =>
- fab.setBackgroundTintList(greyTint)
- handler.postDelayed(() => fabProgressCircle.hide(), 1000)
- fab.setEnabled(true)
- changeSwitch(checked = false)
- if (m != null) Snackbar.make(findViewById(android.R.id.content),
- getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m), Snackbar.LENGTH_LONG).show
- setPreferenceEnabled(enabled = true)
- case State.STOPPING =>
- fab.setBackgroundTintList(greyTint)
- fab.setImageResource(R.drawable.ic_cloud_queue)
- fab.setEnabled(false)
- if (state == State.CONNECTED) fabProgressCircle.show() // ignore for stopped
- setPreferenceEnabled(enabled = false)
- }
- state = s
- })
- }
}
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
index 16bd2a42..865cc586 100644
--- a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
@@ -54,7 +54,6 @@ import com.github.shadowsocks.aidl.Config
import com.github.shadowsocks.utils._
import scala.collection.JavaConversions._
-import scala.collection._
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -68,15 +67,14 @@ class ShadowsocksNatService extends BaseService {
"-j DNAT --to-destination 127.0.0.1:8123"
private var notification: ShadowsocksNotification = _
- var closeReceiver: BroadcastReceiver = null
- var connReceiver: BroadcastReceiver = null
- var apps: Array[ProxiedApp] = null
+ var closeReceiver: BroadcastReceiver = _
+ var connReceiver: BroadcastReceiver = _
val myUid = android.os.Process.myUid()
- var sslocalProcess: Process = null
- var sstunnelProcess: Process = null
- var redsocksProcess: Process = null
- var pdnsdProcess: Process = null
+ var sslocalProcess: Process = _
+ var sstunnelProcess: Process = _
+ var redsocksProcess: Process = _
+ var pdnsdProcess: Process = _
private val dnsAddressCache = new SparseArray[String]
@@ -378,16 +376,7 @@ class ShadowsocksNatService extends BaseService {
http_sb.append(Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS)
}
if (config.isProxyApps) {
- if (apps == null || apps.length <= 0) {
- apps = AppManager.getProxiedApps(this, config.proxiedAppString)
- }
- val uidSet: mutable.HashSet[Int] = new mutable.HashSet[Int]
- for (app <- apps) {
- if (app.proxied) {
- uidSet.add(app.uid)
- }
- }
- for (uid <- uidSet) {
+ for (uid <- config.proxiedAppString.split('|').distinct) {
if (!config.isBypassApps) {
http_sb.append((Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS).replace("-t nat", "-t nat -m owner --uid-owner " + uid))
} else {
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala
index 6d115bf8..f8f8b06e 100644
--- a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala
@@ -32,7 +32,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
.setWhen(0)
.setColor(ContextCompat.getColor(service, R.color.material_accent_500))
.setTicker(service.getString(R.string.forward_success))
- .setContentTitle(service.getString(R.string.service_running).formatLocal(Locale.ENGLISH, profileName))
+ .setContentTitle(profileName)
.setContentIntent(PendingIntent.getActivity(service, 0, new Intent(service, classOf[Shadowsocks])
.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), 0))
.setSmallIcon(R.drawable.ic_stat_shadowsocks)
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala
index 5daa69ad..16bd4cd0 100644
--- a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala
@@ -43,9 +43,6 @@ import android.content.{BroadcastReceiver, Context, Intent}
import com.github.shadowsocks.utils._
class ShadowsocksReceiver extends BroadcastReceiver {
-
- val TAG = "Shadowsocks"
-
def onReceive(context: Context, intent: Intent) {
if (ShadowsocksApplication.settings.getBoolean(Key.isAutoConnect, false)) {
Utils.startSsService(context)
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala
index 684fc0ed..b63c7d81 100644
--- a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala
@@ -67,7 +67,7 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
})
}
- override def onResume = {
+ override def onResume {
super.onResume
isProxyApps.setChecked(ShadowsocksApplication.settings.getBoolean(Key.isProxyApps, false)) // update
}
@@ -75,9 +75,8 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
def onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) = key match {
case Key.isNAT => if (ShadowsocksApplication.isRoot && activity != null) {
activity.handler.post(() => {
- val intent = activity.getIntent
- activity.finish()
- startActivity(intent)
+ activity.deattachService
+ activity.attachService
})
}
case _ =>
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
index ba0e2018..3140baaf 100644
--- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
@@ -54,7 +54,6 @@ import com.github.shadowsocks.utils._
import org.apache.commons.net.util.SubnetUtils
import scala.collection.JavaConversions._
-import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -64,16 +63,15 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val VPN_MTU = 1500
val PRIVATE_VLAN = "26.26.26.%s"
val PRIVATE_VLAN6 = "fdfe:dcba:9876::%s"
- var conn: ParcelFileDescriptor = null
- var apps: Array[ProxiedApp] = null
- var vpnThread: ShadowsocksVpnThread = null
+ var conn: ParcelFileDescriptor = _
+ var vpnThread: ShadowsocksVpnThread = _
private var notification: ShadowsocksNotification = _
- var closeReceiver: BroadcastReceiver = null
+ var closeReceiver: BroadcastReceiver = _
- var sslocalProcess: Process = null
- var sstunnelProcess: Process = null
- var pdnsdProcess: Process = null
- var tun2socksProcess: Process = null
+ var sslocalProcess: Process = _
+ var sstunnelProcess: Process = _
+ var pdnsdProcess: Process = _
+ var tun2socksProcess: Process = _
def isByass(net: SubnetUtils): Boolean = {
val info = net.getInfo
@@ -392,14 +390,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (Utils.isLollipopOrAbove) {
if (config.isProxyApps) {
- val apps = AppManager.getProxiedApps(this, config.proxiedAppString)
- val pkgSet: mutable.HashSet[String] = new mutable.HashSet[String]
- for (app <- apps) {
- if (app.proxied) {
- pkgSet.add(app.packageName)
- }
- }
- for (pkg <- pkgSet) {
+ for (pkg <- config.proxiedAppString.split('|').distinct) {
if (!config.isBypassApps) {
builder.addAllowedApplication(pkg)
} else {
diff --git a/src/main/scala/com/github/shadowsocks/helper/TaskerSettings.scala b/src/main/scala/com/github/shadowsocks/helper/TaskerSettings.scala
index 8fadf78a..2b2f18e5 100644
--- a/src/main/scala/com/github/shadowsocks/helper/TaskerSettings.scala
+++ b/src/main/scala/com/github/shadowsocks/helper/TaskerSettings.scala
@@ -44,7 +44,6 @@ import com.github.shadowsocks.{R, ShadowsocksApplication}
import com.twofortyfouram.locale.api.{Intent => ApiIntent}
object TaskerSettings {
- private val KEY_ACTION = "action"
private val KEY_SWITCH_ON = "switch_on"
private val KEY_PROFILE_ID = "profile_id"