Merge pull request #467 from Mygod/master
Cache proxied apps for even better performance
This commit is contained in:
@@ -1 +0,0 @@
|
||||
sbt.version=0.13.8
|
||||
+1
-1
@@ -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")
|
||||
|
||||
@@ -109,6 +109,14 @@
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver android:name=".AppManagerReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PACKAGE_ADDED" />
|
||||
<action android:name="android.intent.action.PACKAGE_REMOVED" />
|
||||
<data android:scheme="package"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".ShadowsocksReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
<string name="auto_connect">自动连接</string>
|
||||
<string name="auto_connect_summary">随系统启动后台服务</string>
|
||||
<string name="forward_success">后台服务已开始运行。</string>
|
||||
<string name="service_running">%s</string>
|
||||
<string name="service_failed">无法连接远程服务器</string>
|
||||
<string name="stop">停止服务</string>
|
||||
<string name="stopping">正在关闭……</string>
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
|
||||
<!-- notification category -->
|
||||
<string name="forward_success">Shadowsocks started.</string>
|
||||
<string name="service_running">%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>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -67,11 +67,13 @@ trait ServiceBoundContext extends Context {
|
||||
} catch {
|
||||
case ignored: RemoteException => // Nothing
|
||||
}
|
||||
callback = null
|
||||
}
|
||||
if (connection != null) {
|
||||
unbindService(connection)
|
||||
connection = null
|
||||
}
|
||||
bgService = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 _ =>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user