Refactor server tabs in HomeActivity

- Switched from Fragments to simple Views for pager
- Moved the whole implementation to ServerTabs.kt, instead of splitting across
  multiple files
- Uniformly name adapters & layout files
This commit is contained in:
Gaurav Ujjwal 2021-09-30 14:54:37 +05:30
parent 69dec7ed78
commit 1ebb97a78f
16 changed files with 279 additions and 384 deletions

View file

@ -11,7 +11,10 @@ package com.gaurav.avnc
import com.gaurav.avnc.model.ServerProfile import com.gaurav.avnc.model.ServerProfile
import com.gaurav.avnc.model.db.MainDb import com.gaurav.avnc.model.db.MainDb
import com.gaurav.avnc.ui.about.AboutActivity import com.gaurav.avnc.ui.about.AboutActivity
import com.gaurav.avnc.ui.home.* import com.gaurav.avnc.ui.home.HomeActivity
import com.gaurav.avnc.ui.home.ProfileEditorFragment
import com.gaurav.avnc.ui.home.ServerTabs
import com.gaurav.avnc.ui.home.UrlBarActivity
import com.gaurav.avnc.ui.prefs.PrefsActivity import com.gaurav.avnc.ui.prefs.PrefsActivity
import com.gaurav.avnc.ui.vnc.* import com.gaurav.avnc.ui.vnc.*
import com.gaurav.avnc.viewmodel.HomeViewModel import com.gaurav.avnc.viewmodel.HomeViewModel
@ -68,8 +71,8 @@ import com.gaurav.avnc.vnc.VncClient
* - A urlbar, which launches [UrlBarActivity], allowing user to quickly connect * - A urlbar, which launches [UrlBarActivity], allowing user to quickly connect
* to a server without creating a profile for it. * to a server without creating a profile for it.
* *
* - List of saved servers, shown by [ServersFragment]. * - Lists of saved & discovered servers, in [ServerTabs].
* - List of servers found by [Discovery], shown by [DiscoveryFragment]. *
* - [ProfileEditorFragment], used for creating/editing [ServerProfile]. * - [ProfileEditorFragment], used for creating/editing [ServerProfile].
* *
* *

View file

@ -1,62 +0,0 @@
/*
* Copyright (c) 2020 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/
package com.gaurav.avnc.ui.home
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.gaurav.avnc.databinding.FragmentDiscoveryBinding
import com.gaurav.avnc.ui.home.adapter.DiscoveryAdapter
import com.gaurav.avnc.viewmodel.HomeViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
/**
* Fragment for displaying VNC servers found by Discovery.
*/
class DiscoveryFragment : Fragment() {
val viewModel by activityViewModels<HomeViewModel>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val binding = FragmentDiscoveryBinding.inflate(inflater, container, false)
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
val adapter = DiscoveryAdapter(viewModel)
binding.discoveredRv.layoutManager = LinearLayoutManager(requireContext())
binding.discoveredRv.adapter = adapter
binding.discoveredRv.setHasFixedSize(true)
viewModel.discovery.servers.observe(viewLifecycleOwner) { adapter.submitList(it) }
launchRestarter()
return binding.root
}
private fun launchRestarter() = lifecycleScope.launch(Dispatchers.Main) {
while (true) {
delay(viewModel.pref.server.discoveryTimeout + viewModel.pref.server.discoveryRestartDelay)
if (activity?.lifecycle?.currentState == Lifecycle.State.RESUMED &&
viewModel.pref.server.discoveryAutoStart &&
viewModel.pref.server.discoveryRestart) {
viewModel.startDiscovery()
}
}
}
}

View file

@ -34,7 +34,7 @@ import com.google.android.material.snackbar.Snackbar
class HomeActivity : AppCompatActivity() { class HomeActivity : AppCompatActivity() {
val viewModel by viewModels<HomeViewModel>() val viewModel by viewModels<HomeViewModel>()
private lateinit var binding: ActivityHomeBinding private lateinit var binding: ActivityHomeBinding
private lateinit var tabController: TabController private val tabs = ServerTabs(this)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -44,7 +44,8 @@ class HomeActivity : AppCompatActivity() {
binding = DataBindingUtil.setContentView(this, R.layout.activity_home) binding = DataBindingUtil.setContentView(this, R.layout.activity_home)
binding.lifecycleOwner = this binding.lifecycleOwner = this
tabController = TabController(this, binding.pager, binding.tabLayout) tabs.create(binding.tabLayout, binding.pager)
binding.drawerNav.setNavigationItemSelectedListener { onMenuItemSelected(it.itemId) } binding.drawerNav.setNavigationItemSelectedListener { onMenuItemSelected(it.itemId) }
binding.navigationBtn.setOnClickListener { binding.drawerLayout.open() } binding.navigationBtn.setOnClickListener { binding.drawerLayout.open() }
binding.settingsBtn.setOnClickListener { showSettings() } binding.settingsBtn.setOnClickListener { showSettings() }
@ -107,7 +108,7 @@ class HomeActivity : AppCompatActivity() {
} }
private fun onProfileInserted(profile: ServerProfile) { private fun onProfileInserted(profile: ServerProfile) {
tabController.showSavedServers() tabs.showSavedServers()
// Show snackbar for new servers // Show snackbar for new servers
if (profile.ID == 0L) if (profile.ID == 0L)
@ -124,6 +125,6 @@ class HomeActivity : AppCompatActivity() {
} }
private fun updateDiscoveryBadge(list: List<ServerProfile>) { private fun updateDiscoveryBadge(list: List<ServerProfile>) {
tabController.updateDiscoveryBadge(list.size) tabs.updateDiscoveryBadge(list.size)
} }
} }

View file

@ -0,0 +1,255 @@
/*
* Copyright (c) 2021 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/
package com.gaurav.avnc.ui.home
import android.view.*
import androidx.core.view.forEach
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.gaurav.avnc.R
import com.gaurav.avnc.databinding.ServerDiscoveryBinding
import com.gaurav.avnc.databinding.ServerDiscoveryItemBinding
import com.gaurav.avnc.databinding.ServerSavedBinding
import com.gaurav.avnc.databinding.ServerSavedItemBinding
import com.gaurav.avnc.model.ServerProfile
import com.gaurav.avnc.ui.home.ServerTabs.PagerAdapter.ViewHolder
import com.gaurav.avnc.util.Experimental
import com.gaurav.avnc.viewmodel.HomeViewModel
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
/**
* This class creates and manages tabs in [HomeActivity].
* Tabs:
* 1. Saved servers
* 2. Discovered servers
*/
class ServerTabs(val activity: HomeActivity) {
lateinit var savedServersTab: TabLayout.Tab
lateinit var discoveredServersTab: TabLayout.Tab
/**
* Creates and initializes tabs
*
* [tabLayout] Hosts tabs
* [pager] Hosts actual content views
*/
fun create(tabLayout: TabLayout, pager: ViewPager2) {
pager.adapter = PagerAdapter()
pager.offscreenPageLimit = 1 // Tell pager to initialize & keep both tabs in memory
TabLayoutMediator(tabLayout, pager) { _, _ -> }
.attach()
savedServersTab = tabLayout.getTabAt(0)!!
savedServersTab.setIcon(R.drawable.ic_computer)
savedServersTab.setContentDescription(R.string.desc_saved_servers_tab)
discoveredServersTab = tabLayout.getTabAt(1)!!
discoveredServersTab.setIcon(R.drawable.ic_search)
discoveredServersTab.setContentDescription(R.string.desc_discovered_servers_tab)
}
fun showSavedServers() {
savedServersTab.select()
}
fun updateDiscoveryBadge(count: Int) {
//Currently, we are not showing the actual count in the badge.
//But maybe we could implement a preference???
discoveredServersTab.getOrCreateBadge().isVisible = (count != 0)
}
/**
* Adapter for our pager.
*
* We have fixed number of static views so our implementation is really simple.
* We override [getItemViewType] to use the given position itself as view type.
* Then, in [onCreateViewHolder], that view type (i.e. position) is used to
* generate corresponding view.
*
* As a result, nothing else needs to be done by [ViewHolder] & [bindViewHolder].
*/
private inner class PagerAdapter : RecyclerView.Adapter<PagerAdapter.ViewHolder>() {
private inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v)
override fun getItemCount() = 2
override fun getItemViewType(position: Int) = position
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return when (viewType) {
0 -> ViewHolder(createSavedServersView(parent))
1 -> ViewHolder(createDiscoveredServersView(parent))
else -> throw IllegalStateException("Unexpected view type: [$viewType]")
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {}
}
/**********************************************************************************************
* Saved servers
**********************************************************************************************/
private fun createSavedServersView(parent: ViewGroup): View {
val binding = ServerSavedBinding.inflate(activity.layoutInflater, parent, false)
binding.lifecycleOwner = activity
binding.viewModel = activity.viewModel
val adapter = SavedServerAdapter(activity.viewModel)
binding.serversRv.layoutManager = LinearLayoutManager(activity)
binding.serversRv.adapter = adapter
binding.serversRv.setHasFixedSize(true)
activity.viewModel.serverProfiles.observe(activity) { adapter.submitList(it) }
if (activity.viewModel.pref.experimental.indicator) {
Experimental.Indicator().setup(binding, adapter)
}
return binding.root
}
/**
* Adapter for saved servers
*/
class SavedServerAdapter(val viewModel: HomeViewModel)
: ListAdapter<ServerProfile, SavedServerAdapter.ViewHolder>(Differ) {
//Experimental feature
var bindListener: ((ViewHolder, ServerProfile) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ServerSavedItemBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val profile = getItem(position)
holder.profile = profile
holder.binding.viewModel = profile
bindListener?.invoke(holder, profile)
}
inner class ViewHolder(val binding: ServerSavedItemBinding)
: ProfileViewHolder(viewModel, binding.root, R.menu.saved_server)
object Differ : DiffUtil.ItemCallback<ServerProfile>() {
override fun areItemsTheSame(old: ServerProfile, new: ServerProfile) = (old.ID == new.ID)
override fun areContentsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
}
}
/**********************************************************************************************
* Discovered servers
**********************************************************************************************/
private fun createDiscoveredServersView(parent: ViewGroup): View {
val binding = ServerDiscoveryBinding.inflate(activity.layoutInflater, parent, false)
binding.lifecycleOwner = activity
binding.viewModel = activity.viewModel
val adapter = DiscoveredServerAdapter(activity.viewModel)
binding.discoveredRv.layoutManager = LinearLayoutManager(activity)
binding.discoveredRv.adapter = adapter
binding.discoveredRv.setHasFixedSize(true)
activity.viewModel.discovery.servers.observe(activity) { adapter.submitList(it) }
return binding.root
}
class DiscoveredServerAdapter(val viewModel: HomeViewModel)
: ListAdapter<ServerProfile, DiscoveredServerAdapter.ViewHolder>(Differ) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ServerDiscoveryItemBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val profile = getItem(position)
holder.profile = profile
holder.binding.viewModel = profile
}
inner class ViewHolder(val binding: ServerDiscoveryItemBinding) :
ProfileViewHolder(viewModel, binding.root, R.menu.discovered_server) {
init {
binding.saveBtn.setOnClickListener { viewModel.onNewProfile(profile) }
}
}
/**
* Profiles generated by discovery don't have unique IDs, so we compare the whole profile.
*/
object Differ : DiffUtil.ItemCallback<ServerProfile>() {
override fun areItemsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
override fun areContentsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
}
}
/**
* Base ViewHolder for [ServerProfile], used by both adapters.
*/
open class ProfileViewHolder(
private val homeViewModel: HomeViewModel,
private val rootView: View,
private val contextMenuId: Int)
: RecyclerView.ViewHolder(rootView) {
/**
* Points to the profile being rendered by this view holder.
* Updated by adapters during onBindViewHolder().
*/
var profile = ServerProfile()
init {
rootView.setOnClickListener { homeViewModel.startConnection(profile) }
rootView.setOnCreateContextMenuListener { contextMenu, view, _ ->
MenuInflater(view.context).inflate(contextMenuId, contextMenu)
contextMenu.forEach { item ->
item.setOnMenuItemClickListener { onContextMenuItemClick(it) }
}
}
}
private fun onContextMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.edit -> homeViewModel.onEditProfile(profile)
R.id.duplicate -> homeViewModel.onDuplicateProfile(profile)
R.id.delete -> homeViewModel.deleteProfile(profile)
R.id.copy_host -> copyToClipboard(profile.host)
R.id.copy_name -> copyToClipboard(profile.name)
}
return true
}
private fun copyToClipboard(text: String) {
homeViewModel.setClipboardText(text)
Snackbar.make(rootView, R.string.msg_copied_to_clipboard, Snackbar.LENGTH_SHORT).show()
}
}
}

View file

@ -1,48 +0,0 @@
/*
* Copyright (c) 2020 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/
package com.gaurav.avnc.ui.home
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import com.gaurav.avnc.databinding.FragmentServersBinding
import com.gaurav.avnc.ui.home.adapter.ServersAdapter
import com.gaurav.avnc.util.Experimental
import com.gaurav.avnc.viewmodel.HomeViewModel
/**
* Fragment for displaying list of known servers.
*/
class ServersFragment : Fragment() {
val viewModel by activityViewModels<HomeViewModel>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val binding = FragmentServersBinding.inflate(inflater, container, false)
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
val adapter = ServersAdapter(viewModel)
binding.serversRv.layoutManager = LinearLayoutManager(context)
binding.serversRv.adapter = adapter
binding.serversRv.setHasFixedSize(true)
viewModel.serverProfiles.observe(viewLifecycleOwner) { adapter.submitList(it) }
if (viewModel.pref.experimental.indicator) {
Experimental.Indicator().setup(binding, adapter)
}
return binding.root
}
}

View file

@ -1,69 +0,0 @@
/*
* Copyright (c) 2021 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/
package com.gaurav.avnc.ui.home
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.gaurav.avnc.R
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
/**
* Controls tab layout and associated pager in home activity.
*/
class TabController(val activity: HomeActivity, private val pager: ViewPager2, tabLayout: TabLayout) {
private data class PageInfo(
val fragment: Fragment,
@DrawableRes val iconId: Int,
@StringRes val descriptionId: Int
)
private val pageList = listOf(
PageInfo(ServersFragment(), R.drawable.ic_computer, R.string.desc_saved_servers_tab),
PageInfo(DiscoveryFragment(), R.drawable.ic_search, R.string.desc_discovered_servers_tab),
)
private inner class PagerAdapter : FragmentStateAdapter(activity) {
override fun getItemCount() = pageList.size
override fun createFragment(position: Int) = pageList[position].fragment
}
private val discoveryTab: TabLayout.Tab
init {
pager.adapter = PagerAdapter()
// Ask the the pager to keep both fragments in memory
// This allows smoother transition between fragments
pager.offscreenPageLimit = 1
val mediator = TabLayoutMediator(tabLayout, pager) { tab, position ->
tab.setIcon(pageList[position].iconId)
tab.setContentDescription(pageList[position].descriptionId)
}
mediator.attach()
discoveryTab = tabLayout.getTabAt(1)!!
}
fun showSavedServers() {
pager.setCurrentItem(0, true)
}
fun updateDiscoveryBadge(count: Int) {
//Currently, we are not showing the actual count in the badge.
//But maybe we could implement a preference???
discoveryTab.getOrCreateBadge().isVisible = (count != 0)
}
}

View file

@ -1,52 +0,0 @@
/*
* Copyright (c) 2020 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/
package com.gaurav.avnc.ui.home.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import com.gaurav.avnc.R
import com.gaurav.avnc.databinding.ItemDiscoveryBinding
import com.gaurav.avnc.model.ServerProfile
import com.gaurav.avnc.viewmodel.HomeViewModel
class DiscoveryAdapter(val viewModel: HomeViewModel) : ListAdapter<ServerProfile, DiscoveryAdapter.ViewHolder>(Differ) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ItemDiscoveryBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
/**
* View holder for discovered servers.
*/
inner class ViewHolder(binding: ItemDiscoveryBinding) :
ProfileViewHolder(viewModel, binding, R.menu.discovered_server) {
init {
binding.saveBtn.setOnClickListener { viewModel.onNewProfile(profile) }
}
}
/**
* This is slightly different from [ServersAdapter.Differ] because all profiles generated
* by service discovery have same ID (0) & all discovered servers are unique. So we
* compare the whole profile instead of a particular field.
*/
object Differ : DiffUtil.ItemCallback<ServerProfile>() {
override fun areItemsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
override fun areContentsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
}
}

View file

@ -1,88 +0,0 @@
/*
* Copyright (c) 2020 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/
package com.gaurav.avnc.ui.home.adapter
import android.view.MenuInflater
import android.view.MenuItem
import androidx.core.view.forEach
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView
import com.gaurav.avnc.BR
import com.gaurav.avnc.R
import com.gaurav.avnc.model.ServerProfile
import com.gaurav.avnc.viewmodel.HomeViewModel
import com.google.android.material.snackbar.Snackbar
/**
* ViewHolder for [ServerProfile].
*/
open class ProfileViewHolder(
private val homeViewModel: HomeViewModel,
val binding: ViewDataBinding,
private val menuId: Int)
: RecyclerView.ViewHolder(binding.root) {
/**
* Points to the profile being rendered by this view holder.
* See [bind].
*/
var profile = ServerProfile()
init {
val root = binding.root
root.setOnClickListener {
homeViewModel.startConnection(profile)
}
root.setOnCreateContextMenuListener { contextMenu, view, _ ->
MenuInflater(view.context).inflate(menuId, contextMenu)
contextMenu.forEach { item ->
item.setOnMenuItemClickListener { onContextOptionClick(it) }
}
}
}
/**
* Handle context menu events
*/
private fun onContextOptionClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.edit -> homeViewModel.onEditProfile(profile)
R.id.duplicate -> homeViewModel.onDuplicateProfile(profile)
R.id.delete -> homeViewModel.deleteProfile(profile)
R.id.copy_host -> {
homeViewModel.setClipboardText(profile.host)
showCopiedMsg()
}
R.id.copy_name -> {
homeViewModel.setClipboardText(profile.name)
showCopiedMsg()
}
}
return true
}
private fun showCopiedMsg() {
Snackbar.make(binding.root, R.string.msg_copied_to_clipboard, Snackbar.LENGTH_SHORT).show()
}
/**
* Binds given profile to this view holder.
*/
fun bind(profile: ServerProfile) {
this.profile = profile
binding.setVariable(BR.viewModel, profile)
binding.executePendingBindings()
}
}

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2020 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/
package com.gaurav.avnc.ui.home.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import com.gaurav.avnc.R
import com.gaurav.avnc.databinding.ItemServerBinding
import com.gaurav.avnc.model.ServerProfile
import com.gaurav.avnc.viewmodel.HomeViewModel
/**
* Adapter for known servers
*/
class ServersAdapter(val viewModel: HomeViewModel)
: ListAdapter<ServerProfile, ProfileViewHolder>(Differ) {
//Experimental feature
var bindListener: ((ProfileViewHolder, ServerProfile) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ItemServerBinding.inflate(inflater, parent, false)
return ProfileViewHolder(viewModel, binding, R.menu.server_profile)
}
override fun onBindViewHolder(holder: ProfileViewHolder, position: Int) {
val profile = getItem(position)
holder.bind(profile)
bindListener?.invoke(holder, profile)
}
object Differ : DiffUtil.ItemCallback<ServerProfile>() {
override fun areItemsTheSame(old: ServerProfile, new: ServerProfile) = (old.ID == new.ID)
override fun areContentsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
}
}

View file

@ -21,10 +21,9 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.OnLifecycleEvent
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.gaurav.avnc.databinding.FragmentServersBinding import com.gaurav.avnc.databinding.ServerSavedBinding
import com.gaurav.avnc.databinding.ItemServerBinding
import com.gaurav.avnc.model.ServerProfile import com.gaurav.avnc.model.ServerProfile
import com.gaurav.avnc.ui.home.adapter.ServersAdapter import com.gaurav.avnc.ui.home.ServerTabs.SavedServerAdapter
/** /**
* All the experimental stuff is dumped here. * All the experimental stuff is dumped here.
@ -83,7 +82,7 @@ object Experimental {
* *
* Note: Viewmodel & lifecycle must have been assigned to [binding]. * Note: Viewmodel & lifecycle must have been assigned to [binding].
*/ */
fun setup(binding: FragmentServersBinding, adapter: ServersAdapter) { fun setup(binding: ServerSavedBinding, adapter: SavedServerAdapter) {
Log.i(javaClass.simpleName, "Setting up Experimental Indicator") Log.i(javaClass.simpleName, "Setting up Experimental Indicator")
// There are 4 event sources which we are interested in: // There are 4 event sources which we are interested in:
@ -100,11 +99,9 @@ object Experimental {
// 2 - RV Binding: We need to know which RV Item View is rendering which profile. // 2 - RV Binding: We need to know which RV Item View is rendering which profile.
adapter.bindListener = { holder, profile -> adapter.bindListener = { holder, profile ->
if (holder.binding is ItemServerBinding) { val view = holder.binding.indicator
val view = holder.binding.indicator profileMap[view] = profile
profileMap[view] = profile animatorMap[view] = isServerDiscovered(profile)
animatorMap[view] = isServerDiscovered(profile)
}
} }
// 3 - RV Views: We need to clear Views which are no longer used by RecyclerView. // 3 - RV Views: We need to clear Views which are no longer used by RecyclerView.

View file

@ -28,7 +28,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:contentDescription="@string/desc_discovered_list" android:contentDescription="@string/desc_discovered_list"
tools:listitem="@layout/item_discovery" /> tools:listitem="@layout/server_discovery_item" />
<TextView <TextView
style="@style/TipView" style="@style/TipView"

View file

@ -22,7 +22,7 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<include <include
layout="@layout/server_profile" layout="@layout/server_item"
app:profile="@{viewModel}" /> app:profile="@{viewModel}" />
<ImageButton <ImageButton

View file

@ -6,6 +6,9 @@
~ See COPYING.txt for more details. ~ See COPYING.txt for more details.
--> -->
<!--
- This is common server view, used by both saved and discovered server items.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">

View file

@ -28,7 +28,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:contentDescription="@string/desc_saved_servers_list" android:contentDescription="@string/desc_saved_servers_list"
tools:listitem="@layout/item_server" /> tools:listitem="@layout/server_saved_item" />
<TextView <TextView
style="@style/TipView" style="@style/TipView"

View file

@ -23,7 +23,7 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<include <include
layout="@layout/server_profile" layout="@layout/server_item"
app:profile="@{viewModel}" /> app:profile="@{viewModel}" />
<!--Experimental Feature--> <!--Experimental Feature-->