Sweet animations from support lib
This commit is contained in:
@@ -4,8 +4,8 @@ buildscript {
|
|||||||
ext {
|
ext {
|
||||||
androidPluginVersion = '3.0.1'
|
androidPluginVersion = '3.0.1'
|
||||||
kotlinVersion = '1.2.21'
|
kotlinVersion = '1.2.21'
|
||||||
supportLibraryVersion = '27.0.2'
|
supportLibraryVersion = '27.1.0'
|
||||||
takisoftFixVersion = '27.0.2.0'
|
takisoftFixVersion = '27.1.0.0'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import android.support.v4.content.ContextCompat
|
|||||||
import android.support.v4.content.LocalBroadcastManager
|
import android.support.v4.content.LocalBroadcastManager
|
||||||
import android.support.v7.app.AlertDialog
|
import android.support.v7.app.AlertDialog
|
||||||
import android.support.v7.app.AppCompatDialog
|
import android.support.v7.app.AppCompatDialog
|
||||||
|
import android.support.v7.recyclerview.extensions.ListAdapter
|
||||||
|
import android.support.v7.util.DiffUtil
|
||||||
import android.support.v7.widget.DefaultItemAnimator
|
import android.support.v7.widget.DefaultItemAnimator
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import android.support.v7.widget.LinearLayoutManager
|
||||||
import android.support.v7.widget.RecyclerView
|
import android.support.v7.widget.RecyclerView
|
||||||
@@ -101,10 +103,28 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
|||||||
else -> throw IllegalStateException("Invalid IpNeighbour.State: $state")
|
else -> throw IllegalStateException("Invalid IpNeighbour.State: $state")
|
||||||
}, ip)
|
}, ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Client
|
||||||
|
|
||||||
|
if (iface != other.iface) return false
|
||||||
|
if (mac != other.mac) return false
|
||||||
|
if (ip != other.ip) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
override fun hashCode() = Objects.hash(iface, mac, ip)
|
||||||
|
}
|
||||||
|
private object ClientDiffCallback : DiffUtil.ItemCallback<Client>() {
|
||||||
|
override fun areItemsTheSame(oldItem: Client, newItem: Client) =
|
||||||
|
oldItem.iface == newItem.iface && oldItem.mac == newItem.mac
|
||||||
|
override fun areContentsTheSame(oldItem: Client, newItem: Client) = oldItem == newItem
|
||||||
}
|
}
|
||||||
private class ClientViewHolder(val binding: ListitemClientBinding) : RecyclerView.ViewHolder(binding.root)
|
private class ClientViewHolder(val binding: ListitemClientBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
private inner class ClientAdapter : RecyclerView.Adapter<ClientViewHolder>() {
|
private inner class ClientAdapter : ListAdapter<Client, ClientViewHolder>(ClientDiffCallback) {
|
||||||
private val clients = ArrayList<Client>()
|
|
||||||
var p2p: Collection<WifiP2pDevice> = emptyList()
|
var p2p: Collection<WifiP2pDevice> = emptyList()
|
||||||
var neighbours = emptyList<IpNeighbour>()
|
var neighbours = emptyList<IpNeighbour>()
|
||||||
|
|
||||||
@@ -120,10 +140,7 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
|||||||
}
|
}
|
||||||
client.ip += Pair(neighbour.ip, neighbour.state)
|
client.ip += Pair(neighbour.ip, neighbour.state)
|
||||||
}
|
}
|
||||||
clients.clear()
|
submitList(p2p.values.sortedWith(compareBy<Client> { it.iface }.thenBy { it.mac }))
|
||||||
clients.addAll(p2p.values)
|
|
||||||
clients.sortWith(compareBy<Client> { it.iface }.thenBy { it.mac })
|
|
||||||
notifyDataSetChanged() // recreate everything
|
|
||||||
binding.swipeRefresher.isRefreshing = false
|
binding.swipeRefresher.isRefreshing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,11 +148,9 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
|||||||
ClientViewHolder(ListitemClientBinding.inflate(LayoutInflater.from(parent.context)))
|
ClientViewHolder(ListitemClientBinding.inflate(LayoutInflater.from(parent.context)))
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ClientViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ClientViewHolder, position: Int) {
|
||||||
holder.binding.client = clients[position]
|
holder.binding.client = getItem(position)
|
||||||
holder.binding.executePendingBindings()
|
holder.binding.executePendingBindings()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount() = clients.size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var binding: FragmentRepeaterBinding
|
private lateinit var binding: FragmentRepeaterBinding
|
||||||
@@ -153,9 +168,7 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
|||||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_repeater, container, false)
|
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_repeater, container, false)
|
||||||
binding.data = data
|
binding.data = data
|
||||||
binding.clients.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
binding.clients.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||||
val animator = DefaultItemAnimator()
|
binding.clients.itemAnimator = DefaultItemAnimator()
|
||||||
animator.supportsChangeAnimations = false // prevent fading-in/out when rebinding
|
|
||||||
binding.clients.itemAnimator = animator
|
|
||||||
binding.clients.adapter = adapter
|
binding.clients.adapter = adapter
|
||||||
binding.swipeRefresher.setColorSchemeResources(R.color.colorAccent)
|
binding.swipeRefresher.setColorSchemeResources(R.color.colorAccent)
|
||||||
binding.swipeRefresher.setOnRefreshListener {
|
binding.swipeRefresher.setOnRefreshListener {
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import android.os.Bundle
|
|||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.support.v4.app.Fragment
|
import android.support.v4.app.Fragment
|
||||||
import android.support.v4.content.ContextCompat
|
import android.support.v4.content.ContextCompat
|
||||||
import android.support.v7.util.SortedList
|
import android.support.v7.recyclerview.extensions.ListAdapter
|
||||||
|
import android.support.v7.util.DiffUtil
|
||||||
import android.support.v7.widget.DefaultItemAnimator
|
import android.support.v7.widget.DefaultItemAnimator
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import android.support.v7.widget.LinearLayoutManager
|
||||||
import android.support.v7.widget.RecyclerView
|
import android.support.v7.widget.RecyclerView
|
||||||
@@ -20,6 +21,7 @@ import be.mygod.vpnhotspot.net.ConnectivityManagerHelper
|
|||||||
import be.mygod.vpnhotspot.net.TetherType
|
import be.mygod.vpnhotspot.net.TetherType
|
||||||
import java.net.NetworkInterface
|
import java.net.NetworkInterface
|
||||||
import java.net.SocketException
|
import java.net.SocketException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class TetheringFragment : Fragment(), ServiceConnection {
|
class TetheringFragment : Fragment(), ServiceConnection {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -27,22 +29,6 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
|||||||
private const val VIEW_TYPE_MANAGE = 1
|
private const val VIEW_TYPE_MANAGE = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract class BaseSorter<T> : SortedList.Callback<T>() {
|
|
||||||
override fun onInserted(position: Int, count: Int) { }
|
|
||||||
override fun areContentsTheSame(oldItem: T?, newItem: T?): Boolean = oldItem == newItem
|
|
||||||
override fun onMoved(fromPosition: Int, toPosition: Int) { }
|
|
||||||
override fun onChanged(position: Int, count: Int) { }
|
|
||||||
override fun onRemoved(position: Int, count: Int) { }
|
|
||||||
override fun areItemsTheSame(item1: T?, item2: T?): Boolean = item1 == item2
|
|
||||||
override fun compare(o1: T?, o2: T?): Int =
|
|
||||||
if (o1 == null) if (o2 == null) 0 else 1 else if (o2 == null) -1 else compareNonNull(o1, o2)
|
|
||||||
abstract fun compareNonNull(o1: T, o2: T): Int
|
|
||||||
}
|
|
||||||
private open class DefaultSorter<T : Comparable<T>> : BaseSorter<T>() {
|
|
||||||
override fun compareNonNull(o1: T, o2: T): Int = o1.compareTo(o2)
|
|
||||||
}
|
|
||||||
private object TetheredInterfaceSorter : DefaultSorter<TetheredInterface>()
|
|
||||||
|
|
||||||
inner class Data(val iface: TetheredInterface) : BaseObservable() {
|
inner class Data(val iface: TetheredInterface) : BaseObservable() {
|
||||||
val icon: Int get() = TetherType.ofInterface(iface.name).icon
|
val icon: Int get() = TetherType.ofInterface(iface.name).icon
|
||||||
val active = binder?.active?.contains(iface.name) == true
|
val active = binder?.active?.contains(iface.name) == true
|
||||||
@@ -80,10 +66,24 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
|||||||
val addresses = lookup[name]?.formatAddresses() ?: ""
|
val addresses = lookup[name]?.formatAddresses() ?: ""
|
||||||
|
|
||||||
override fun compareTo(other: TetheredInterface) = name.compareTo(other.name)
|
override fun compareTo(other: TetheredInterface) = name.compareTo(other.name)
|
||||||
}
|
override fun equals(other: Any?): Boolean {
|
||||||
inner class TetheringAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
if (this === other) return true
|
||||||
private val tethered = SortedList(TetheredInterface::class.java, TetheredInterfaceSorter)
|
if (javaClass != other?.javaClass) return false
|
||||||
|
other as TetheredInterface
|
||||||
|
if (name != other.name) return false
|
||||||
|
if (addresses != other.addresses) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
override fun hashCode(): Int = Objects.hash(name, addresses)
|
||||||
|
|
||||||
|
object DiffCallback : DiffUtil.ItemCallback<TetheredInterface>() {
|
||||||
|
override fun areItemsTheSame(oldItem: TetheredInterface, newItem: TetheredInterface) =
|
||||||
|
oldItem.name == newItem.name
|
||||||
|
override fun areContentsTheSame(oldItem: TetheredInterface, newItem: TetheredInterface) = oldItem == newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inner class TetheringAdapter :
|
||||||
|
ListAdapter<TetheredInterface, RecyclerView.ViewHolder>(TetheredInterface.DiffCallback) {
|
||||||
fun update(data: Set<String>) {
|
fun update(data: Set<String>) {
|
||||||
val lookup = try {
|
val lookup = try {
|
||||||
NetworkInterface.getNetworkInterfaces().asSequence().associateBy { it.name }
|
NetworkInterface.getNetworkInterfaces().asSequence().associateBy { it.name }
|
||||||
@@ -91,14 +91,12 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
|||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
emptyMap<String, NetworkInterface>()
|
emptyMap<String, NetworkInterface>()
|
||||||
}
|
}
|
||||||
tethered.clear()
|
submitList(data.map { TetheredInterface(it, lookup) }.sorted())
|
||||||
tethered.addAll(data.map { TetheredInterface(it, lookup) })
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount() = tethered.size() + 1
|
override fun getItemCount() = super.getItemCount() + 1
|
||||||
override fun getItemViewType(position: Int) =
|
override fun getItemViewType(position: Int) =
|
||||||
if (position == tethered.size()) VIEW_TYPE_MANAGE else VIEW_TYPE_INTERFACE
|
if (position == super.getItemCount()) VIEW_TYPE_MANAGE else VIEW_TYPE_INTERFACE
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
@@ -109,7 +107,7 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
|||||||
}
|
}
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
when (holder) {
|
when (holder) {
|
||||||
is InterfaceViewHolder -> holder.binding.data = Data(tethered[position])
|
is InterfaceViewHolder -> holder.binding.data = Data(getItem(position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,9 +122,7 @@ class TetheringFragment : Fragment(), ServiceConnection {
|
|||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_tethering, container, false)
|
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_tethering, container, false)
|
||||||
binding.interfaces.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
binding.interfaces.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||||
val animator = DefaultItemAnimator()
|
binding.interfaces.itemAnimator = DefaultItemAnimator()
|
||||||
animator.supportsChangeAnimations = false // prevent fading-in/out when rebinding
|
|
||||||
binding.interfaces.itemAnimator = animator
|
|
||||||
binding.interfaces.adapter = adapter
|
binding.interfaces.adapter = adapter
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user