Refactor using ViewModel

This commit is contained in:
Mygod
2018-12-21 14:53:12 +08:00
parent e9421fd24f
commit f1db0f2c8f
6 changed files with 34 additions and 65 deletions

View File

@@ -5,6 +5,7 @@ apply plugin: 'com.github.ben-manes.versions'
buildscript { buildscript {
ext { ext {
kotlinVersion = '1.3.11' kotlinVersion = '1.3.11'
lifecycleVersion = '2.0.0'
roomVersion = '2.0.0' roomVersion = '2.0.0'
} }
repositories { repositories {

View File

@@ -58,11 +58,14 @@ android {
} }
dependencies { dependencies {
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycleVersion"
kapt "androidx.room:room-compiler:$roomVersion" kapt "androidx.room:room-compiler:$roomVersion"
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "androidx.browser:browser:1.0.0" implementation "androidx.browser:browser:1.0.0"
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
implementation "androidx.core:core-ktx:1.0.1" implementation "androidx.core:core-ktx:1.0.1"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
implementation "androidx.preference:preference:1.0.0" implementation "androidx.preference:preference:1.0.0"
implementation "androidx.room:room-runtime:$roomVersion" implementation "androidx.room:room-runtime:$roomVersion"
implementation 'com.github.luongvo:BadgeView:1.1.5' implementation 'com.github.luongvo:BadgeView:1.1.5'
@@ -74,6 +77,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
baseImplementation 'com.android.billingclient:billing:1.2' baseImplementation 'com.android.billingclient:billing:1.2'
baseImplementation 'com.crashlytics.sdk.android:crashlytics:2.9.7' baseImplementation 'com.crashlytics.sdk.android:crashlytics:2.9.7'
testImplementation "androidx.arch.core:core-testing:$lifecycleVersion"
testImplementation "androidx.room:room-testing:$roomVersion" testImplementation "androidx.room:room-testing:$roomVersion"
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:runner:1.1.1'

View File

@@ -82,7 +82,6 @@
<action android:name="android.service.quicksettings.action.QS_TILE" /> <action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter> </intent-filter>
</service> </service>
<service android:name=".client.ClientMonitorService"/>
<receiver <receiver
android:name=".BootReceiver" android:name=".BootReceiver"

View File

@@ -1,12 +1,9 @@
package be.mygod.vpnhotspot package be.mygod.vpnhotspot
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.ServiceConnection
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.IBinder
import android.view.Gravity import android.view.Gravity
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -14,7 +11,11 @@ import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import be.mygod.vpnhotspot.client.ClientMonitorService import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.lifecycle.get
import be.mygod.vpnhotspot.client.Client
import be.mygod.vpnhotspot.client.ClientViewModel
import be.mygod.vpnhotspot.client.ClientsFragment import be.mygod.vpnhotspot.client.ClientsFragment
import be.mygod.vpnhotspot.databinding.ActivityMainBinding import be.mygod.vpnhotspot.databinding.ActivityMainBinding
import be.mygod.vpnhotspot.manage.TetheringFragment import be.mygod.vpnhotspot.manage.TetheringFragment
@@ -25,10 +26,9 @@ import com.google.android.material.bottomnavigation.BottomNavigationView
import q.rorbin.badgeview.QBadgeView import q.rorbin.badgeview.QBadgeView
import timber.log.Timber import timber.log.Timber
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener, ServiceConnection { class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
private lateinit var badge: QBadgeView private lateinit var badge: QBadgeView
private var clients: ClientMonitorService.Binder? = null
private val customTabsIntent by lazy { private val customTabsIntent by lazy {
CustomTabsIntent.Builder() CustomTabsIntent.Builder()
.setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary)) .setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary))
@@ -56,7 +56,9 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
badge.badgeTextColor = ContextCompat.getColor(this, R.color.primary_text_default_material_light) badge.badgeTextColor = ContextCompat.getColor(this, R.color.primary_text_default_material_light)
badge.badgeGravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL badge.badgeGravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
badge.setGravityOffset(16f, 0f, true) badge.setGravityOffset(16f, 0f, true)
ServiceForegroundConnector(this, this, ClientMonitorService::class) val model = ViewModelProviders.of(this).get<ClientViewModel>()
if (RepeaterService.supported) ServiceForegroundConnector(this, model, RepeaterService::class)
model.clients.observe(this, Observer<List<Client>> { badge.badgeNumber = it.size })
SmartSnackbar.Register(lifecycle, binding.fragmentHolder) SmartSnackbar.Register(lifecycle, binding.fragmentHolder)
} }
@@ -85,17 +87,6 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
else -> false else -> false
} }
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
clients = service as ClientMonitorService.Binder
service.clientsChanged[this] = { badge.badgeNumber = it.size }
}
override fun onServiceDisconnected(name: ComponentName?) {
val clients = clients ?: return
this.clients = null
clients.clientsChanged -= this
}
private fun displayFragment(fragment: Fragment) = private fun displayFragment(fragment: Fragment) =
supportFragmentManager.beginTransaction().replace(R.id.fragmentHolder, fragment).commitAllowingStateLoss() supportFragmentManager.beginTransaction().replace(R.id.fragmentHolder, fragment).commitAllowingStateLoss()

View File

@@ -1,24 +1,20 @@
package be.mygod.vpnhotspot.client package be.mygod.vpnhotspot.client
import android.app.Service import android.content.ComponentName
import android.content.* import android.content.IntentFilter
import android.content.ServiceConnection
import android.net.wifi.p2p.WifiP2pDevice import android.net.wifi.p2p.WifiP2pDevice
import android.os.IBinder import android.os.IBinder
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.RepeaterService import be.mygod.vpnhotspot.RepeaterService
import be.mygod.vpnhotspot.net.IpNeighbour import be.mygod.vpnhotspot.net.IpNeighbour
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.TetheringManager
import be.mygod.vpnhotspot.util.StickyEvent1 import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
import be.mygod.vpnhotspot.util.broadcastReceiver import be.mygod.vpnhotspot.util.broadcastReceiver
import be.mygod.vpnhotspot.util.stopAndUnbind
class ClientMonitorService : Service(), ServiceConnection, IpNeighbourMonitor.Callback {
inner class Binder : android.os.Binder() {
val clientsChanged = StickyEvent1 { clients }
}
private val binder = Binder()
override fun onBind(intent: Intent?) = binder
class ClientViewModel : ViewModel(), ServiceConnection, IpNeighbourMonitor.Callback {
private var tetheredInterfaces = emptySet<String>() private var tetheredInterfaces = emptySet<String>()
private val receiver = broadcastReceiver { _, intent -> private val receiver = broadcastReceiver { _, intent ->
val extras = intent.extras ?: return@broadcastReceiver val extras = intent.extras ?: return@broadcastReceiver
@@ -30,11 +26,7 @@ class ClientMonitorService : Service(), ServiceConnection, IpNeighbourMonitor.Ca
private var repeater: RepeaterService.Binder? = null private var repeater: RepeaterService.Binder? = null
private var p2p: Collection<WifiP2pDevice> = emptyList() private var p2p: Collection<WifiP2pDevice> = emptyList()
private var neighbours = emptyList<IpNeighbour>() private var neighbours = emptyList<IpNeighbour>()
private var clients = emptyList<Client>() val clients = MutableLiveData<List<Client>>()
private set(value) {
field = value
binder.clientsChanged(value)
}
private fun populateClients() { private fun populateClients() {
val clients = HashMap<Pair<String, String>, Client>() val clients = HashMap<Pair<String, String>, Client>()
@@ -53,7 +45,7 @@ class ClientMonitorService : Service(), ServiceConnection, IpNeighbourMonitor.Ca
} }
client.ip += Pair(neighbour.ip, neighbour.state) client.ip += Pair(neighbour.ip, neighbour.state)
} }
this.clients = clients.values.sortedWith(compareBy<Client> { it.iface }.thenBy { it.mac }) this.clients.postValue(clients.values.sortedWith(compareBy<Client> { it.iface }.thenBy { it.mac }))
} }
private fun refreshP2p() { private fun refreshP2p() {
@@ -62,20 +54,14 @@ class ClientMonitorService : Service(), ServiceConnection, IpNeighbourMonitor.Ca
populateClients() populateClients()
} }
override fun onCreate() { init {
super.onCreate() app.registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
if (RepeaterService.supported) {
bindService(Intent(this, RepeaterService::class.java), this, Context.BIND_AUTO_CREATE)
}
IpNeighbourMonitor.registerCallback(this) IpNeighbourMonitor.registerCallback(this)
registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
} }
override fun onDestroy() { override fun onCleared() {
unregisterReceiver(receiver)
IpNeighbourMonitor.unregisterCallback(this) IpNeighbourMonitor.unregisterCallback(this)
if (RepeaterService.supported) stopAndUnbind(this) app.unregisterReceiver(receiver)
super.onDestroy()
} }
override fun onServiceConnected(name: ComponentName?, service: IBinder?) { override fun onServiceConnected(name: ComponentName?, service: IBinder?) {

View File

@@ -1,10 +1,7 @@
package be.mygod.vpnhotspot.client package be.mygod.vpnhotspot.client
import android.content.ComponentName
import android.content.DialogInterface import android.content.DialogInterface
import android.content.ServiceConnection
import android.os.Bundle import android.os.Bundle
import android.os.IBinder
import android.text.format.DateUtils import android.text.format.DateUtils
import android.text.format.Formatter import android.text.format.Formatter
import android.util.LongSparseArray import android.util.LongSparseArray
@@ -19,6 +16,9 @@ import androidx.core.os.bundleOf
import androidx.databinding.BaseObservable import androidx.databinding.BaseObservable
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.lifecycle.get
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
@@ -31,13 +31,12 @@ import be.mygod.vpnhotspot.databinding.ListitemClientBinding
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
import be.mygod.vpnhotspot.net.monitor.TrafficRecorder import be.mygod.vpnhotspot.net.monitor.TrafficRecorder
import be.mygod.vpnhotspot.room.* import be.mygod.vpnhotspot.room.*
import be.mygod.vpnhotspot.util.ServiceForegroundConnector
import be.mygod.vpnhotspot.util.computeIfAbsentCompat import be.mygod.vpnhotspot.util.computeIfAbsentCompat
import be.mygod.vpnhotspot.util.toPluralInt import be.mygod.vpnhotspot.util.toPluralInt
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import java.text.NumberFormat import java.text.NumberFormat
class ClientsFragment : Fragment(), ServiceConnection { class ClientsFragment : Fragment() {
class NicknameDialogFragment : AlertDialogFragment() { class NicknameDialogFragment : AlertDialogFragment() {
companion object { companion object {
const val KEY_MAC = "mac" const val KEY_MAC = "mac"
@@ -198,7 +197,6 @@ class ClientsFragment : Fragment(), ServiceConnection {
private lateinit var binding: FragmentClientsBinding private lateinit var binding: FragmentClientsBinding
private val adapter = ClientAdapter() private val adapter = ClientAdapter()
private var clients: ClientMonitorService.Binder? = null
private var rates = HashMap<Pair<String, Long>, TrafficRate>() private var rates = HashMap<Pair<String, Long>, TrafficRate>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@@ -210,21 +208,11 @@ class ClientsFragment : Fragment(), ServiceConnection {
binding.swipeRefresher.setOnRefreshListener { binding.swipeRefresher.setOnRefreshListener {
IpNeighbourMonitor.instance?.flush() IpNeighbourMonitor.instance?.flush()
} }
ServiceForegroundConnector(this, this, ClientMonitorService::class) ViewModelProviders.of(requireActivity()).get<ClientViewModel>().clients.observe(this,
Observer<List<Client>> { adapter.submitList(it.toMutableList()) })
return binding.root return binding.root
} }
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
clients = service as ClientMonitorService.Binder
service.clientsChanged[this] = { adapter.submitList(it.toMutableList()) }
}
override fun onServiceDisconnected(name: ComponentName?) {
val clients = clients ?: return
clients.clientsChanged -= this
this.clients = null
}
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
// we just put these two thing together as this is the only place we need to use this event for now // we just put these two thing together as this is the only place we need to use this event for now