diff --git a/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksServiceCallback.aidl b/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksServiceCallback.aidl index d3b5d05d..ce20ee7c 100644 --- a/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksServiceCallback.aidl +++ b/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksServiceCallback.aidl @@ -2,5 +2,7 @@ package com.github.shadowsocks.aidl; interface IShadowsocksServiceCallback { oneway void stateChanged(int state, String profileName, String msg); - oneway void trafficUpdated(long txRate, long rxRate, long txTotal, long rxTotal); + oneway void trafficUpdated(int profileId, long txRate, long rxRate, long txTotal, long rxTotal); + // Traffic data has persisted to database, listener should refetch their data from database + oneway void trafficPersisted(int profileId); } diff --git a/src/main/scala/com/github/shadowsocks/BaseService.scala b/src/main/scala/com/github/shadowsocks/BaseService.scala index 285354f0..f4ccc635 100644 --- a/src/main/scala/com/github/shadowsocks/BaseService.scala +++ b/src/main/scala/com/github/shadowsocks/BaseService.scala @@ -97,7 +97,8 @@ trait BaseService extends Service { }, 1000, 1000) } TrafficMonitor.updateRate() - cb.trafficUpdated(TrafficMonitor.txRate, TrafficMonitor.rxRate, TrafficMonitor.txTotal, TrafficMonitor.rxTotal) + cb.trafficUpdated(profile.id, + TrafficMonitor.txRate, TrafficMonitor.rxRate, TrafficMonitor.txTotal, TrafficMonitor.rxTotal) } override def stopListeningForBandwidth(cb: IShadowsocksServiceCallback): Unit = @@ -246,6 +247,20 @@ trait BaseService extends Service { p.tx += tx p.rx += rx app.profileManager.updateProfile(p) + handler.post(() => { + if (bandwidthListeners.nonEmpty) { + val n = callbacks.beginBroadcast() + for (i <- 0 until n) { + try { + val item = callbacks.getBroadcastItem(i) + if (bandwidthListeners.contains(item.asBinder)) item.trafficPersisted(p.id) + } catch { + case _: Exception => // Ignore + } + } + callbacks.finishBroadcast() + } + }) case None => } } @@ -266,7 +281,8 @@ trait BaseService extends Service { for (i <- 0 until n) { try { val item = callbacks.getBroadcastItem(i) - if (bandwidthListeners.contains(item.asBinder)) item.trafficUpdated(txRate, rxRate, txTotal, rxTotal) + if (bandwidthListeners.contains(item.asBinder)) + item.trafficUpdated(profile.id, txRate, rxRate, txTotal, rxTotal) } catch { case _: Exception => // Ignore } diff --git a/src/main/scala/com/github/shadowsocks/MainActivity.scala b/src/main/scala/com/github/shadowsocks/MainActivity.scala index 723862a1..76222fe1 100644 --- a/src/main/scala/com/github/shadowsocks/MainActivity.scala +++ b/src/main/scala/com/github/shadowsocks/MainActivity.scala @@ -97,8 +97,10 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe var state: Int = _ private val callback = new IShadowsocksServiceCallback.Stub { def stateChanged(s: Int, profileName: String, m: String): Unit = handler.post(() => changeState(s, profileName, m)) - def trafficUpdated(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long): Unit = - handler.post(() => updateTraffic(txRate, rxRate, txTotal, rxTotal)) + def trafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long): Unit = + handler.post(() => updateTraffic(profileId, txRate, rxRate, txTotal, rxTotal)) + override def trafficPersisted(profileId: Int): Unit = handler.post(() => if (ProfilesFragment.instance != null) + ProfilesFragment.instance.onTrafficPersisted(profileId)) } private lazy val greyTint = ContextCompat.getColorStateList(MainActivity.this, R.color.material_primary_500) @@ -136,25 +138,22 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe state = s if (state == State.CONNECTED) fab.setBackgroundTintList(greenTint) else { fab.setBackgroundTintList(greyTint) - updateTraffic(0, 0, 0, 0) + updateTraffic(-1, 0, 0, 0, 0) testCount += 1 // suppress previous test messages } - if (ProfilesFragment.instance != null) { - val adapter = ProfilesFragment.instance.profilesAdapter - adapter.notifyDataSetChanged() // refresh button enabled state - if (state == State.STOPPED) adapter.refreshId(app.profileId) // refresh bandwidth statistics - } + if (ProfilesFragment.instance != null) + ProfilesFragment.instance.profilesAdapter.notifyDataSetChanged() // refresh button enabled state fab.setEnabled(false) if (state == State.CONNECTED || state == State.STOPPED) handler.postDelayed(() => fab.setEnabled(state == State.CONNECTED || state == State.STOPPED), 1000) } - def updateTraffic(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { + def updateTraffic(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { txText.setText(TrafficMonitor.formatTraffic(txTotal)) rxText.setText(TrafficMonitor.formatTraffic(rxTotal)) txRateText.setText(TrafficMonitor.formatTraffic(txRate) + "/s") rxRateText.setText(TrafficMonitor.formatTraffic(rxRate) + "/s") val child = getFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment] - if (state != State.STOPPING && child != null) child.onTrafficUpdated(txRate, rxRate, txTotal, rxTotal) + if (state != State.STOPPING && child != null) child.onTrafficUpdated(profileId, txRate, rxRate, txTotal, rxTotal) } override def onServiceConnected() { diff --git a/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala b/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala index 9b3953fc..8136a25b 100644 --- a/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala +++ b/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala @@ -101,7 +101,7 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe def saveAndExit() { profile.deserialize(app.settings) app.profileManager.updateProfile(profile) - if (ProfilesFragment.instance != null) ProfilesFragment.instance.profilesAdapter.refreshId(profile.id) + if (ProfilesFragment.instance != null) ProfilesFragment.instance.profilesAdapter.deepRefreshId(profile.id) getActivity.finish() } } diff --git a/src/main/scala/com/github/shadowsocks/ProfilesFragment.scala b/src/main/scala/com/github/shadowsocks/ProfilesFragment.scala index 0bd31cee..87c467eb 100644 --- a/src/main/scala/com/github/shadowsocks/ProfilesFragment.scala +++ b/src/main/scala/com/github/shadowsocks/ProfilesFragment.scala @@ -106,7 +106,7 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic edit.setAlpha(if (editable) 1 else 0.5F) var tx = item.tx var rx = item.rx - if (item.id == app.profileId) { + if (item.id == bandwidthProfile) { tx += txTotal rx += rxTotal } @@ -222,6 +222,10 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic for ((_, item) <- actions) app.profileManager.delProfile(item.id) def refreshId(id: Int) { + val index = profiles.indexWhere(_.id == id) + if (index >= 0) notifyItemChanged(index) + } + def deepRefreshId(id: Int) { val index = profiles.indexWhere(_.id == id) if (index >= 0) { profiles(index) = app.profileManager.getProfile(id).get @@ -242,6 +246,7 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic lazy val profilesAdapter = new ProfilesAdapter private var undoManager: UndoSnackbarManager[Profile] = _ + private var bandwidthProfile: Int = _ private var txTotal: Long = _ private var rxTotal: Long = _ @@ -292,10 +297,24 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic }).attachToRecyclerView(profilesList) } - override def onTrafficUpdated(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long): Unit = { - this.txTotal = txTotal - this.rxTotal = rxTotal - profilesAdapter.refreshId(app.profileId) + override def onTrafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long): Unit = + if (profileId != -1) { // ignore resets from MainActivity + if (bandwidthProfile != profileId) { + onTrafficPersisted(bandwidthProfile) + bandwidthProfile = profileId + } + this.txTotal = txTotal + this.rxTotal = rxTotal + profilesAdapter.refreshId(profileId) + } + def onTrafficPersisted(profileId: Int) { + txTotal = 0 + rxTotal = 0 + if (bandwidthProfile != profileId) { + onTrafficPersisted(bandwidthProfile) + bandwidthProfile = profileId + } + profilesAdapter.deepRefreshId(profileId) } override def onDetach() { diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala index 5cefdbe7..36b248db 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala @@ -39,7 +39,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str private lazy val nm = service.getSystemService(Context.NOTIFICATION_SERVICE).asInstanceOf[NotificationManager] private lazy val callback = new Stub { override def stateChanged(state: Int, profileName: String, msg: String): Unit = () // ignore - override def trafficUpdated(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { + override def trafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { val txr = TrafficMonitor.formatTraffic(txRate) val rxr = TrafficMonitor.formatTraffic(rxRate) builder.setContentText(service.getString(R.string.traffic_summary).formatLocal(Locale.ENGLISH, txr, rxr)) @@ -47,6 +47,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str TrafficMonitor.formatTraffic(txTotal), TrafficMonitor.formatTraffic(rxTotal))) show() } + override def trafficPersisted(profileId: Int): Unit = () } private var lockReceiver: BroadcastReceiver = _ private var callbackRegistered: Boolean = _ diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksTileService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksTileService.scala index 5894cbce..81c9c9dd 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksTileService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksTileService.scala @@ -36,7 +36,7 @@ final class ShadowsocksTileService extends TileService with ServiceBoundContext private lazy val iconBusy = Icon.createWithResource(this, R.drawable.ic_start_busy) private lazy val iconConnected = Icon.createWithResource(this, R.drawable.ic_start_connected) private lazy val callback = new IShadowsocksServiceCallback.Stub { - def trafficUpdated(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long): Unit = () + def trafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long): Unit = () def stateChanged(state: Int, profileName: String, msg: String) { val tile = getQsTile if (tile != null) { @@ -57,6 +57,7 @@ final class ShadowsocksTileService extends TileService with ServiceBoundContext tile.updateTile() } } + override def trafficPersisted(profileId: Int): Unit = () } override def onServiceConnected(): Unit = callback.stateChanged(bgService.getState, bgService.getProfileName, null) diff --git a/src/main/scala/com/github/shadowsocks/ToolbarFragment.scala b/src/main/scala/com/github/shadowsocks/ToolbarFragment.scala index 5810aefd..461877fa 100644 --- a/src/main/scala/com/github/shadowsocks/ToolbarFragment.scala +++ b/src/main/scala/com/github/shadowsocks/ToolbarFragment.scala @@ -35,5 +35,5 @@ class ToolbarFragment extends Fragment { if (activity.crossfader == null) activity.drawer.setToolbar(activity, toolbar, true) } - def onTrafficUpdated(txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long): Unit = () + def onTrafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long): Unit = () }