From 99a6c4ff3e9726d10c30d8cfc87c02bdc0d28b58 Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 24 Nov 2015 18:09:27 +0800 Subject: [PATCH 1/6] Extract notification logic --- .../com/github/shadowsocks/BaseService.scala | 9 +-- .../shadowsocks/ShadowsocksNatService.scala | 70 ++----------------- .../shadowsocks/ShadowsocksNotification.scala | 67 ++++++++++++++++++ .../shadowsocks/ShadowsocksVpnService.scala | 35 +--------- 4 files changed, 76 insertions(+), 105 deletions(-) create mode 100644 src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala diff --git a/src/main/scala/com/github/shadowsocks/BaseService.scala b/src/main/scala/com/github/shadowsocks/BaseService.scala index bbd24e30..a372066e 100644 --- a/src/main/scala/com/github/shadowsocks/BaseService.scala +++ b/src/main/scala/com/github/shadowsocks/BaseService.scala @@ -41,14 +41,13 @@ package com.github.shadowsocks import java.util.{Timer, TimerTask} -import android.app.Notification +import android.app.Service import android.content.Context import android.os.{Handler, RemoteCallbackList} -import android.util.Log import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback} import com.github.shadowsocks.utils.{State, TrafficMonitor, TrafficMonitorThread} -trait BaseService { +trait BaseService extends Service { @volatile private var state = State.INIT @volatile private var callbackCount = 0 @@ -197,8 +196,4 @@ trait BaseService { state = s }) } - - def initSoundVibrateLights(notification: Notification) { - notification.sound = null - } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala index 75f7cca0..9f87966a 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala @@ -44,13 +44,10 @@ import java.lang.Process import java.net.{Inet6Address, InetAddress} import java.util.Locale -import android.app._ import android.content._ import android.content.pm.{PackageInfo, PackageManager} import android.net.{ConnectivityManager, Network} import android.os._ -import android.support.v4.app.NotificationCompat -import android.support.v4.content.ContextCompat import android.util.{Log, SparseArray} import android.widget.Toast import com.github.shadowsocks.aidl.Config @@ -62,7 +59,7 @@ import scala.collection.mutable.ArrayBuffer import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -class ShadowsocksNatService extends Service with BaseService { +class ShadowsocksNatService extends BaseService { val TAG = "ShadowsocksNatService" @@ -70,7 +67,7 @@ class ShadowsocksNatService extends Service with BaseService { val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + "-j DNAT --to-destination 127.0.0.1:8123" - var lockReceiver: BroadcastReceiver = null + private var notification: ShadowsocksNotification = _ var closeReceiver: BroadcastReceiver = null var connReceiver: BroadcastReceiver = null var apps: Array[ProxiedApp] = null @@ -324,33 +321,6 @@ class ShadowsocksNatService extends Service with BaseService { true } - def notifyForegroundAlert(title: String, info: String, visible: Boolean) { - val openIntent = new Intent(this, classOf[Shadowsocks]) - openIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) - val contentIntent = PendingIntent.getActivity(this, 0, openIntent, 0) - val closeIntent = new Intent(Action.CLOSE) - val actionIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0) - val builder = new NotificationCompat.Builder(this) - - builder - .setWhen(0) - .setColor(ContextCompat.getColor(this, R.color.material_accent_500)) - .setTicker(title) - .setContentTitle(getString(R.string.app_name)) - .setContentText(info) - .setContentIntent(contentIntent) - .setSmallIcon(R.drawable.ic_stat_shadowsocks) - .addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop), - actionIntent) - - if (visible) - builder.setPriority(NotificationCompat.PRIORITY_DEFAULT) - else - builder.setPriority(NotificationCompat.PRIORITY_MIN) - - startForeground(1, builder.build) - } - def onBind(intent: Intent): IBinder = { Log.d(TAG, "onBind") if (Action.SERVICE == intent.getAction) { @@ -443,30 +413,6 @@ class ShadowsocksNatService extends Service with BaseService { } registerReceiver(closeReceiver, filter) - if (Utils.isLollipopOrAbove) { - val screenFilter = new IntentFilter() - screenFilter.addAction(Intent.ACTION_SCREEN_ON) - screenFilter.addAction(Intent.ACTION_SCREEN_OFF) - screenFilter.addAction(Intent.ACTION_USER_PRESENT) - lockReceiver = (context: Context, intent: Intent) => if (getState == State.CONNECTED) { - val action = intent.getAction - if (action == Intent.ACTION_SCREEN_OFF) { - notifyForegroundAlert(getString(R.string.forward_success), - getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false) - } else if (action == Intent.ACTION_SCREEN_ON) { - val keyGuard = getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager] - if (!keyGuard.inKeyguardRestrictedInputMode) { - notifyForegroundAlert(getString(R.string.forward_success), - getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true) - } - } else if (action == Intent.ACTION_USER_PRESENT) { - notifyForegroundAlert(getString(R.string.forward_success), - getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true) - } - } - registerReceiver(lockReceiver, screenFilter) - } - ShadowsocksApplication.track(TAG, "start") changeState(State.CONNECTING) @@ -507,8 +453,7 @@ class ShadowsocksNatService extends Service with BaseService { // Set DNS flushDns() - notifyForegroundAlert(getString(R.string.forward_success), - getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true) + notification = new ShadowsocksNotification(this, config.profileName, true) changeState(State.CONNECTED) } else { changeState(State.STOPPED, getString(R.string.service_failed)) @@ -531,12 +476,7 @@ class ShadowsocksNatService extends Service with BaseService { closeReceiver = null } - if (Utils.isLollipopOrAbove) { - if (lockReceiver != null) { - unregisterReceiver(lockReceiver) - lockReceiver = null - } - } + notification.destroy() ShadowsocksApplication.track(TAG, "stop") @@ -548,8 +488,6 @@ class ShadowsocksNatService extends Service with BaseService { stopSelf() } - stopForeground(true) - // change the state changeState(State.STOPPED) } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala new file mode 100644 index 00000000..8228f9c9 --- /dev/null +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala @@ -0,0 +1,67 @@ +package com.github.shadowsocks + +import java.util.Locale + +import android.app.{KeyguardManager, PendingIntent} +import android.content.{BroadcastReceiver, Context, Intent, IntentFilter} +import android.support.v4.app.NotificationCompat +import android.support.v4.app.NotificationCompat.BigTextStyle +import android.support.v4.content.ContextCompat +import com.github.shadowsocks.utils.{Action, State, Utils} + +/** + * @author Mygod + */ +class ShadowsocksNotification(private val service: BaseService, profileName: String, visible: Boolean = false) { + private lazy val keyGuard = service.getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager] + private var lockReceiver: BroadcastReceiver = _ + + private val builder = new NotificationCompat.Builder(service) + .setWhen(0) + .setColor(ContextCompat.getColor(service, R.color.material_accent_500)) + .setTicker(service.getString(R.string.forward_success)) + .setContentTitle(service.getString(R.string.app_name)) + .setContentText(service.getString(R.string.service_running).formatLocal(Locale.ENGLISH, 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) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, service.getString(R.string.stop), + PendingIntent.getBroadcast(service, 0, new Intent(Action.CLOSE), 0)) + private val style = new BigTextStyle(builder) + if (visible && Utils.isLollipopOrAbove) { + val screenFilter = new IntentFilter() + screenFilter.addAction(Intent.ACTION_SCREEN_ON) + screenFilter.addAction(Intent.ACTION_SCREEN_OFF) + screenFilter.addAction(Intent.ACTION_USER_PRESENT) + lockReceiver = (context: Context, intent: Intent) => if (service.getState == State.CONNECTED) { + val action = intent.getAction + if (action == Intent.ACTION_SCREEN_OFF) { + setVisible(false).show() + } else if (action == Intent.ACTION_SCREEN_ON) { + if (!keyGuard.inKeyguardRestrictedInputMode) { + setVisible(true).show() + } + } else if (action == Intent.ACTION_USER_PRESENT) { + setVisible(true).show() + } + } + service.registerReceiver(lockReceiver, screenFilter) + } + setVisible(visible).show() + + def setVisible(visible: Boolean) = { + builder.setPriority(if (visible) NotificationCompat.PRIORITY_DEFAULT else NotificationCompat.PRIORITY_MIN) + this + } + + def notification = style.build() + def show() = service.startForeground(1, notification) + + def destroy() = { + if (lockReceiver != null) { + service.unregisterReceiver(lockReceiver) + lockReceiver = null + } + service.stopForeground(true) + } +} diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala index 7c07e8e2..cc89cdc9 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala @@ -43,13 +43,10 @@ import java.io.File import java.lang.Process import java.util.Locale -import android.app._ import android.content._ import android.content.pm.{PackageInfo, PackageManager} import android.net.VpnService import android.os._ -import android.support.v4.app.NotificationCompat -import android.support.v4.content.ContextCompat import android.util.Log import android.widget.Toast import com.github.shadowsocks.aidl.Config @@ -70,6 +67,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { var conn: ParcelFileDescriptor = null var apps: Array[ProxiedApp] = null var vpnThread: ShadowsocksVpnThread = null + private var notification: ShadowsocksNotification = _ var closeReceiver: BroadcastReceiver = null var sslocalProcess: Process = null @@ -108,32 +106,6 @@ class ShadowsocksVpnService extends VpnService with BaseService { null } - def notifyForegroundAlert(title: String, info: String, visible: Boolean) { - val openIntent = new Intent(this, classOf[Shadowsocks]) - val contentIntent = PendingIntent.getActivity(this, 0, openIntent, 0) - val closeIntent = new Intent(Action.CLOSE) - val actionIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0) - val builder = new NotificationCompat.Builder(this) - - builder - .setWhen(0) - .setColor(ContextCompat.getColor(this, R.color.material_accent_500)) - .setTicker(title) - .setContentTitle(getString(R.string.app_name)) - .setContentText(info) - .setContentIntent(contentIntent) - .setSmallIcon(R.drawable.ic_stat_shadowsocks) - .addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop), - actionIntent) - - if (visible) - builder.setPriority(NotificationCompat.PRIORITY_DEFAULT) - else - builder.setPriority(NotificationCompat.PRIORITY_MIN) - - startForeground(1, builder.build) - } - override def onCreate() { super.onCreate() ConfigUtils.refresh(this) @@ -152,7 +124,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { vpnThread = null } - stopForeground(true) + notification.destroy() // channge the state changeState(State.STOPPING) @@ -275,8 +247,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { } if (resolved && handleConnection) { - notifyForegroundAlert(getString(R.string.forward_success), - getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false) + notification = new ShadowsocksNotification(this, config.profileName) changeState(State.CONNECTED) } else { changeState(State.STOPPED, getString(R.string.service_failed)) From 118aa1271c0c5a9b4ac390d9b24334d7ae289e85 Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 24 Nov 2015 18:12:24 +0800 Subject: [PATCH 2/6] Fix bypass not copied --- .../scala/com/github/shadowsocks/database/ProfileManager.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/com/github/shadowsocks/database/ProfileManager.scala b/src/main/scala/com/github/shadowsocks/database/ProfileManager.scala index bf01d1e3..0cf4d8e5 100644 --- a/src/main/scala/com/github/shadowsocks/database/ProfileManager.scala +++ b/src/main/scala/com/github/shadowsocks/database/ProfileManager.scala @@ -59,6 +59,7 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) { profile.route = oldProfile.route profile.ipv6 = oldProfile.ipv6 profile.proxyApps = oldProfile.proxyApps + profile.bypass = oldProfile.bypass profile.individual = oldProfile.individual profile.udpdns = oldProfile.udpdns case _ => From 5e0d52324b9f7699d0f2c779e8c1bfc505d44564 Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 24 Nov 2015 19:25:41 +0800 Subject: [PATCH 3/6] Add traffic to notification --- src/main/res/values/strings.xml | 1 + .../com/github/shadowsocks/BaseService.scala | 2 +- .../com/github/shadowsocks/Shadowsocks.scala | 2 +- .../shadowsocks/ShadowsocksNatService.scala | 2 +- .../shadowsocks/ShadowsocksNotification.scala | 51 ++++++++++++------- .../shadowsocks/ShadowsocksVpnService.scala | 2 +- 6 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 02f32d08..444b5ddf 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -15,6 +15,7 @@ Network Traffic Sent: \t\t\t\t\t%3$s\t↑\t%1$s/s\nReceived: \t%4$s\t↓\t%2$s/s \n%1$s↑\t%2$s↓ + %1$s/s↑\t%2$s/s↓ Byte Bytes diff --git a/src/main/scala/com/github/shadowsocks/BaseService.scala b/src/main/scala/com/github/shadowsocks/BaseService.scala index a372066e..5b8a8314 100644 --- a/src/main/scala/com/github/shadowsocks/BaseService.scala +++ b/src/main/scala/com/github/shadowsocks/BaseService.scala @@ -58,7 +58,7 @@ trait BaseService extends Service { final val callbacks = new RemoteCallbackList[IShadowsocksServiceCallback] - protected val binder = new IShadowsocksService.Stub { + val binder = new IShadowsocksService.Stub { override def getMode: Int = { getServiceMode } diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index aa6ecc75..32aba6a5 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -346,7 +346,7 @@ class Shadowsocks super.onCreate(savedInstanceState) setContentView(R.layout.layout_main) - // Initialize action bar + // Initialize Toolbar val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar] toolbar.setTitle(getString(R.string.screen_name)) toolbar.setTitleTextAppearance(toolbar.getContext, R.style.Toolbar_Logo) diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala index 9f87966a..16bd2a42 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala @@ -453,8 +453,8 @@ class ShadowsocksNatService extends BaseService { // Set DNS flushDns() - notification = new ShadowsocksNotification(this, config.profileName, true) changeState(State.CONNECTED) + notification = new ShadowsocksNotification(this, config.profileName, true) } else { changeState(State.STOPPED, getString(R.string.service_failed)) stopRunner() diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala index 8228f9c9..8a8b06bd 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala @@ -7,54 +7,66 @@ import android.content.{BroadcastReceiver, Context, Intent, IntentFilter} import android.support.v4.app.NotificationCompat import android.support.v4.app.NotificationCompat.BigTextStyle import android.support.v4.content.ContextCompat +import com.github.shadowsocks.aidl.IShadowsocksServiceCallback.Stub import com.github.shadowsocks.utils.{Action, State, Utils} /** * @author Mygod */ -class ShadowsocksNotification(private val service: BaseService, profileName: String, visible: Boolean = false) { +class ShadowsocksNotification(private val service: BaseService, profileName: String, visible: Boolean = false, + private val showTraffic: Boolean = true) { private lazy val keyGuard = service.getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager] + private lazy val callback = new Stub { + override def stateChanged(state: Int, msg: String) = () // ignore + override def trafficUpdated(txRate: String, rxRate: String, txTotal: String, rxTotal: String) { + builder.setContentText(service.getString(R.string.traffic_summary).formatLocal(Locale.ENGLISH, txRate, rxRate)) + style.bigText(service.getString(R.string.stat_summary) + .formatLocal(Locale.ENGLISH, txRate, rxRate, txTotal, rxTotal)) + show() + } + } private var lockReceiver: BroadcastReceiver = _ private val builder = new NotificationCompat.Builder(service) .setWhen(0) .setColor(ContextCompat.getColor(service, R.color.material_accent_500)) .setTicker(service.getString(R.string.forward_success)) - .setContentTitle(service.getString(R.string.app_name)) - .setContentText(service.getString(R.string.service_running).formatLocal(Locale.ENGLISH, profileName)) + .setContentTitle(service.getString(R.string.app_name) + ": " + 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) .addAction(android.R.drawable.ic_menu_close_clear_cancel, service.getString(R.string.stop), PendingIntent.getBroadcast(service, 0, new Intent(Action.CLOSE), 0)) - private val style = new BigTextStyle(builder) - if (visible && Utils.isLollipopOrAbove) { + private lazy val style = new BigTextStyle(builder) + private val showOnUnlock = visible && Utils.isLollipopOrAbove + if (showOnUnlock || showTraffic) { + update(Intent.ACTION_USER_PRESENT) + lockReceiver = (context: Context, intent: Intent) => update(intent.getAction) val screenFilter = new IntentFilter() screenFilter.addAction(Intent.ACTION_SCREEN_ON) screenFilter.addAction(Intent.ACTION_SCREEN_OFF) screenFilter.addAction(Intent.ACTION_USER_PRESENT) - lockReceiver = (context: Context, intent: Intent) => if (service.getState == State.CONNECTED) { - val action = intent.getAction - if (action == Intent.ACTION_SCREEN_OFF) { - setVisible(false).show() - } else if (action == Intent.ACTION_SCREEN_ON) { - if (!keyGuard.inKeyguardRestrictedInputMode) { - setVisible(true).show() - } - } else if (action == Intent.ACTION_USER_PRESENT) { - setVisible(true).show() - } - } service.registerReceiver(lockReceiver, screenFilter) + } else setVisible(visible).show() + + private def update(action: String) = if (service.getState == State.CONNECTED) { + if (action == Intent.ACTION_SCREEN_OFF) { + if (showOnUnlock) setVisible(false) + if (showTraffic) service.binder.unregisterCallback(callback) + } else if (action == Intent.ACTION_USER_PRESENT || + action == Intent.ACTION_SCREEN_ON && !keyGuard.inKeyguardRestrictedInputMode) { + setVisible(showOnUnlock) + if (showTraffic) service.binder.registerCallback(callback) + } + show() } - setVisible(visible).show() def setVisible(visible: Boolean) = { builder.setPriority(if (visible) NotificationCompat.PRIORITY_DEFAULT else NotificationCompat.PRIORITY_MIN) this } - def notification = style.build() + def notification = if (showTraffic) style.build() else builder.build() def show() = service.startForeground(1, notification) def destroy() = { @@ -62,6 +74,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str service.unregisterReceiver(lockReceiver) lockReceiver = null } + if (showTraffic) service.binder.unregisterCallback(callback) service.stopForeground(true) } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala index cc89cdc9..4a29ed6c 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala @@ -247,8 +247,8 @@ class ShadowsocksVpnService extends VpnService with BaseService { } if (resolved && handleConnection) { - notification = new ShadowsocksNotification(this, config.profileName) changeState(State.CONNECTED) + notification = new ShadowsocksNotification(this, config.profileName) } else { changeState(State.STOPPED, getString(R.string.service_failed)) stopRunner() From 9fa57dfe4bf56ef70535d1cdbbfe08a4e1ec42ed Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 24 Nov 2015 19:28:31 +0800 Subject: [PATCH 4/6] Refine service_running string --- src/main/res/values-zh/strings.xml | 2 +- src/main/res/values/strings.xml | 2 +- .../scala/com/github/shadowsocks/ShadowsocksNotification.scala | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml index 9db0cf6c..bb8dbe89 100755 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -35,7 +35,7 @@ 流量统计 在通知栏上显示当前上下行速率 后台服务已开始运行。 - 已连接到「%s」 + 影梭 → %s 无法连接远程服务器 停止服务 正在关闭…… diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 444b5ddf..564fb4eb 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -62,7 +62,7 @@ Shadowsocks started. - Connected to "%s" + Shadowsocks → %s Failed to connect the remote server Stop the service Shutting down… diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala index 8a8b06bd..a2d6b7b7 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala @@ -31,7 +31,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.app_name) + ": " + profileName) + .setContentTitle(service.getString(R.string.service_running).formatLocal(Locale.ENGLISH, 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) From c1730e4fa667d59f9b15876fcbd089ae70bcac19 Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 24 Nov 2015 21:16:08 +0800 Subject: [PATCH 5/6] Add a toggle for showing traffic in notification. TODO: Disabling it causes weird behavior. :-( --- src/main/res/values-zh/strings.xml | 3 +- src/main/res/values/strings.xml | 3 +- src/main/res/xml/pref_all.xml | 2 ++ .../com/github/shadowsocks/Shadowsocks.scala | 5 +-- .../shadowsocks/ShadowsocksApplication.scala | 3 +- .../shadowsocks/ShadowsocksNotification.scala | 35 +++++++++++-------- .../shadowsocks/ShadowsocksSettings.scala | 20 ++++++----- .../github/shadowsocks/utils/Constants.scala | 1 + 8 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml index bb8dbe89..da978fdb 100755 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -32,8 +32,7 @@ 启用该选项,以使所选应用程序的流量不经过代理 自动连接 随系统启动后台服务 - 流量统计 - 在通知栏上显示当前上下行速率 + 在通知内显示当前上下行速率 后台服务已开始运行。 影梭 → %s 无法连接远程服务器 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 564fb4eb..0524eb80 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -13,6 +13,7 @@ Use NAT mode instead of VPN mode NAT mode is disabled without root Network Traffic + Show traffic in notification Sent: \t\t\t\t\t%3$s\t↑\t%1$s/s\nReceived: \t%4$s\t↓\t%2$s/s \n%1$s↑\t%2$s↓ %1$s/s↑\t%2$s/s↓ @@ -55,8 +56,6 @@ Enable this option to bypass selected apps Auto Connect Enable Shadowsocks on startup - Traffic Stats - Show traffic stats in notification bar UDP Forwarding Forward UDP packets to remote diff --git a/src/main/res/xml/pref_all.xml b/src/main/res/xml/pref_all.xml index 2390e8e3..8ab4e23f 100644 --- a/src/main/res/xml/pref_all.xml +++ b/src/main/res/xml/pref_all.xml @@ -82,6 +82,8 @@ android:key="isAutoConnect" android:summary="@string/auto_connect_summary" android:title="@string/auto_connect"/> + update(intent.getAction) val screenFilter = new IntentFilter() screenFilter.addAction(Intent.ACTION_SCREEN_ON) @@ -49,32 +49,39 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str service.registerReceiver(lockReceiver, screenFilter) } else setVisible(visible).show() - private def update(action: String) = if (service.getState == State.CONNECTED) { + def update(action: String = Intent.ACTION_SCREEN_ON) = if (service.getState == State.CONNECTED) { if (action == Intent.ACTION_SCREEN_OFF) { if (showOnUnlock) setVisible(false) - if (showTraffic) service.binder.unregisterCallback(callback) - } else if (action == Intent.ACTION_USER_PRESENT || - action == Intent.ACTION_SCREEN_ON && !keyGuard.inKeyguardRestrictedInputMode) { - setVisible(showOnUnlock) - if (showTraffic) service.binder.registerCallback(callback) - } + unregisterCallback // unregister callback to save battery + } else if (action == Intent.ACTION_SCREEN_ON) { + if (ShadowsocksApplication.notificationTraffic) { + service.binder.registerCallback(callback) + callbackRegistered = true + } else unregisterCallback + if (!keyGuard.inKeyguardRestrictedInputMode) setVisible(showOnUnlock) + } else if (action == Intent.ACTION_USER_PRESENT) setVisible(showOnUnlock) show() } + private def unregisterCallback = if (callbackRegistered) { + service.binder.unregisterCallback(callback) + callbackRegistered = false + } + def setVisible(visible: Boolean) = { builder.setPriority(if (visible) NotificationCompat.PRIORITY_DEFAULT else NotificationCompat.PRIORITY_MIN) this } - def notification = if (showTraffic) style.build() else builder.build() + def notification = if (callbackRegistered) style.build() else builder.build() def show() = service.startForeground(1, notification) - def destroy() = { + def destroy() { if (lockReceiver != null) { service.unregisterReceiver(lockReceiver) lockReceiver = null } - if (showTraffic) service.binder.unregisterCallback(callback) + unregisterCallback service.stopForeground(true) } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala index f6e25db2..0831eee7 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala @@ -2,7 +2,8 @@ package com.github.shadowsocks import java.util.Locale -import android.content.{DialogInterface, Intent} +import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import android.content.{DialogInterface, Intent, SharedPreferences} import android.net.Uri import android.os.{Build, Bundle} import android.preference.{Preference, PreferenceFragment, SwitchPreference} @@ -11,7 +12,7 @@ import android.webkit.{WebView, WebViewClient} import com.github.shadowsocks.utils.Key // TODO: Move related logic here -class ShadowsocksSettings extends PreferenceFragment { +class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChangeListener { private lazy val activity = getActivity.asInstanceOf[Shadowsocks] private var isProxyApps: SwitchPreference = _ @@ -19,6 +20,7 @@ class ShadowsocksSettings extends PreferenceFragment { override def onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) addPreferencesFromResource(R.xml.pref_all) + getPreferenceManager.getSharedPreferences.registerOnSharedPreferenceChangeListener(this) isProxyApps = findPreference(Key.isProxyApps).asInstanceOf[SwitchPreference] isProxyApps.setOnPreferenceChangeListener((preference: Preference, newValue: Any) => { @@ -26,12 +28,6 @@ class ShadowsocksSettings extends PreferenceFragment { newValue.asInstanceOf[Boolean] // keep it ON }) - findPreference(Key.isNAT).setOnPreferenceChangeListener((preference: Preference, newValue: Any) => - if (ShadowsocksApplication.isRoot) activity.handler.post(() => { - activity.deattachService() - activity.attachService() - }) else false) - findPreference("recovery").setOnPreferenceClickListener((preference: Preference) => { ShadowsocksApplication.track(Shadowsocks.TAG, "reset") activity.recovery() @@ -75,4 +71,12 @@ class ShadowsocksSettings extends PreferenceFragment { super.onResume isProxyApps.setChecked(ShadowsocksApplication.settings.getBoolean(Key.isProxyApps, false)) // update } + + def onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) = key match { + case Key.isNAT => if (ShadowsocksApplication.isRoot) activity.handler.post(() => { + activity.deattachService() + activity.attachService() + }) + case _ => + } } diff --git a/src/main/scala/com/github/shadowsocks/utils/Constants.scala b/src/main/scala/com/github/shadowsocks/utils/Constants.scala index cfc405c9..97bf71b0 100644 --- a/src/main/scala/com/github/shadowsocks/utils/Constants.scala +++ b/src/main/scala/com/github/shadowsocks/utils/Constants.scala @@ -68,6 +68,7 @@ object Key { val profiles = "profiles" val isNAT = "isNAT" val route = "route" + val notificationTraffic = "notificationTraffic" val stat = "stat" val isRunning = "isRunning" From 1eda4c47a4abba26f4669967f2c00a1ffca1452e Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 24 Nov 2015 21:41:09 +0800 Subject: [PATCH 6/6] Removed the switch for now --- src/main/res/values-zh/strings.xml | 1 - src/main/res/values/strings.xml | 1 - src/main/res/xml/pref_all.xml | 2 -- .../com/github/shadowsocks/Shadowsocks.scala | 1 - .../shadowsocks/ShadowsocksApplication.scala | 2 -- .../shadowsocks/ShadowsocksNotification.scala | 22 ++++++++----------- .../github/shadowsocks/utils/Constants.scala | 1 - 7 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml index da978fdb..9015716b 100755 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -32,7 +32,6 @@ 启用该选项,以使所选应用程序的流量不经过代理 自动连接 随系统启动后台服务 - 在通知内显示当前上下行速率 后台服务已开始运行。 影梭 → %s 无法连接远程服务器 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 0524eb80..422a0b3f 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -13,7 +13,6 @@ Use NAT mode instead of VPN mode NAT mode is disabled without root Network Traffic - Show traffic in notification Sent: \t\t\t\t\t%3$s\t↑\t%1$s/s\nReceived: \t%4$s\t↓\t%2$s/s \n%1$s↑\t%2$s↓ %1$s/s↑\t%2$s/s↓ diff --git a/src/main/res/xml/pref_all.xml b/src/main/res/xml/pref_all.xml index 8ab4e23f..2390e8e3 100644 --- a/src/main/res/xml/pref_all.xml +++ b/src/main/res/xml/pref_all.xml @@ -82,8 +82,6 @@ android:key="isAutoConnect" android:summary="@string/auto_connect_summary" android:title="@string/auto_connect"/> - update(intent.getAction) - val screenFilter = new IntentFilter() - screenFilter.addAction(Intent.ACTION_SCREEN_ON) - screenFilter.addAction(Intent.ACTION_SCREEN_OFF) - screenFilter.addAction(Intent.ACTION_USER_PRESENT) - service.registerReceiver(lockReceiver, screenFilter) - } else setVisible(visible).show() + update() + lockReceiver = (context: Context, intent: Intent) => update(intent.getAction) + val screenFilter = new IntentFilter() + screenFilter.addAction(Intent.ACTION_SCREEN_ON) + screenFilter.addAction(Intent.ACTION_SCREEN_OFF) + if (showOnUnlock) screenFilter.addAction(Intent.ACTION_USER_PRESENT) + service.registerReceiver(lockReceiver, screenFilter) def update(action: String = Intent.ACTION_SCREEN_ON) = if (service.getState == State.CONNECTED) { if (action == Intent.ACTION_SCREEN_OFF) { if (showOnUnlock) setVisible(false) unregisterCallback // unregister callback to save battery } else if (action == Intent.ACTION_SCREEN_ON) { - if (ShadowsocksApplication.notificationTraffic) { - service.binder.registerCallback(callback) - callbackRegistered = true - } else unregisterCallback + service.binder.registerCallback(callback) + callbackRegistered = true if (!keyGuard.inKeyguardRestrictedInputMode) setVisible(showOnUnlock) } else if (action == Intent.ACTION_USER_PRESENT) setVisible(showOnUnlock) show() diff --git a/src/main/scala/com/github/shadowsocks/utils/Constants.scala b/src/main/scala/com/github/shadowsocks/utils/Constants.scala index 97bf71b0..cfc405c9 100644 --- a/src/main/scala/com/github/shadowsocks/utils/Constants.scala +++ b/src/main/scala/com/github/shadowsocks/utils/Constants.scala @@ -68,7 +68,6 @@ object Key { val profiles = "profiles" val isNAT = "isNAT" val route = "route" - val notificationTraffic = "notificationTraffic" val stat = "stat" val isRunning = "isRunning"