Make some changes to the Merchant's map Cluster logic, so that both Merchants and Tellers clusters work at the same time and automatically as the map camera moves.

This commit is contained in:
Severiano Jaramillo 2019-01-24 13:37:06 -06:00
parent fbbb4f9f48
commit 6a8def0cf1
6 changed files with 61 additions and 52 deletions

View file

@ -20,22 +20,21 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.model.*
import com.google.maps.android.MarkerManager
import com.google.maps.android.clustering.Cluster
import com.google.maps.android.clustering.ClusterItem
import com.google.maps.android.clustering.ClusterManager
import cy.agorise.bitsybitshareswallet.database.entities.Merchant
import cy.agorise.bitsybitshareswallet.database.entities.Teller
import cy.agorise.bitsybitshareswallet.utils.Constants
import cy.agorise.bitsybitshareswallet.utils.MerchantMarkerRenderer
import cy.agorise.bitsybitshareswallet.utils.TellerMarkerRenderer
import cy.agorise.bitsybitshareswallet.utils.MerchantClusterRenderer
import cy.agorise.bitsybitshareswallet.utils.TellerClusterRenderer
import cy.agorise.bitsybitshareswallet.utils.toast
import cy.agorise.bitsybitshareswallet.viewmodels.MerchantViewModel
import java.lang.Exception
class MerchantsFragment : Fragment(), OnMapReadyCallback,
ClusterManager.OnClusterClickListener<Merchant>,
ClusterManager.OnClusterItemClickListener<Merchant>,
ClusterManager.OnClusterItemInfoWindowClickListener<Merchant>{
class MerchantsFragment : Fragment(), OnMapReadyCallback {
companion object {
private const val TAG = "MerchantsFragment"
@ -48,13 +47,12 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback,
private lateinit var mMerchantViewModel: MerchantViewModel
private var mMarkerManager: MarkerManager? = null
private var mMerchantClusterManager: ClusterManager<Merchant>? = null
private var mTellerClusterManager: ClusterManager<Teller>? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_merchants, container, false)
}
@ -68,17 +66,6 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback,
mMerchantViewModel = ViewModelProviders.of(this).get(MerchantViewModel::class.java)
}
private fun verifyLocationPermission() {
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not already granted
requestPermissions(arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_LOCATION_PERMISSION)
} else {
// Permission is already granted
mMap.isMyLocationEnabled = true
}
}
/** Handles the result from the location permission request */
@SuppressLint("MissingPermission")
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
@ -110,9 +97,19 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback,
verifyLocationPermission()
mMarkerManager = MarkerManager(mMap)
initMerchantsCluster()
initTellersCluster()
// Point the map's listeners at the listeners implemented by the cluster manager.
mMap.setOnMarkerClickListener(mMarkerManager)
mMap.setOnCameraIdleListener {
mMerchantClusterManager?.onCameraIdle()
mTellerClusterManager?.onCameraIdle()
}
}
private fun applyMapTheme() {
@ -134,22 +131,25 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback,
}
}
private fun verifyLocationPermission() {
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not already granted
requestPermissions(arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_LOCATION_PERMISSION)
} else {
// Permission is already granted
mMap.isMyLocationEnabled = true
}
}
private fun initMerchantsCluster() {
// Setup clusters to group markers when possible
mMerchantClusterManager = ClusterManager(context, mMap)
val merchantRenderer = MerchantMarkerRenderer(context, mMap, mMerchantClusterManager)
mMerchantClusterManager = ClusterManager(context, mMap, mMarkerManager)
val merchantRenderer = MerchantClusterRenderer(context, mMap, mMerchantClusterManager)
mMerchantClusterManager?.renderer = merchantRenderer
// Point the map's listeners at the listeners implemented by the cluster manager.
mMap.setOnCameraIdleListener(mMerchantClusterManager)
mMap.setOnMarkerClickListener(mMerchantClusterManager)
mMap.setOnMarkerClickListener(mMerchantClusterManager)
mMap.setInfoWindowAdapter(mMerchantClusterManager?.markerManager)
mMap.setOnInfoWindowClickListener(mMerchantClusterManager)
mMerchantClusterManager?.setOnClusterClickListener(this)
mMerchantClusterManager?.setOnClusterItemClickListener(this)
mMerchantClusterManager?.setOnClusterItemInfoWindowClickListener(this)
mMerchantClusterManager?.setOnClusterClickListener { onClusterClick(it as Cluster<ClusterItem>) }
mMerchantClusterManager?.setOnClusterItemClickListener { false }
mMerchantViewModel.getAllMerchants().observe(this, Observer<List<Merchant>> {merchants ->
mMerchantClusterManager?.clearItems()
@ -160,10 +160,13 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback,
private fun initTellersCluster() {
// Setup clusters to group markers when possible
mTellerClusterManager = ClusterManager(context, mMap)
val tellerRenderer = TellerMarkerRenderer(context, mMap, mTellerClusterManager)
mTellerClusterManager = ClusterManager(context, mMap, mMarkerManager)
val tellerRenderer = TellerClusterRenderer(context, mMap, mTellerClusterManager)
mTellerClusterManager?.renderer = tellerRenderer
mTellerClusterManager?.setOnClusterClickListener { onClusterClick(it as Cluster<ClusterItem>) }
mTellerClusterManager?.setOnClusterItemClickListener { false }
mMerchantViewModel.getAllTellers().observe(this, Observer<List<Teller>> {tellers ->
mTellerClusterManager?.clearItems()
mTellerClusterManager?.addItems(tellers)
@ -172,14 +175,14 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback,
}
/** Animates the camera update to focus on an area that shows all the items from the cluster that was tapped. */
override fun onClusterClick(cluster: Cluster<Merchant>?): Boolean {
private fun onClusterClick(cluster: Cluster<ClusterItem>?): Boolean {
val builder = LatLngBounds.builder()
val merchantMarkers = cluster?.items
val items = cluster?.items
if (merchantMarkers != null) {
for (item in merchantMarkers) {
val merchantPosition = item.position
builder.include(merchantPosition)
if (items != null) {
for (item in items) {
val position = item.position
builder.include(position)
}
val bounds = builder.build()
@ -190,14 +193,6 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback,
Log.d(TAG, e.message)
}
}
return true
}
override fun onClusterItemClick(p0: Merchant?): Boolean {
return false
}
override fun onClusterItemInfoWindowClick(p0: Merchant?) {
}
}

View file

@ -3,6 +3,7 @@ package cy.agorise.bitsybitshareswallet.repositories
import android.content.Context
import android.os.AsyncTask
import android.preference.PreferenceManager
import android.util.Log
import androidx.lifecycle.LiveData
import cy.agorise.bitsybitshareswallet.database.BitsyDatabase
import cy.agorise.bitsybitshareswallet.database.daos.MerchantDao
@ -17,6 +18,10 @@ import retrofit2.converter.gson.GsonConverterFactory
class MerchantRepository internal constructor(val context: Context) : retrofit2.Callback<FeathersResponse<Merchant>> {
companion object {
private const val TAG = "MerchantRepository"
}
private val mMerchantDao: MerchantDao
init {
@ -38,6 +43,8 @@ class MerchantRepository internal constructor(val context: Context) : retrofit2.
val now = System.currentTimeMillis()
if (lastMerchantUpdate + Constants.MERCHANTS_UPDATE_PERIOD < now) {
Log.d(TAG, "Updating merchants from webservice")
// TODO make sure it works when there are more merchants than those sent back in the first response
val retrofit = Retrofit.Builder()
.baseUrl(Constants.MERCHANTS_WEBSERVICE_URL)
.addConverterFactory(GsonConverterFactory.create())

View file

@ -3,6 +3,7 @@ package cy.agorise.bitsybitshareswallet.repositories
import android.content.Context
import android.os.AsyncTask
import android.preference.PreferenceManager
import android.util.Log
import androidx.lifecycle.LiveData
import cy.agorise.bitsybitshareswallet.database.BitsyDatabase
import cy.agorise.bitsybitshareswallet.database.daos.TellerDao
@ -17,6 +18,10 @@ import retrofit2.converter.gson.GsonConverterFactory
class TellerRepository internal constructor(val context: Context) : retrofit2.Callback<FeathersResponse<Teller>> {
companion object {
private const val TAG = "TellerRepository"
}
private val mTellerDao: TellerDao
init {
@ -38,6 +43,8 @@ class TellerRepository internal constructor(val context: Context) : retrofit2.Ca
val now = System.currentTimeMillis()
if (lastTellerUpdate + Constants.MERCHANTS_UPDATE_PERIOD < now) {
Log.d(TAG, "Updating tellers from webservice")
// TODO make sure it works when there are more tellers than those sent back in the first response
val retrofit = Retrofit.Builder()
.baseUrl(Constants.MERCHANTS_WEBSERVICE_URL)
.addConverterFactory(GsonConverterFactory.create())

View file

@ -1,6 +1,5 @@
package cy.agorise.bitsybitshareswallet.utils
import cy.agorise.graphenej.Asset
import cy.agorise.graphenej.UserAccount
object Constants {
@ -94,5 +93,6 @@ object Constants {
/** Key used to store the last time in millis that the tellers info was refreshed */
const val KEY_TELLERS_LAST_UPDATE = "key_tellers_last_update"
/** Constant used to decide whether or not to update the tellers and merchants info from the webservice */
const val MERCHANTS_UPDATE_PERIOD = 1000L * 60 * 60 + 24 // 1 day
}

View file

@ -21,7 +21,7 @@ import cy.agorise.bitsybitshareswallet.database.entities.Merchant
/**
* This class is used to create custom merchant and merchant cluster icons to show on the map.
*/
class MerchantMarkerRenderer(val context: Context?, map: GoogleMap?, clusterManager: ClusterManager<Merchant>?) :
class MerchantClusterRenderer(val context: Context?, map: GoogleMap?, clusterManager: ClusterManager<Merchant>?) :
DefaultClusterRenderer<Merchant>(context, map, clusterManager) {
// Icons used to display merchants and merchants' clusters on the map

View file

@ -21,7 +21,7 @@ import cy.agorise.bitsybitshareswallet.database.entities.Teller
/**
* This class is used to create custom merchant and merchant cluster icons to show on the map.
*/
class TellerMarkerRenderer(val context: Context?, map: GoogleMap?, clusterManager: ClusterManager<Teller>?) :
class TellerClusterRenderer(val context: Context?, map: GoogleMap?, clusterManager: ClusterManager<Teller>?) :
DefaultClusterRenderer<Teller>(context, map, clusterManager) {
// Icons used to display merchants and merchants' clusters on the map