Commit 407ba8be authored by Mygod's avatar Mygod

Add realtime traffic in profiles selector

parent c0829689
...@@ -45,8 +45,6 @@ import android.app.Notification ...@@ -45,8 +45,6 @@ import android.app.Notification
import android.content.Context import android.content.Context
import android.os.{Handler, RemoteCallbackList} import android.os.{Handler, RemoteCallbackList}
import android.util.Log import android.util.Log
import com.github.shadowsocks.database.{Profile, ProfileManager}
import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback} import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback}
import com.github.shadowsocks.utils.{State, TrafficMonitor, TrafficMonitorThread} import com.github.shadowsocks.utils.{State, TrafficMonitor, TrafficMonitorThread}
...@@ -135,12 +133,11 @@ trait BaseService { ...@@ -135,12 +133,11 @@ trait BaseService {
handler.post(() => { handler.post(() => {
if (config != null) { if (config != null) {
ShadowsocksApplication.profileManager.getProfile(config.profileId) match { ShadowsocksApplication.profileManager.getProfile(config.profileId) match {
case Some(profile) => { case Some(profile) =>
profile.tx += tx; profile.tx += tx
profile.rx += rx; profile.rx += rx
Log.d("test", profile.tx + " " + profile.rx) Log.d("test", profile.tx + " " + profile.rx)
ShadowsocksApplication.profileManager.updateProfile(profile) ShadowsocksApplication.profileManager.updateProfile(profile)
}
case None => // Ignore case None => // Ignore
} }
} }
......
...@@ -14,6 +14,7 @@ import android.text.{SpannableStringBuilder, Spanned} ...@@ -14,6 +14,7 @@ import android.text.{SpannableStringBuilder, Spanned}
import android.view.View.{OnAttachStateChangeListener, OnClickListener} import android.view.View.{OnAttachStateChangeListener, OnClickListener}
import android.view.{LayoutInflater, MenuItem, View, ViewGroup} import android.view.{LayoutInflater, MenuItem, View, ViewGroup}
import android.widget.{CheckedTextView, ImageView, LinearLayout, Toast} import android.widget.{CheckedTextView, ImageView, LinearLayout, Toast}
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.utils.{Parser, TrafficMonitor, Utils} import com.github.shadowsocks.utils.{Parser, TrafficMonitor, Utils}
import com.google.zxing.integration.android.IntentIntegrator import com.google.zxing.integration.android.IntentIntegrator
...@@ -24,7 +25,7 @@ import scala.collection.mutable.ArrayBuffer ...@@ -24,7 +25,7 @@ import scala.collection.mutable.ArrayBuffer
/** /**
* @author Mygod * @author Mygod
*/ */
class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListener { class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListener with ServiceBoundContext {
private class ProfileViewHolder(val view: View) extends RecyclerView.ViewHolder(view) with View.OnClickListener { private class ProfileViewHolder(val view: View) extends RecyclerView.ViewHolder(view) with View.OnClickListener {
private var item: Profile = _ private var item: Profile = _
private val text = itemView.findViewById(android.R.id.text1).asInstanceOf[CheckedTextView] private val text = itemView.findViewById(android.R.id.text1).asInstanceOf[CheckedTextView]
...@@ -54,8 +55,12 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe ...@@ -54,8 +55,12 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
}) })
} }
def updateText() { def updateText(refetch: Boolean = false) {
val builder = new SpannableStringBuilder val builder = new SpannableStringBuilder
val item = if (refetch) ShadowsocksApplication.profileManager.getProfile(this.item.id) match {
case Some(profile) => profile
case None => return
} else this.item
builder.append(item.name) builder.append(item.name)
if (item.tx != 0 || item.rx != 0) { if (item.tx != 0 || item.rx != 0) {
val start = builder.length val start = builder.length
...@@ -70,7 +75,10 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe ...@@ -70,7 +75,10 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
def bind(item: Profile) { def bind(item: Profile) {
this.item = item this.item = item
updateText() updateText()
text.setChecked(item.id == ShadowsocksApplication.profileId) if (item.id == ShadowsocksApplication.profileId) {
text.setChecked(true)
selectedItem = this
}
} }
def onClick(v: View) = { def onClick(v: View) = {
...@@ -120,6 +128,8 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe ...@@ -120,6 +128,8 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
} }
} }
private var selectedItem: ProfileViewHolder = _
private lazy val profilesAdapter = new ProfilesAdapter private lazy val profilesAdapter = new ProfilesAdapter
private var removedSnackbar: Snackbar = _ private var removedSnackbar: Snackbar = _
...@@ -155,9 +165,17 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe ...@@ -155,9 +165,17 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
} }
def onMove(recyclerView: RecyclerView, viewHolder: ViewHolder, target: ViewHolder) = false // TODO? def onMove(recyclerView: RecyclerView, viewHolder: ViewHolder, target: ViewHolder) = false // TODO?
}).attachToRecyclerView(profilesList) }).attachToRecyclerView(profilesList)
attachService(new IShadowsocksServiceCallback.Stub {
def stateChanged(state: Int, msg: String) = () // ignore
def trafficUpdated(txRate: String, rxRate: String, txTotal: String, rxTotal: String) {
if (selectedItem != null) selectedItem.updateText(true)
}
})
} }
override def onDestroy { override def onDestroy {
deattachService()
super.onDestroy super.onDestroy
ShadowsocksApplication.profileManager.setProfileAddedListener(null) ShadowsocksApplication.profileManager.setProfileAddedListener(null)
profilesAdapter.commitRemoves profilesAdapter.commitRemoves
......
package com.github.shadowsocks
import android.content.{ComponentName, Context, Intent, ServiceConnection}
import android.os.{RemoteException, IBinder}
import com.github.shadowsocks.aidl.{IShadowsocksServiceCallback, IShadowsocksService}
import com.github.shadowsocks.utils.Action
/**
* @author Mygod
*/
trait ServiceBoundContext extends Context {
val connection = new ServiceConnection {
override def onServiceConnected(name: ComponentName, service: IBinder) {
bgService = IShadowsocksService.Stub.asInterface(service)
if (callback != null) try {
bgService.registerCallback(callback)
} catch {
case ignored: RemoteException => // Nothing
}
ServiceBoundContext.this.onServiceConnected()
}
override def onServiceDisconnected(name: ComponentName) {
if (callback != null) {
try {
if (bgService != null) bgService.unregisterCallback(callback)
} catch {
case ignored: RemoteException => // Nothing
}
callback = null
}
ServiceBoundContext.this.onServiceDisconnected()
bgService = null
}
}
def onServiceConnected() = ()
def onServiceDisconnected() = ()
private var callback: IShadowsocksServiceCallback.Stub = _
// Variables
var bgService: IShadowsocksService = _
def attachService(callback: IShadowsocksServiceCallback.Stub = null) {
this.callback = callback
if (bgService == null) {
val s =
if (ShadowsocksApplication.isVpnEnabled) classOf[ShadowsocksVpnService] else classOf[ShadowsocksNatService]
val intent = new Intent(this, s)
intent.setAction(Action.SERVICE)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
startService(new Intent(this, s))
}
}
def deattachService() {
if (bgService != null) {
unbindService(connection)
bgService = null
}
}
}
...@@ -58,7 +58,7 @@ import android.util.Log ...@@ -58,7 +58,7 @@ import android.util.Log
import android.view.{View, ViewGroup, ViewParent} import android.view.{View, ViewGroup, ViewParent}
import android.widget._ import android.widget._
import com.github.jorgecastilloprz.FABProgressCircle import com.github.jorgecastilloprz.FABProgressCircle
import com.github.shadowsocks.aidl.{IShadowsocksService, IShadowsocksServiceCallback} import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import com.github.shadowsocks.database._ import com.github.shadowsocks.database._
import com.github.shadowsocks.preferences.{DropDownPreference, NumberPickerPreference, PasswordEditTextPreference, SummaryEditTextPreference} import com.github.shadowsocks.preferences.{DropDownPreference, NumberPickerPreference, PasswordEditTextPreference, SummaryEditTextPreference}
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
...@@ -111,7 +111,7 @@ object Shadowsocks { ...@@ -111,7 +111,7 @@ object Shadowsocks {
pref.asInstanceOf[PasswordEditTextPreference].setText(value) pref.asInstanceOf[PasswordEditTextPreference].setText(value)
} }
def updateNumberPickerPreference(pref: Preference, value: Int): Unit = { def updateNumberPickerPreference(pref: Preference, value: Int) {
pref.asInstanceOf[NumberPickerPreference].setValue(value) pref.asInstanceOf[NumberPickerPreference].setValue(value)
} }
...@@ -142,7 +142,7 @@ object Shadowsocks { ...@@ -142,7 +142,7 @@ object Shadowsocks {
} }
class Shadowsocks class Shadowsocks
extends AppCompatActivity { extends AppCompatActivity with ServiceBoundContext {
// Variables // Variables
var serviceStarted = false var serviceStarted = false
...@@ -157,47 +157,28 @@ class Shadowsocks ...@@ -157,47 +157,28 @@ class Shadowsocks
// Services // Services
var currentServiceName = classOf[ShadowsocksNatService].getName var currentServiceName = classOf[ShadowsocksNatService].getName
var bgService: IShadowsocksService = null
val callback = new IShadowsocksServiceCallback.Stub { override def onServiceConnected() {
override def stateChanged(state: Int, msg: String) { // Update the UI
onStateChanged(state, msg) if (fab != null) fab.setEnabled(true)
} stateUpdate()
override def trafficUpdated(txRate: String, rxRate: String, txTotal: String, rxTotal: String) {
val trafficStat = getString(R.string.stat_summary) if (!ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)) {
.formatLocal(Locale.ENGLISH, txRate, rxRate, txTotal, rxTotal) ShadowsocksApplication.settings.edit.putBoolean(ShadowsocksApplication.getVersionName, true).apply()
handler.post(() => { recovery()
preferences.findPreference(Key.stat).setSummary(trafficStat)
})
} }
} }
val connection = new ServiceConnection {
override def onServiceConnected(name: ComponentName, service: IBinder) {
// Initialize the background service
bgService = IShadowsocksService.Stub.asInterface(service)
try {
bgService.registerCallback(callback)
} catch {
case ignored: RemoteException => // Nothing
}
// Update the UI
if (fab != null) fab.setEnabled(true)
stateUpdate()
if (!ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)) { override def onServiceDisconnected() {
ShadowsocksApplication.settings.edit.putBoolean(ShadowsocksApplication.getVersionName, true).apply() if (fab != null) fab.setEnabled(false)
recovery() }
}
}
override def onServiceDisconnected(name: ComponentName) { def trafficUpdated(txRate: String, rxRate: String, txTotal: String, rxTotal: String) {
if (fab != null) fab.setEnabled(false) val trafficStat = getString(R.string.stat_summary)
try { .formatLocal(Locale.ENGLISH, txRate, rxRate, txTotal, rxTotal)
if (bgService != null) bgService.unregisterCallback(callback) handler.post(() => {
} catch { preferences.findPreference(Key.stat).setSummary(trafficStat)
case ignored: RemoteException => // Nothing })
}
bgService = null
}
} }
private lazy val preferences = private lazy val preferences =
...@@ -388,33 +369,17 @@ class Shadowsocks ...@@ -388,33 +369,17 @@ class Shadowsocks
// Bind to the service // Bind to the service
handler.post(() => { handler.post(() => {
attachService() 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)
}
})
}) })
} }
def attachService() {
if (bgService == null) {
val s = if (ShadowsocksApplication.isVpnEnabled) classOf[ShadowsocksVpnService]
else classOf[ShadowsocksNatService]
val intent = new Intent(this, s)
intent.setAction(Action.SERVICE)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
startService(new Intent(this, s))
}
}
def deattachService() {
if (bgService != null) {
try {
bgService.unregisterCallback(callback)
} catch {
case ignored: RemoteException => // Nothing
}
bgService = null
unbindService(connection)
}
}
def reloadProfile() { def reloadProfile() {
currentProfile = ShadowsocksApplication.currentProfile match { currentProfile = ShadowsocksApplication.currentProfile match {
case Some(profile) => profile // updated case Some(profile) => profile // updated
...@@ -467,7 +432,7 @@ class Shadowsocks ...@@ -467,7 +432,7 @@ class Shadowsocks
// Check if current profile changed // Check if current profile changed
if (ShadowsocksApplication.profileId != currentProfile.id) reloadProfile() if (ShadowsocksApplication.profileId != currentProfile.id) reloadProfile()
callback.trafficUpdated(TrafficMonitor.getTxRate, TrafficMonitor.getRxRate, trafficUpdated(TrafficMonitor.getTxRate, TrafficMonitor.getRxRate,
TrafficMonitor.getTxTotal, TrafficMonitor.getRxTotal) TrafficMonitor.getTxTotal, TrafficMonitor.getRxTotal)
} }
......
...@@ -95,7 +95,7 @@ class ShadowsocksApplication extends Application { ...@@ -95,7 +95,7 @@ class ShadowsocksApplication extends Application {
val tm = TagManager.getInstance(this) val tm = TagManager.getInstance(this)
val pending = tm.loadContainerPreferNonDefault("GTM-NT8WS8", R.raw.gtm_default_container) val pending = tm.loadContainerPreferNonDefault("GTM-NT8WS8", R.raw.gtm_default_container)
val callback = new ResultCallback[ContainerHolder] { val callback = new ResultCallback[ContainerHolder] {
override def onResult(holder: ContainerHolder): Unit = { override def onResult(holder: ContainerHolder) {
if (!holder.getStatus.isSuccess) { if (!holder.getStatus.isSuccess) {
return return
} }
......
...@@ -40,30 +40,22 @@ ...@@ -40,30 +40,22 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.app.{Activity, KeyguardManager} import android.app.{Activity, KeyguardManager}
import android.content._ import android.content.{Intent, IntentFilter, Context, BroadcastReceiver}
import android.net.VpnService import android.net.VpnService
import android.os._ import android.os.{Bundle, Handler}
import android.support.v7.app.AppCompatActivity
import android.util.Log import android.util.Log
import com.github.shadowsocks.aidl.IShadowsocksService import com.github.shadowsocks.utils.ConfigUtils
import com.github.shadowsocks.utils._
class ShadowsocksRunnerActivity extends Activity {
class ShadowsocksRunnerActivity extends AppCompatActivity with ServiceBoundContext {
val handler = new Handler() val handler = new Handler()
val connection = new ServiceConnection {
override def onServiceConnected(name: ComponentName, service: IBinder) {
bgService = IShadowsocksService.Stub.asInterface(service)
handler.postDelayed(() => if (bgService != null) startBackgroundService(), 1000)
}
override def onServiceDisconnected(name: ComponentName) {
bgService = null
}
}
// Variables // Variables
var bgService: IShadowsocksService = _ var receiver: BroadcastReceiver = _
var receiver:BroadcastReceiver = _
override def onServiceConnected() {
handler.postDelayed(() => if (bgService != null) startBackgroundService(), 1000)
}
def startBackgroundService() { def startBackgroundService() {
if (ShadowsocksApplication.isVpnEnabled) { if (ShadowsocksApplication.isVpnEnabled) {
...@@ -79,24 +71,6 @@ class ShadowsocksRunnerActivity extends Activity { ...@@ -79,24 +71,6 @@ class ShadowsocksRunnerActivity extends Activity {
} }
} }
def attachService() {
if (bgService == null) {
val s = if (ShadowsocksApplication.isVpnEnabled) classOf[ShadowsocksVpnService]
else classOf[ShadowsocksNatService]
val intent = new Intent(this, s)
intent.setAction(Action.SERVICE)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
startService(new Intent(this, s))
}
}
def deattachService() {
if (bgService != null) {
unbindService(connection)
bgService = null
}
}
override def onCreate(savedInstanceState: Bundle) { override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val km = getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager] val km = getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager]
......
...@@ -40,31 +40,22 @@ ...@@ -40,31 +40,22 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.app.Service import android.app.Service
import android.content._ import android.content.Intent
import android.net.VpnService import android.net.VpnService
import android.os._ import android.os.{IBinder, Handler}
import com.github.shadowsocks.aidl.IShadowsocksService import com.github.shadowsocks.utils.ConfigUtils
import com.github.shadowsocks.utils._
class ShadowsocksRunnerService extends Service { class ShadowsocksRunnerService extends Service with ServiceBoundContext {
val handler = new Handler() val handler = new Handler()
val connection = new ServiceConnection {
override def onServiceConnected(name: ComponentName, service: IBinder) {
bgService = IShadowsocksService.Stub.asInterface(service)
handler.postDelayed(() => if (bgService != null) startBackgroundService(), 1000)
}
override def onServiceDisconnected(name: ComponentName) {
bgService = null
}
}
// Variables
var bgService: IShadowsocksService = _
override def onBind(intent: Intent): IBinder = { override def onBind(intent: Intent): IBinder = {
null null
} }
override def onServiceConnected() {
handler.postDelayed(() => if (bgService != null) startBackgroundService(), 1000)
}
def startBackgroundService() { def startBackgroundService() {
if (ShadowsocksApplication.isVpnEnabled) { if (ShadowsocksApplication.isVpnEnabled) {
val intent = VpnService.prepare(ShadowsocksRunnerService.this) val intent = VpnService.prepare(ShadowsocksRunnerService.this)
...@@ -79,23 +70,6 @@ class ShadowsocksRunnerService extends Service { ...@@ -79,23 +70,6 @@ class ShadowsocksRunnerService extends Service {
stopSelf() stopSelf()
} }
def attachService() {
if (bgService == null) {
val s = if (!ShadowsocksApplication.isVpnEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService]
val intent = new Intent(this, s)
intent.setAction(Action.SERVICE)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
startService(new Intent(this, s))
}
}
def deattachService() {
if (bgService != null) {
unbindService(connection)
bgService = null
}
}
override def onCreate() { override def onCreate() {
super.onCreate() super.onCreate()
attachService() attachService()
......
...@@ -69,7 +69,7 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread { ...@@ -69,7 +69,7 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
closeServerSocket() closeServerSocket()
} }
override def run(): Unit = { override def run() {
try { try {
new File(PATH).delete() new File(PATH).delete()
......
...@@ -10,15 +10,15 @@ case class Traffic(tx: Long, rx: Long, timestamp: Long) ...@@ -10,15 +10,15 @@ case class Traffic(tx: Long, rx: Long, timestamp: Long)
object TrafficMonitor { object TrafficMonitor {
var last: Traffic = getTraffic(0, 0) var last: Traffic = getTraffic(0, 0)
// Kilo bytes per second // Bytes per second
var txRate: Long = 0 var txRate: Long = 0
var rxRate: Long = 0 var rxRate: Long = 0
// Kilo bytes for the current session // Bytes for the current session
var txTotal: Long = 0 var txTotal: Long = 0
var rxTotal: Long = 0 var rxTotal: Long = 0
// Kilo bytes for the last query // Bytes for the last query
var txLast: Long = 0 var txLast: Long = 0
var rxLast: Long = 0 var rxLast: Long = 0
...@@ -42,14 +42,14 @@ object TrafficMonitor { ...@@ -42,14 +42,14 @@ object TrafficMonitor {
def update(tx: Long, rx: Long) { def update(tx: Long, rx: Long) {
val now = getTraffic(tx, rx) val now = getTraffic(tx, rx)
val delta = now.timestamp - last.timestamp val delta = now.timestamp - last.timestamp
if (delta != 0) {
txRate = (now.tx - last.tx) * 1000 / delta
rxRate = (now.rx - last.rx) * 1000 / delta
}
txLast = now.tx - last.tx txLast = now.tx - last.tx
rxLast = now.rx - last.rx rxLast = now.rx - last.rx
txTotal += now.tx - last.tx if (delta != 0) {
rxTotal += now.rx - last.rx txRate = txLast * 1000 / delta
rxRate = rxLast * 1000 / delta
}
txTotal += txLast
rxTotal += rxLast
last = now last = now
} }
......
...@@ -70,7 +70,7 @@ class TrafficMonitorThread(service: BaseService) extends Thread { ...@@ -70,7 +70,7 @@ class TrafficMonitorThread(service: BaseService) extends Thread {
closeServerSocket() closeServerSocket()
} }
override def run(): Unit = { override def run() {
try { try {
new File(PATH).delete() new File(PATH).delete()
...@@ -108,9 +108,9 @@ class TrafficMonitorThread(service: BaseService) extends Thread { ...@@ -108,9 +108,9 @@ class TrafficMonitorThread(service: BaseService) extends Thread {
val stat = new String(array, "UTF-8").split("\\|") val stat = new String(array, "UTF-8").split("\\|")
if (stat.length == 2) { if (stat.length == 2) {
TrafficMonitor.update(stat(0).toLong, stat(1).toLong) TrafficMonitor.update(stat(0).toLong, stat(1).toLong)
service.updateTrafficTotal(TrafficMonitor.txLast, TrafficMonitor.rxLast)
service.updateTrafficRate(TrafficMonitor.getTxRate, TrafficMonitor.getRxRate, service.updateTrafficRate(TrafficMonitor.getTxRate, TrafficMonitor.getRxRate,
TrafficMonitor.getTxTotal, TrafficMonitor.getRxTotal) TrafficMonitor.getTxTotal, TrafficMonitor.getRxTotal)
service.updateTrafficTotal(TrafficMonitor.txLast, TrafficMonitor.rxLast)
} }
output.write(0) output.write(0)
......
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