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 androidx.lifecycle.ViewModelProviders
import com.google.android.gms.maps.CameraUpdateFactory import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.model.* 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.Cluster
import com.google.maps.android.clustering.ClusterItem
import com.google.maps.android.clustering.ClusterManager import com.google.maps.android.clustering.ClusterManager
import cy.agorise.bitsybitshareswallet.database.entities.Merchant import cy.agorise.bitsybitshareswallet.database.entities.Merchant
import cy.agorise.bitsybitshareswallet.database.entities.Teller import cy.agorise.bitsybitshareswallet.database.entities.Teller
import cy.agorise.bitsybitshareswallet.utils.Constants import cy.agorise.bitsybitshareswallet.utils.Constants
import cy.agorise.bitsybitshareswallet.utils.MerchantMarkerRenderer import cy.agorise.bitsybitshareswallet.utils.MerchantClusterRenderer
import cy.agorise.bitsybitshareswallet.utils.TellerMarkerRenderer import cy.agorise.bitsybitshareswallet.utils.TellerClusterRenderer
import cy.agorise.bitsybitshareswallet.utils.toast import cy.agorise.bitsybitshareswallet.utils.toast
import cy.agorise.bitsybitshareswallet.viewmodels.MerchantViewModel import cy.agorise.bitsybitshareswallet.viewmodels.MerchantViewModel
import java.lang.Exception import java.lang.Exception
class MerchantsFragment : Fragment(), OnMapReadyCallback, class MerchantsFragment : Fragment(), OnMapReadyCallback {
ClusterManager.OnClusterClickListener<Merchant>,
ClusterManager.OnClusterItemClickListener<Merchant>,
ClusterManager.OnClusterItemInfoWindowClickListener<Merchant>{
companion object { companion object {
private const val TAG = "MerchantsFragment" private const val TAG = "MerchantsFragment"
@ -48,13 +47,12 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback,
private lateinit var mMerchantViewModel: MerchantViewModel private lateinit var mMerchantViewModel: MerchantViewModel
private var mMarkerManager: MarkerManager? = null
private var mMerchantClusterManager: ClusterManager<Merchant>? = null private var mMerchantClusterManager: ClusterManager<Merchant>? = null
private var mTellerClusterManager: ClusterManager<Teller>? = null private var mTellerClusterManager: ClusterManager<Teller>? = null
override fun onCreateView( override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_merchants, container, false) 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) 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 */ /** Handles the result from the location permission request */
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
@ -110,9 +97,19 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback,
verifyLocationPermission() verifyLocationPermission()
mMarkerManager = MarkerManager(mMap)
initMerchantsCluster() initMerchantsCluster()
initTellersCluster() 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() { 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() { private fun initMerchantsCluster() {
// Setup clusters to group markers when possible // Setup clusters to group markers when possible
mMerchantClusterManager = ClusterManager(context, mMap) mMerchantClusterManager = ClusterManager(context, mMap, mMarkerManager)
val merchantRenderer = MerchantMarkerRenderer(context, mMap, mMerchantClusterManager) val merchantRenderer = MerchantClusterRenderer(context, mMap, mMerchantClusterManager)
mMerchantClusterManager?.renderer = merchantRenderer mMerchantClusterManager?.renderer = merchantRenderer
// Point the map's listeners at the listeners implemented by the cluster manager. mMerchantClusterManager?.setOnClusterClickListener { onClusterClick(it as Cluster<ClusterItem>) }
mMap.setOnCameraIdleListener(mMerchantClusterManager) mMerchantClusterManager?.setOnClusterItemClickListener { false }
mMap.setOnMarkerClickListener(mMerchantClusterManager)
mMap.setOnMarkerClickListener(mMerchantClusterManager)
mMap.setInfoWindowAdapter(mMerchantClusterManager?.markerManager)
mMap.setOnInfoWindowClickListener(mMerchantClusterManager)
mMerchantClusterManager?.setOnClusterClickListener(this)
mMerchantClusterManager?.setOnClusterItemClickListener(this)
mMerchantClusterManager?.setOnClusterItemInfoWindowClickListener(this)
mMerchantViewModel.getAllMerchants().observe(this, Observer<List<Merchant>> {merchants -> mMerchantViewModel.getAllMerchants().observe(this, Observer<List<Merchant>> {merchants ->
mMerchantClusterManager?.clearItems() mMerchantClusterManager?.clearItems()
@ -160,10 +160,13 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback,
private fun initTellersCluster() { private fun initTellersCluster() {
// Setup clusters to group markers when possible // Setup clusters to group markers when possible
mTellerClusterManager = ClusterManager(context, mMap) mTellerClusterManager = ClusterManager(context, mMap, mMarkerManager)
val tellerRenderer = TellerMarkerRenderer(context, mMap, mTellerClusterManager) val tellerRenderer = TellerClusterRenderer(context, mMap, mTellerClusterManager)
mTellerClusterManager?.renderer = tellerRenderer mTellerClusterManager?.renderer = tellerRenderer
mTellerClusterManager?.setOnClusterClickListener { onClusterClick(it as Cluster<ClusterItem>) }
mTellerClusterManager?.setOnClusterItemClickListener { false }
mMerchantViewModel.getAllTellers().observe(this, Observer<List<Teller>> {tellers -> mMerchantViewModel.getAllTellers().observe(this, Observer<List<Teller>> {tellers ->
mTellerClusterManager?.clearItems() mTellerClusterManager?.clearItems()
mTellerClusterManager?.addItems(tellers) 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. */ /** 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 builder = LatLngBounds.builder()
val merchantMarkers = cluster?.items val items = cluster?.items
if (merchantMarkers != null) { if (items != null) {
for (item in merchantMarkers) { for (item in items) {
val merchantPosition = item.position val position = item.position
builder.include(merchantPosition) builder.include(position)
} }
val bounds = builder.build() val bounds = builder.build()
@ -190,14 +193,6 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback,
Log.d(TAG, e.message) Log.d(TAG, e.message)
} }
} }
return true 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.content.Context
import android.os.AsyncTask import android.os.AsyncTask
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import cy.agorise.bitsybitshareswallet.database.BitsyDatabase import cy.agorise.bitsybitshareswallet.database.BitsyDatabase
import cy.agorise.bitsybitshareswallet.database.daos.MerchantDao 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>> { class MerchantRepository internal constructor(val context: Context) : retrofit2.Callback<FeathersResponse<Merchant>> {
companion object {
private const val TAG = "MerchantRepository"
}
private val mMerchantDao: MerchantDao private val mMerchantDao: MerchantDao
init { init {
@ -38,6 +43,8 @@ class MerchantRepository internal constructor(val context: Context) : retrofit2.
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
if (lastMerchantUpdate + Constants.MERCHANTS_UPDATE_PERIOD < now) { 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() val retrofit = Retrofit.Builder()
.baseUrl(Constants.MERCHANTS_WEBSERVICE_URL) .baseUrl(Constants.MERCHANTS_WEBSERVICE_URL)
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create())

View file

@ -3,6 +3,7 @@ package cy.agorise.bitsybitshareswallet.repositories
import android.content.Context import android.content.Context
import android.os.AsyncTask import android.os.AsyncTask
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import cy.agorise.bitsybitshareswallet.database.BitsyDatabase import cy.agorise.bitsybitshareswallet.database.BitsyDatabase
import cy.agorise.bitsybitshareswallet.database.daos.TellerDao 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>> { class TellerRepository internal constructor(val context: Context) : retrofit2.Callback<FeathersResponse<Teller>> {
companion object {
private const val TAG = "TellerRepository"
}
private val mTellerDao: TellerDao private val mTellerDao: TellerDao
init { init {
@ -38,6 +43,8 @@ class TellerRepository internal constructor(val context: Context) : retrofit2.Ca
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
if (lastTellerUpdate + Constants.MERCHANTS_UPDATE_PERIOD < now) { 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() val retrofit = Retrofit.Builder()
.baseUrl(Constants.MERCHANTS_WEBSERVICE_URL) .baseUrl(Constants.MERCHANTS_WEBSERVICE_URL)
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create())

View file

@ -1,6 +1,5 @@
package cy.agorise.bitsybitshareswallet.utils package cy.agorise.bitsybitshareswallet.utils
import cy.agorise.graphenej.Asset
import cy.agorise.graphenej.UserAccount import cy.agorise.graphenej.UserAccount
object Constants { object Constants {
@ -94,5 +93,6 @@ object Constants {
/** Key used to store the last time in millis that the tellers info was refreshed */ /** 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" 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 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. * 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) { DefaultClusterRenderer<Merchant>(context, map, clusterManager) {
// Icons used to display merchants and merchants' clusters on the map // 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. * 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) { DefaultClusterRenderer<Teller>(context, map, clusterManager) {
// Icons used to display merchants and merchants' clusters on the map // Icons used to display merchants and merchants' clusters on the map