Refactor using ViewModel
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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?) {
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user