Commit c6a7560b authored by Mygod's avatar Mygod

Cache proxied apps for even better performance

parent dd420661
...@@ -109,6 +109,14 @@ ...@@ -109,6 +109,14 @@
</intent-filter> </intent-filter>
</service> </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"> <receiver android:name=".ShadowsocksReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.BOOT_COMPLETED"/>
......
...@@ -39,7 +39,8 @@ ...@@ -39,7 +39,8 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.content.{ClipData, ClipboardManager, Context, SharedPreferences} import android.content.pm.PackageManager
import android.content.{ClipData, ClipboardManager, Context}
import android.graphics.PixelFormat import android.graphics.PixelFormat
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.{Bundle, Handler} import android.os.{Bundle, Handler}
...@@ -61,14 +62,24 @@ import scala.concurrent.ExecutionContext.Implicits.global ...@@ -61,14 +62,24 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future import scala.concurrent.Future
import scala.language.implicitConversions import scala.language.implicitConversions
case class ProxiedApp(uid: Int, name: String, packageName: String, icon: Drawable, var proxied: Boolean) object AppManager {
case class ProxiedApp(uid: Int, name: String, packageName: String, icon: Drawable)
private case class ListEntry(switch: Switch, text: TextView, icon: ImageView)
case class ListEntry(switch: Switch, text: TextView, icon: ImageView) 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 class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnClickListener
with OnMenuItemClickListener { with OnMenuItemClickListener {
import AppManager._
private var apps: mutable.Buffer[ProxiedApp] = _ private var apps: Array[ProxiedApp] = _
private var proxiedApps: mutable.HashSet[Int] = _
private var toolbar: Toolbar = _ private var toolbar: Toolbar = _
private var appListView: ListView = _ private var appListView: ListView = _
private var loadingView: View = _ private var loadingView: View = _
...@@ -78,23 +89,10 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -78,23 +89,10 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
def loadApps() { def loadApps() {
appsLoading = true appsLoading = true
val proxiedApps = ShadowsocksApplication.settings.getString(Key.proxied, "").split('|').toSet proxiedApps = ShadowsocksApplication.settings.getString(Key.proxied, "").split('|').map(_.toInt).to[mutable.HashSet]
apps = getApps(getPackageManager).sortWith((a, b) => {
val packageManager = getPackageManager val aProxied = proxiedApps.contains(a.uid)
apps = packageManager.getInstalledApplications(0).filter(_.uid >= 10000).map { if (aProxied ^ proxiedApps.contains(b.uid)) aProxied else a.name < b.name
case a =>
val uid = a.uid
val userName = uid.toString
val name = packageManager.getApplicationLabel(a).toString
val packageName = a.packageName
val proxied = proxiedApps.contains(userName)
new ProxiedApp(uid, name, packageName, a.loadIcon(packageManager), proxied)
}.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 a.proxied
}) })
adapter = new ArrayAdapter[ProxiedApp](this, R.layout.layout_apps_item, R.id.itemtext, apps) { adapter = new ArrayAdapter[ProxiedApp](this, R.layout.layout_apps_item, R.id.itemtext, apps) {
override def getView(position: Int, view: View, parent: ViewGroup): View = { override def getView(position: Int, view: View, parent: ViewGroup): View = {
...@@ -102,10 +100,9 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -102,10 +100,9 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
var entry: ListEntry = null var entry: ListEntry = null
if (convertView == null) { if (convertView == null) {
convertView = getLayoutInflater.inflate(R.layout.layout_apps_item, parent, false) convertView = getLayoutInflater.inflate(R.layout.layout_apps_item, parent, false)
val icon = convertView.findViewById(R.id.itemicon).asInstanceOf[ImageView] entry = new ListEntry(convertView.findViewById(R.id.itemcheck).asInstanceOf[Switch],
val switch = convertView.findViewById(R.id.itemcheck).asInstanceOf[Switch] convertView.findViewById(R.id.itemtext).asInstanceOf[TextView],
val text = convertView.findViewById(R.id.itemtext).asInstanceOf[TextView] convertView.findViewById(R.id.itemicon).asInstanceOf[ImageView])
entry = new ListEntry(switch, text, icon)
convertView.setOnClickListener(AppManager.this) convertView.setOnClickListener(AppManager.this)
convertView.setTag(entry) convertView.setTag(entry)
entry.switch.setOnCheckedChangeListener(AppManager.this) entry.switch.setOnCheckedChangeListener(AppManager.this)
...@@ -119,19 +116,19 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -119,19 +116,19 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
entry.icon.setImageDrawable(app.icon) entry.icon.setImageDrawable(app.icon)
val switch = entry.switch val switch = entry.switch
switch.setTag(app) switch.setTag(app)
switch.setChecked(app.proxied) switch.setChecked(proxiedApps.contains(app.uid))
entry.text.setTag(switch) entry.text.setTag(switch)
convertView convertView
} }
} }
} }
private def setProxied(uid: Int, proxied: Boolean) = if (proxied) proxiedApps.add(uid) else proxiedApps.remove(uid)
/** Called an application is check/unchecked */ /** Called an application is check/unchecked */
def onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { def onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
val app: ProxiedApp = buttonView.getTag.asInstanceOf[ProxiedApp] val app: ProxiedApp = buttonView.getTag.asInstanceOf[ProxiedApp]
if (app != null) { if (app != null) setProxied(app.uid, isChecked)
app.proxied = isChecked
}
saveAppSettings(this) saveAppSettings(this)
} }
...@@ -139,8 +136,9 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -139,8 +136,9 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
val switch = v.getTag.asInstanceOf[ListEntry].switch val switch = v.getTag.asInstanceOf[ListEntry].switch
val app: ProxiedApp = switch.getTag.asInstanceOf[ProxiedApp] val app: ProxiedApp = switch.getTag.asInstanceOf[ProxiedApp]
if (app != null) { if (app != null) {
app.proxied = !app.proxied val proxied = !proxiedApps.contains(app.uid)
switch.setChecked(app.proxied) setProxied(app.uid, proxied)
switch.setChecked(proxied)
} }
saveAppSettings(this) saveAppSettings(this)
} }
...@@ -235,12 +233,8 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -235,12 +233,8 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
def onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, def onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int,
totalItemCount: Int) { totalItemCount: Int) {
if (visible) { if (visible) {
val name: String = apps(firstVisibleItem).name val name = apps(firstVisibleItem).name
if (name != null && name.length > 1) { overlay.setText(if (name != null && name.length > 1) name(0).toString else "*")
overlay.setText(apps(firstVisibleItem).name.substring(0, 1))
} else {
overlay.setText("*")
}
overlay.setVisibility(View.VISIBLE) overlay.setVisibility(View.VISIBLE)
} }
} }
...@@ -266,16 +260,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -266,16 +260,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
} }
def saveAppSettings(context: Context) { def saveAppSettings(context: Context) {
if (apps == null) return if (!appsLoading) ShadowsocksApplication.settings.edit.putString(Key.proxied, proxiedApps.mkString("|")).apply
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
} }
var handler: Handler = null var handler: Handler = null
......
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
}
...@@ -43,9 +43,6 @@ import android.content.{BroadcastReceiver, Context, Intent} ...@@ -43,9 +43,6 @@ import android.content.{BroadcastReceiver, Context, Intent}
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
class ShadowsocksReceiver extends BroadcastReceiver { class ShadowsocksReceiver extends BroadcastReceiver {
val TAG = "Shadowsocks"
def onReceive(context: Context, intent: Intent) { def onReceive(context: Context, intent: Intent) {
if (ShadowsocksApplication.settings.getBoolean(Key.isAutoConnect, false)) { if (ShadowsocksApplication.settings.getBoolean(Key.isAutoConnect, false)) {
Utils.startSsService(context) Utils.startSsService(context)
......
...@@ -63,16 +63,15 @@ class ShadowsocksVpnService extends VpnService with BaseService { ...@@ -63,16 +63,15 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val VPN_MTU = 1500 val VPN_MTU = 1500
val PRIVATE_VLAN = "26.26.26.%s" val PRIVATE_VLAN = "26.26.26.%s"
val PRIVATE_VLAN6 = "fdfe:dcba:9876::%s" val PRIVATE_VLAN6 = "fdfe:dcba:9876::%s"
var conn: ParcelFileDescriptor = null var conn: ParcelFileDescriptor = _
var apps: Array[ProxiedApp] = null var vpnThread: ShadowsocksVpnThread = _
var vpnThread: ShadowsocksVpnThread = null
private var notification: ShadowsocksNotification = _ private var notification: ShadowsocksNotification = _
var closeReceiver: BroadcastReceiver = null var closeReceiver: BroadcastReceiver = _
var sslocalProcess: Process = null var sslocalProcess: Process = _
var sstunnelProcess: Process = null var sstunnelProcess: Process = _
var pdnsdProcess: Process = null var pdnsdProcess: Process = _
var tun2socksProcess: Process = null var tun2socksProcess: Process = _
def isByass(net: SubnetUtils): Boolean = { def isByass(net: SubnetUtils): Boolean = {
val info = net.getInfo val info = net.getInfo
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment