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()