I am trying to remove/clear the cache mechanism from AWSAppSyncClient.
Now the problem is i am getting the value which is store or request early (it is cacheing the value and returning that value)
what i want is, it should return the value from the api.(should not cache the value).
i have tried this :
AWSAppSyncClient.builder()
.context(syncPort.androidContext())
.awsConfiguration(AWSConfiguration(syncPort.androidContext()))
.build()
.clearCaches(ClearCacheOptions.builder().clearSubscriptions().build())
Query code :
override fun getMedication(date: String): Single<SampleAdministration> {
return Single.create<SampleAdministration> { emitter ->
client.query(
GetSampleQuery.builder().date(date).build()
).enqueue(object : GraphQLCall.Callback<GetSampleQuery.Data>() {
override fun onFailure(e: ApolloException) {
emitter.onError(e)
}
override fun onResponse(response: Response<GetSampleQuery.Data>) {
val data = response.data()?.sample
val sampleAdministration = object : SampleAdministration {
override var date: String? =
data?.date()
}
emitter.onSuccess(sampleAdministration)
}
})
}
}
i have remove clearCaches from AWSAppSyncClient.builder()
and added responseFetcher in query
override fun getMedication(date: String): Single<SampleAdministration> {
return Single.create<SampleAdministration> { emitter ->
client.query(
GetSampleQuery.builder().date(date).build()
).responseFetcher(AppSyncResponseFetchers.NETWORK_ONLY).enqueue(object : GraphQLCall.Callback<GetSampleQuery.Data>() {
override fun onFailure(e: ApolloException) {
emitter.onError(e)
}override fun onResponse(response: Response<GetSampleQuery.Data>) {
val data = response.data()?.sample
val sampleAdministration = object : SampleAdministration {
override var date: String? =
data?.date()
}
emitter.onSuccess(sampleAdministration)
}
})
}
Related
I would like to use Mapbox to get the road speed limit value but returned the value from LocationObserver always is null while speed limit value in the official map box app has value In the same direction. how can i fix this problem?
setup mapBoxNavigation:
if (!MapboxNavigationApp.isSetup()) {
MapboxNavigationApp.setup {
NavigationOptions.Builder(this)
.accessToken(MAPBOX_ACCESS_TOKEN)
.build()
}
}
lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
MapboxNavigationApp.attach(owner)
}
override fun onPause(owner: LifecycleOwner) {
MapboxNavigationApp.detach(owner)
}
})
MapboxNavigationApp.current()?.startTripSession()
This is observer dataSource:
class MapBoxLocationObserverDataSource #Inject constructor(context: Context) :
MapboxNavigationObserver, MapBoxLocationObserver {
private val speedLimitCallback = MutableStateFlow<NavigationInfo?>(null)
override val speedLimit: Flow<NavigationInfo?>
get() = speedLimitCallback
private val locationObserver = object : LocationObserver {
override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
MapboxSpeedInfoApi().updatePostedAndCurrentSpeed(
locationMatcherResult,
DistanceFormatterOptions.Builder(context).build(),
).apply {
speedLimitCallback.value =
NavigationInfo(speedInfo = this, locationMatcherResult = locationMatcherResult)
}
}
override fun onNewRawLocation(rawLocation: Location) = Unit
}
override fun onAttached(mapboxNavigation: MapboxNavigation) {
mapboxNavigation.registerLocationObserver(locationObserver)
}
override fun onDetached(mapboxNavigation: MapboxNavigation) {
mapboxNavigation.unregisterLocationObserver(locationObserver)
}
data class NavigationInfo(
val speedInfo: SpeedInfoValue,
val locationMatcherResult: LocationMatcherResult
)
}
The speed limit always is null in the LocationMatcherResult but some value are not, like current speed or enhancedLocation
I am trying to observe a progressBar and everything seems ok to me but its not working..
when it gets to the viewmodel its getting null value..
This is the repository:
val isLoadingProgressBarMutableLiveData = MutableLiveData<Boolean>()
fun getEmployeeListFromAPI(): MutableLiveData<List<Employee>> {
isLoadingProgressBarMutableLiveData.value = true
val apiRequest: APICallRequest = APIRequest.retrofitCallGetList
apiRequest.callEmployeeList().enqueue(object : Callback<EmployeesListResult?> {
override fun onResponse(
call: Call<EmployeesListResult?>,
response: Response<EmployeesListResult?>
) {
Log.e("onResponse1", "${isLoadingProgressBarMutableLiveData.value}")
if (response.isSuccessful) {
isLoadingProgressBarMutableLiveData.value = false
mutableListLiveData.value = response.body()?.getEmployeesListResult
Log.e("onResponse2", "${isLoadingProgressBarMutableLiveData.value}")
Log.e("onResponse", "Success!")
Log.e("Response:", "${response.body()}")
}
}
override fun onFailure(call: Call<EmployeesListResult?>, t: Throwable) {
Log.e("onFailure", "Failed getting list: ${t.message}")
isLoadingProgressBarMutableLiveData.value = false
}
})
return mutableListLiveData
}
fun getLoadingState() : MutableLiveData<Boolean>{
return isLoadingProgressBarMutableLiveData
}
"onResponse1" = true
"onResponse2" = false
but when I move it to the ViewModel I get null ...
This is the ViewModel:
class MainViewModel : ViewModel() {
fun getEmployeeListFromRepo() : LiveData<List<Employee>>{
return MainRepository().getEmployeeListFromAPI()
}
fun showProgressBar(): LiveData<Boolean> {
Log.e("Progress","ddd ${MainRepository().getLoadingState().value}")
return MainRepository().getLoadingState()
}
}
"ProgressBar" = is null
And in the activity:
mainViewModel.showProgressBar().observe(this, object : Observer<Boolean?> {
override fun onChanged(isLoading: Boolean?) {
Log.e("isLoadingProgressBar:", "Loading is...: $isLoading")
if (isLoading == true){
progressBar.visibility = View.VISIBLE
}else{
progressBar.visibility = View.GONE
}
}
})
Buddy - every time you do MainRepository() you're creating a new repository and accessing that. You should have one repository you're working with.
class MainViewModel : ViewModel() {
private val repository = MainRespository() // ONE repo
fun getEmployeeListFromRepo() : LiveData<List<Employee>>{
return repository.getEmployeeListFromAPI() // Get the live data to the ONE repo instead of creating a new one
}
fun showProgressBar(): LiveData<Boolean> {
// Print and return the value from the ONE respository instead of creating
// TWO new ones in this method
Log.e("Progress","ddd ${respository.getLoadingState().value}")
return respository.getLoadingState()
}
}
I have a small app I am using to try learn more about some of the newer Android components. I'm finding it difficult to find information and understand how best to do what I want.
Currently: Open app -> load data + stores in DB -> display data in list
I want to be able to query data again upon button press.
I have 2 buttons, 1 to fetch data again, 1 to delete the list data from the DB.
Problem is that it seems you cannot refresh if you are observing on an instance of LiveData, which I am. I understand that however the way I found to actually do a Network call and store in the Database returns an instance of LiveData and I am not sure how best to proceed.
Let me show you the code.
Fragment
private val viewModel: quoteViewModel by viewModels()
private lateinit var binding: FragmentHomeBinding
private lateinit var adapter: QuoteAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initRecyclerView()
setupRetrieveQuotesObserver()
setupDeleteDataListener()
setupFetchNewDataListener()
setupSwipeToRefresh()
}
private fun initRecyclerView() {
adapter = QuoteAdapter()
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.adapter = adapter
}
private fun setupDeleteDataListener() {
binding.removeQuotesButton.setOnClickListener {
viewModel.removeAllQuotes()
}
}
private fun setupFetchNewDataListener() {
binding.getQuotesButton.setOnClickListener {
viewModel.removeQuotes()
viewModel.getQuotes()
}
}
private fun setupRetrieveQuotesObserver() {
viewModel.quoteLiveDataList.observe(viewLifecycleOwner, Observer { result ->
when (result.status) {
NewResult.Status.SUCCESS -> {
result.data.let { adapter.setItems(ArrayList(result.data)) }
binding.progressBar.visibility = View.GONE
binding.swipeContainer.isRefreshing = false
}
NewResult.Status.ERROR -> {
binding.progressBar.visibility = View.GONE
Snackbar.make(binding.root, "Some error has occurred", Snackbar.LENGTH_SHORT)
.show()
}
NewResult.Status.LOADING -> {
binding.progressBar.visibility = View.VISIBLE
}
}
})
}
private fun setupSwipeToRefresh() {
binding.swipeContainer.setOnRefreshListener {
viewModel.getQuotes()
}
}
ViewModel
val quoteLiveDataList: LiveData<NewResult<List<Quote>>> = repository.quotes
fun getQuotes() = viewModelScope.launch {
repository.quotes
}
fun removeAllQuotes() = viewModelScope.launch {
repository.deleteAllQuotes()
}
Repository
val quotes = performGetOperation(
databaseQuery = { dao.getAllQuotes() },
networkCall = { remoteSource.getAllQuotes() },
saveCallResult = {
val quotesList = ArrayList<Quote>()
for (messageString in it.messages.non_personalized) {
quotesList.add(
Quote(
messageString,
FaceImageProvider().getRandomFacePicture(),
false
)
)
}
dao.insertQuotes(quotesList)
}
)
#WorkerThread
suspend fun deleteAllQuotes() = withContext(Dispatchers.IO) { dao.deleteAllQuotes() }
performGetOperation
This is a class I saw online for handling what I want to do. I think the issue stems from here as it is returning LiveData, I'm not sure how best to fix it
fun <T, A> performGetOperation(
databaseQuery: () -> LiveData<T>,
networkCall: suspend () -> NewResult<A>,
saveCallResult: suspend (A) -> Unit
): LiveData<NewResult<T>> =
liveData(Dispatchers.IO) {
emit(NewResult.loading())
val source = databaseQuery.invoke().map { NewResult.success(it) }
emitSource(source)
val responseStatus = networkCall.invoke()
if (responseStatus.status == NewResult.Status.SUCCESS) {
saveCallResult(responseStatus.data!!)
} else if (responseStatus.status == NewResult.Status.ERROR) {
emit(NewResult.error(responseStatus.message!!))
emitSource(source)
}
}
RemoteDataSource
suspend fun getQuotes() = getResult { service.getQuotes() }
getResult
protected suspend fun <T> getResult(call: suspend () -> Response<T>): NewResult<T> {
try {
val response = call.invoke()
if (response.isSuccessful) {
val body = response.body()
if (body != null) {
return NewResult.success(body)
}
}
return error("${response.code()} ${response.message()}")
} catch (e: Exception) {
return error(e.message ?: e.toString())
}
}
private fun <T> error(message: String): NewResult<T> {
Log.d("BaseDataSource", message)
return NewResult.error("Network called failed due to: $message")
}
NewResult
data class NewResult<out T>(val status: Status, val data: T?, val message: String?) {
enum class Status {
SUCCESS,
ERROR,
LOADING,
}
companion object {
fun <T> success(data: T): NewResult<T> {
return NewResult(Status.SUCCESS, data, null)
}
fun <T> error(message: String, data: T? = null): NewResult<T> {
return NewResult(Status.ERROR, data, message)
}
fun <T> loading(data: T? = null): NewResult<T> {
return NewResult(Status.LOADING, data, null)
}
}
Apologies for the very long message, but I guess I need to show all the little bits and bobs I'm using.
I think the problem is in the Fragment where I do viewModel.quoteLiveDataList.observe, as it is returning a new LiveData if it is called again. So I'm not sure how I can do another server call and update the DB and return it here.
Appreciate any help!
Thanks
Use Transformations.switchMap on a MutableLiveData to trigger your repository call like it is done here in the GithubBrowserSample project. This will allow you to implement the refresh functionality -
private val _getQuotes = MutableLiveData<Boolean>()
val quotes: LiveData<NewResult<List<Quote>>> = _getQuotes.switchMap { getQuotes ->
repository.quotes
}
fun getQuotes() {
_getQuotes.value = true
}
fun refresh() {
_getQuotes.value?.let {
_getQuotes.value = it
}
}
I have the following ViewModel class -
class VerifyOtpViewModel : ViewModel() {
private var existingUserProfileData: MutableLiveData<TwoVerteUsers.TwoVerteUser>? = null
fun checkInfoForAuthenticatedUser(authorization: String, user: String) {
ProfileNetworking.getUsersProfiles(authorization, GetUserProfilesBodyModel(listOf(user)), object : ProfileNetworking.OnGetUserProfilesListener {
override fun onSuccess(model: TwoVerteUsers) {
existingUserProfileData?.value = model[0]
}
override fun onError(reason: String) {
Log.d("existingProfile", reason)
}
})
}
fun getExistingUserProfileData(): LiveData<TwoVerteUsers.TwoVerteUser>? {
if (existingUserProfileData == null) return null
return existingUserProfileData as LiveData<TwoVerteUsers.TwoVerteUser>
}
}
and the following observer -
private fun initViewModel() {
verifyOtpViewModel = ViewModelProvider(this).get(VerifyOtpViewModel::class.java)
verifyOtpViewModel.getExistingUserProfileData()?.observe(this, Observer {
if (it != null)
Log.d("existingProfile", it.username)
})
}
For some reason the observe is never triggered even after the MutableLiveData object is being given a value
Tried to search for a solution here at stackoverflow but nothing helped
what am I missing?
refactor your code to this, and you should be good to go:
class VerifyOtpViewModel : ViewModel() {
private val _existingUserProfileData = MutableLiveData<TwoVerteUsers.TwoVerteUser>()
val existingUserProfileData: LiveData<TwoVerteUsers.TwoVerteUser>
get() = _existingUserProfileData
fun checkInfoForAuthenticatedUser(authorization: String, user: String) {
ProfileNetworking.getUsersProfiles(
authorization,
GetUserProfilesBodyModel(listOf(user)),
object : ProfileNetworking.OnGetUserProfilesListener {
override fun onSuccess(model: TwoVerteUsers) {
existingUserProfileData.value = model[0]
}
override fun onError(reason: String) {
Log.d("existingProfile", reason)
}
})
}
}
And observing:
verifyOtpViewModel.existingUserProfileData.observe(this, Observer {
.....
})
I was inspired by the writing of this adapter to Valery Katkov's answer answer
My Retrofit call adapter is able to transform the JSON of normal objects correctly, but when I expect from a call a List<Object>, Retrofit returns me a List<LinkedTreeMap>. It cannot parse Object within the list
Exception
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.networkcalladapter.Post
CallAdapter Factory And CallAdapter
class NetworkCallAdapterFactory : CallAdapter.Factory() {
override fun get(
returnType: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
) = when (getRawType(returnType)) {
Call::class.java -> {
val callType = getParameterUpperBound(0, returnType as ParameterizedType)
when (getRawType(callType)) {
ResponseNetwork::class.java -> {
require(callType is ParameterizedType){ "resource must be paramterized" }
val resultType = getParameterUpperBound(0, callType)
ResponseNetworkAdapter<Any>(getRawType(resultType))
}
else -> null
}
}
else -> null
}
}
class ResponseNetworkAdapter<T: Any>(
private val type: Type
) : CallAdapter<T, Call<ResponseNetwork<T>>> {
override fun responseType() = type
override fun adapt(call: Call<T>): Call<ResponseNetwork<T>> = ResponseNetworkCall(call)
}
abstract class CallDelegate<TIn, TOut>(
protected val proxy: Call<TIn>
) : Call<TOut> {
override fun execute(): Response<TOut> = throw NotImplementedError()
final override fun enqueue(callback: Callback<TOut>) = enqueueImpl(callback)
final override fun clone(): Call<TOut> = cloneImpl()
override fun cancel() = proxy.cancel()
override fun request(): Request = proxy.request()
override fun isExecuted() = proxy.isExecuted
override fun isCanceled() = proxy.isCanceled
abstract fun enqueueImpl(callback: Callback<TOut>)
abstract fun cloneImpl(): Call<TOut>
}
class ResponseNetworkCall<T: Any>(proxy: Call<T>) : CallDelegate<T, ResponseNetwork<T>>(proxy) {
override fun enqueueImpl(callback: Callback<ResponseNetwork<T>>) {
proxy.enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
callback.onResponse(this#ResponseNetworkCall, Response.success(ResponseNetwork.create(response)))
}
override fun onFailure(call: Call<T>, t: Throwable) {
callback.onResponse(this#ResponseNetworkCall, Response.success(ResponseNetwork.create(Exception(t))))
}
})
}
override fun cloneImpl() = ResponseNetworkCall(proxy.clone())
}
ResponseNetwork
sealed class ResponseNetwork<T> {
companion object {
fun <T> create(error: Exception): ResponseNetworkError<T> {
return ResponseNetworkError(error)
}
fun <T> create(response: Response<T>): ResponseNetwork<T> {
return if (response.isSuccessful) {
response.body()?.let {
ResponseNetworkSuccess(response.code(), response.headers(), it)
} ?: ResponseNetworkEmpty(
response.code(),
response.errorBody()?.string() ?: "unknown error"
)
} else {
val msg = response.errorBody()?.string()
ResponseNetworkError(Exception(msg))
}
}
}
}
data class ResponseNetworkSuccess<T>(
val code: Int,
val header: Headers,
val body: T
) : ResponseNetwork<T>()
data class ResponseNetworkEmpty<T>(
val code: Int,
val message: String
) : ResponseNetwork<T>()
data class ResponseNetworkError<T>(
val exception: Exception
) : ResponseNetwork<T>()
Remote Api
#GET("posts")
suspend fun getPost(): ResponseNetwork<List<Post>>
Retrofit
Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(NetworkCallAdapterFactory())
.build()
.create(RemoteApi::class.java)
Post Model
data class Post(val userId: Int,
val id: Int,
val title: String,
val body: String)
Someone understands why retrofit always comes back to me List<LinkedTreeMap> whenever I need a list from the network ?
can you replace your remote API with this and check it.
#GET("posts")
suspend fun getPost(): Deferred<Response<ResponseNetwork<List<Post>>>
i fixed my bug in NetworkCallAdapterFactory
ResponseNetworkAdapter<Any>((resultType))