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:
parent
fbbb4f9f48
commit
6a8def0cf1
6 changed files with 61 additions and 52 deletions
|
@ -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?) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in a new issue