diff --git a/project/Build.scala b/project/Build.scala index 758d03b2..d0fe8b6c 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -5,8 +5,8 @@ import org.scalasbt.androidplugin._ import org.scalasbt.androidplugin.AndroidKeys._ object App { - val version = "1.4.1" - val versionCode = 23 + val version = "1.4.2" + val versionCode = 24 } object General { diff --git a/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala index df3dabda..01095025 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala @@ -37,12 +37,8 @@ */ package com.github.shadowsocks -import android.app.Notification -import android.app.PendingIntent -import android.app.Service -import android.content.Context -import android.content.Intent -import android.content.SharedPreferences +import android.app.{NotificationManager, Notification, PendingIntent, Service} +import android.content._ import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.os._ @@ -222,7 +218,6 @@ class ShadowVpnService extends VpnService { } if (resolved && handleConnection) { - notifyAlert(getString(R.string.forward_success), getString(R.string.service_running)) handler.sendEmptyMessageDelayed(MSG_CONNECT_SUCCESS, 500) } else { notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed)) @@ -340,10 +335,13 @@ class ShadowVpnService extends VpnService { val openIntent: Intent = new Intent(this, classOf[Shadowsocks]) openIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) val contentIntent: PendingIntent = PendingIntent.getActivity(this, 0, openIntent, 0) - val closeIntent: Intent = new Intent(Utils.CLOSE_ACTION) - val actionIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0) val builder: NotificationCompat.Builder = new NotificationCompat.Builder(this) - builder.setSmallIcon(R.drawable.ic_stat_shadowsocks).setWhen(0).setTicker(title).setContentTitle(getString(R.string.app_name)).setContentText(info).setContentIntent(contentIntent).addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop), actionIntent) + builder + .setSmallIcon(R.drawable.ic_stat_shadowsocks).setWhen(0) + .setTicker(title).setContentTitle(getString(R.string.app_name)) + .setContentText(info).setContentIntent(contentIntent) + .setAutoCancel(true) + notificationManager.notify(1, builder.build) } override def onBind(intent: Intent): IBinder = { @@ -358,11 +356,20 @@ class ShadowVpnService extends VpnService { super.onCreate() EasyTracker.getTracker.sendEvent("service", "start", getVersionName, 0L) settings = PreferenceManager.getDefaultSharedPreferences(this) + + notificationManager = getSystemService(Context.NOTIFICATION_SERVICE).asInstanceOf[NotificationManager] + + // register close receiver + val filter = new IntentFilter() + filter.addAction(Intent.ACTION_SHUTDOWN) + filter.addAction(Utils.CLOSE_ACTION) + receiver = new CloseReceiver + registerReceiver(receiver, filter) } - /** Called when the activity is closed. */ - override def onDestroy() { + def destroy() { EasyTracker.getTracker.sendEvent("service", "stop", getVersionName, 0L) + if (receiver != null) unregisterReceiver(receiver) new Thread { override def run() { onDisconnect() @@ -376,21 +383,27 @@ class ShadowVpnService extends VpnService { conn.close() conn = null } - super.onDestroy() markServiceStopped() } + /** Called when the activity is closed. */ + override def onDestroy() { + destroy() + super.onDestroy() + } + def onDisconnect() { - Utils.runRootCommand(Utils.getIptables + " -t nat -F OUTPUT") val sb = new StringBuilder - sb.append("kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`").append("\n") + if (!waitForProcess("redsocks")) { + sb.append("kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`").append("\n") + } if (!waitForProcess("shadowsocks")) { sb.append("kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`").append("\n") } if (!waitForProcess("tun2socks")) { sb.append("kill -9 `cat /data/data/com.github.shadowsocks/tun2socks.pid`").append("\n") } - Utils.runRootCommand(sb.toString()) + Utils.runCommand(sb.toString()) } override def onStart(intent: Intent, startId: Int) { @@ -406,6 +419,13 @@ class ShadowVpnService extends VpnService { stopSelf() } + class CloseReceiver extends BroadcastReceiver { + def onReceive(p1: Context, p2: Intent) { + destroy() + stopSelf() + } + } + val handler: Handler = new Handler { override def handleMessage(msg: Message) { val ed: SharedPreferences.Editor = settings.edit @@ -428,6 +448,9 @@ class ShadowVpnService extends VpnService { super.handleMessage(msg) } } + + var notificationManager: NotificationManager = null + var receiver: CloseReceiver = null var appHost: String = null var remotePort: Int = 0 var localPort: Int = 0 diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index c9aa1697..100508f2 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -110,14 +110,15 @@ object Shadowsocks { private def setPreferenceEnabled() { val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity) val enabled: Boolean = !settings.getBoolean("isRunning", false) && !settings.getBoolean("isConnecting", false) - for (name <- FEATRUE_PREFS) { + for (name <- Shadowsocks.FEATRUE_PREFS) { val pref: Preference = findPreference(name) if (pref != null) { if ((name == "isBypassApps") || (name == "proxyedApps")) { val isGlobalProxy: Boolean = settings.getBoolean("isGlobalProxy", false) pref.setEnabled(enabled && !isGlobalProxy) - } - else { + } else if (name == "isAutoConnect") { + pref.setEnabled(Utils.getRoot) + } else { pref.setEnabled(enabled) } } @@ -411,8 +412,9 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton. if ((name == "isBypassApps") || (name == "proxyedApps")) { val isGlobalProxy: Boolean = settings.getBoolean("isGlobalProxy", false) pref.setEnabled(enabled && !isGlobalProxy) - } - else { + } else if (name == "isAutoConnect") { + pref.setEnabled(Utils.getRoot) + } else { pref.setEnabled(enabled) } } @@ -531,7 +533,7 @@ class Shadowsocks extends UnifiedSherlockPreferenceActivity with CompoundButton. stopService(new Intent(this, classOf[ShadowVpnService])) } if (ShadowsocksService.isServiceStarted) { - stopService(new Intent(this, classOf[ShadowsocksService])) + sendBroadcast(new Intent(Utils.CLOSE_ACTION)) } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala index 03e05c6c..40ecd5f8 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala @@ -90,11 +90,8 @@ class ShadowsocksReceiver extends BroadcastReceiver { if (ShadowsocksService.isServiceStarted) return val it: Intent = new Intent(context, classOf[ShadowsocksService]) context.startService(it) - } else { - if (ShadowVpnService.isServiceStarted) return - val it: Intent = new Intent(context, classOf[ShadowVpnService]) - context.startService(it) } + } } } \ No newline at end of file diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala index 6616d6cd..afb2aab3 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala @@ -61,9 +61,6 @@ import java.io.IOException import java.lang.ref.WeakReference import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method -import java.net.Inet6Address -import java.net.InetAddress -import java.net.UnknownHostException import org.apache.http.conn.util.InetAddressUtils import scala.collection._ import org.xbill.DNS._ @@ -243,7 +240,7 @@ class ShadowsocksService extends Service { Log.d(TAG, "IPTABLES: " + Utils.getIptables) hasRedirectSupport = Utils.getHasRedirectSupport if (resolved && handleConnection) { - notifyAlert(getString(R.string.forward_success), getString(R.string.service_running)) + notifyForegroundAlert(getString(R.string.forward_success), getString(R.string.service_running)) handler.sendEmptyMessageDelayed(MSG_CONNECT_SUCCESS, 500) } else { @@ -323,17 +320,35 @@ class ShadowsocksService extends Service { ShadowsocksService.sRunningInstance = null } - def notifyAlert(title: String, info: String) { + def notifyForegroundAlert(title: String, info: String) { val openIntent: Intent = new Intent(this, classOf[Shadowsocks]) openIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) val contentIntent: PendingIntent = PendingIntent.getActivity(this, 0, openIntent, 0) val closeIntent: Intent = new Intent(Utils.CLOSE_ACTION) val actionIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0) val builder: NotificationCompat.Builder = new NotificationCompat.Builder(this) - builder.setSmallIcon(R.drawable.ic_stat_shadowsocks).setWhen(0).setTicker(title).setContentTitle(getString(R.string.app_name)).setContentText(info).setContentIntent(contentIntent).addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop), actionIntent) + builder + .setSmallIcon(R.drawable.ic_stat_shadowsocks).setWhen(0) + .setTicker(title).setContentTitle(getString(R.string.app_name)) + .setContentText(info).setContentIntent(contentIntent) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop), actionIntent) startForegroundCompat(1, builder.build) } + def notifyAlert(title: String, info: String) { + val openIntent: Intent = new Intent(this, classOf[Shadowsocks]) + openIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + val contentIntent: PendingIntent = PendingIntent.getActivity(this, 0, openIntent, 0) + val builder: NotificationCompat.Builder = new NotificationCompat.Builder(this) + builder + .setSmallIcon(R.drawable.ic_stat_shadowsocks).setWhen(0) + .setTicker(title) + .setContentTitle(getString(R.string.app_name)) + .setContentText(info).setContentIntent(contentIntent) + .setAutoCancel(true) + notificationManager.notify(1, builder.build) + } + def onBind(intent: Intent): IBinder = { null } @@ -378,15 +393,8 @@ class ShadowsocksService extends Service { ed.putBoolean("isRunning", false) ed.putBoolean("isConnecting", false) ed.commit - try { - notificationManager.cancel(0) - } - catch { - case ignore: Exception => { - } - } - super.onDestroy() markServiceStopped() + super.onDestroy() } def onDisconnect() {