Create MerchantViewModel and MerchantRepository, the first to hold the merchants data and deliver it to the MerchantsFragment and the second to serve as a single source to fetch the Merchants list. When it gets a request for the merchants it retrieves and serves the list from the database and meanwhile refreshes the list from the WebService.

This commit is contained in:
Severiano Jaramillo 2019-01-23 15:37:13 -06:00
parent 6aad0ca624
commit 4bb53de009
5 changed files with 100 additions and 33 deletions

View file

@ -1,8 +1,10 @@
package cy.agorise.bitsybitshareswallet.database.daos package cy.agorise.bitsybitshareswallet.database.daos
import androidx.lifecycle.LiveData
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query
import cy.agorise.bitsybitshareswallet.database.entities.Merchant import cy.agorise.bitsybitshareswallet.database.entities.Merchant
@Dao @Dao
@ -12,4 +14,7 @@ interface MerchantDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(merchants: List<Merchant>) fun insertAll(merchants: List<Merchant>)
@Query("SELECT * FROM merchants")
fun getAll(): LiveData<List<Merchant>>
} }

View file

@ -12,17 +12,12 @@ import android.view.ViewGroup
import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment import com.google.android.gms.maps.SupportMapFragment
import com.google.gson.GsonBuilder
import cy.agorise.bitsybitshareswallet.R import cy.agorise.bitsybitshareswallet.R
import cy.agorise.bitsybitshareswallet.network.MerchantsWebservice
import cy.agorise.bitsybitshareswallet.network.FeathersResponse
import retrofit2.Call
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import android.preference.PreferenceManager import android.preference.PreferenceManager
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
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.clustering.Cluster import com.google.maps.android.clustering.Cluster
@ -31,10 +26,11 @@ import cy.agorise.bitsybitshareswallet.database.entities.Merchant
import cy.agorise.bitsybitshareswallet.utils.Constants import cy.agorise.bitsybitshareswallet.utils.Constants
import cy.agorise.bitsybitshareswallet.utils.MerchantMarkerRenderer import cy.agorise.bitsybitshareswallet.utils.MerchantMarkerRenderer
import cy.agorise.bitsybitshareswallet.utils.toast import cy.agorise.bitsybitshareswallet.utils.toast
import cy.agorise.bitsybitshareswallet.viewmodels.MerchantViewModel
import java.lang.Exception import java.lang.Exception
class MerchantsFragment : Fragment(), OnMapReadyCallback, retrofit2.Callback<FeathersResponse<Merchant>>, class MerchantsFragment : Fragment(), OnMapReadyCallback,
ClusterManager.OnClusterClickListener<Merchant>, ClusterManager.OnClusterClickListener<Merchant>,
ClusterManager.OnClusterItemClickListener<Merchant>, ClusterManager.OnClusterItemClickListener<Merchant>,
ClusterManager.OnClusterItemInfoWindowClickListener<Merchant>{ ClusterManager.OnClusterItemInfoWindowClickListener<Merchant>{
@ -48,6 +44,8 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, retrofit2.Callback<Fea
private lateinit var mMap: GoogleMap private lateinit var mMap: GoogleMap
private lateinit var mMerchantViewModel: MerchantViewModel
private var mClusterManager: ClusterManager<Merchant>? = null private var mClusterManager: ClusterManager<Merchant>? = null
override fun onCreateView( override fun onCreateView(
@ -63,6 +61,8 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, retrofit2.Callback<Fea
// Obtain the SupportMapFragment and get notified when the map is ready to be used. // Obtain the SupportMapFragment and get notified when the map is ready to be used.
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this) mapFragment.getMapAsync(this)
mMerchantViewModel = ViewModelProviders.of(this).get(MerchantViewModel::class.java)
} }
private fun verifyLocationPermission() { private fun verifyLocationPermission() {
@ -123,17 +123,11 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, retrofit2.Callback<Fea
mClusterManager?.setOnClusterItemClickListener(this) mClusterManager?.setOnClusterItemClickListener(this)
mClusterManager?.setOnClusterItemInfoWindowClickListener(this) mClusterManager?.setOnClusterItemInfoWindowClickListener(this)
val gson = GsonBuilder() mMerchantViewModel.getAllMerchants().observe(this, Observer<List<Merchant>> {merchants ->
.setLenient() mClusterManager?.clearItems()
.create() mClusterManager?.addItems(merchants)
val retrofit = Retrofit.Builder() mClusterManager?.cluster()
.baseUrl(Constants.MERCHANTS_WEBSERVICE_URL) })
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
val ambassadorService = retrofit.create<MerchantsWebservice>(MerchantsWebservice::class.java)
val call = ambassadorService.getMerchants(0)
call.enqueue(this)
} }
private fun applyMapTheme() { private fun applyMapTheme() {
@ -155,19 +149,6 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, retrofit2.Callback<Fea
} }
} }
override fun onResponse(call: Call<FeathersResponse<Merchant>>, response: Response<FeathersResponse<Merchant>>) {
if (response.isSuccessful) {
val res: FeathersResponse<Merchant>? = response.body()
val merchants = res?.data ?: return
mClusterManager?.addItems(merchants)
mClusterManager?.cluster()
} else {
Log.e("error_bitsy", response.errorBody()?.string())
}
}
override fun onFailure(call: Call<FeathersResponse<Merchant>>, t: Throwable) { /* Do nothing */ }
/** /**
* 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.
*/ */

View file

@ -9,7 +9,9 @@ import retrofit2.http.Query
interface MerchantsWebservice { interface MerchantsWebservice {
@GET("/api/v1/merchants") @GET("/api/v1/merchants")
fun getMerchants(@Query(value = "\$skip") skip: Int): Call<FeathersResponse<Merchant>> fun getMerchants(@Query(value = "\$skip") skip: Int,
@Query(value = "\$limit") limit: Int = 50):
Call<FeathersResponse<Merchant>>
@GET("api/v2/tellers") @GET("api/v2/tellers")
fun getTellers(@Query(value = "\$skip") skip: Int): Call<FeathersResponse<Teller>> fun getTellers(@Query(value = "\$skip") skip: Int): Call<FeathersResponse<Teller>>

View file

@ -0,0 +1,64 @@
package cy.agorise.bitsybitshareswallet.repositories
import android.content.Context
import android.os.AsyncTask
import androidx.lifecycle.LiveData
import cy.agorise.bitsybitshareswallet.database.BitsyDatabase
import cy.agorise.bitsybitshareswallet.database.daos.MerchantDao
import cy.agorise.bitsybitshareswallet.database.entities.Merchant
import cy.agorise.bitsybitshareswallet.network.FeathersResponse
import cy.agorise.bitsybitshareswallet.network.MerchantsWebservice
import cy.agorise.bitsybitshareswallet.utils.Constants
import retrofit2.Call
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class MerchantRepository internal constructor(context: Context) : retrofit2.Callback<FeathersResponse<Merchant>> {
private val mMerchantDao: MerchantDao
init {
val db = BitsyDatabase.getDatabase(context)
mMerchantDao = db!!.merchantDao()
}
/**
* Returns a LiveData object directly from the database while the response from the WebService is obtained.
*/
fun getAll(): LiveData<List<Merchant>> {
refreshMerchants()
return mMerchantDao.getAll()
}
private fun refreshMerchants() {
val retrofit = Retrofit.Builder()
.baseUrl(Constants.MERCHANTS_WEBSERVICE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val ambassadorService = retrofit.create<MerchantsWebservice>(MerchantsWebservice::class.java)
val call = ambassadorService.getMerchants(0)
call.enqueue(this)
}
override fun onResponse(call: Call<FeathersResponse<Merchant>>, response: Response<FeathersResponse<Merchant>>) {
if (response.isSuccessful) {
val res: FeathersResponse<Merchant>? = response.body()
val merchants = res?.data ?: return
insertAllAsyncTask(mMerchantDao).execute(merchants)
}
}
override fun onFailure(call: Call<FeathersResponse<Merchant>>, t: Throwable) { /* Do nothing */ }
private class insertAllAsyncTask internal constructor(private val mAsyncTaskDao: MerchantDao) :
AsyncTask<List<Merchant>, Void, Void>() {
override fun doInBackground(vararg merchants: List<Merchant>): Void? {
// TODO Delete all first
mAsyncTaskDao.insertAll(merchants[0])
return null
}
}
}

View file

@ -0,0 +1,15 @@
package cy.agorise.bitsybitshareswallet.viewmodels
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import cy.agorise.bitsybitshareswallet.database.entities.Merchant
import cy.agorise.bitsybitshareswallet.repositories.MerchantRepository
class MerchantViewModel(application: Application) : AndroidViewModel(application) {
private var mMerchantRepository = MerchantRepository(application)
internal fun getAllMerchants(): LiveData<List<Merchant>> {
return mMerchantRepository.getAll()
}
}