loadAfter method of Android Paging library calls itself continuously - android

Just after calling loadInitial method it automatically starts calling loadAfter method until and unless all pages are loaded and it gets an empty array from response. I am attaching my DataSource and DataSourceFactory code along with ViewModel function and PagedListAdapter.
PassbookDataSource.kt
package co.indiagold.gold.buy.loan.home
import androidx.paging.PageKeyedDataSource
import co.indiagold.gold.buy.loan.helper.GenericMethods.handleServerError
import co.indiagold.gold.buy.loan.helper.GenericResponse
import co.indiagold.gold.buy.loan.network.Repository
import co.indiagold.gold.buy.loan.network.ResultWrapper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
class PassbookDataSource(private val accessToken: String, private val assetName:String,private val viewModelScope: CoroutineScope) : PageKeyedDataSource<Int, PassbookResponseModel>() {
private val PAGE_SIZE = 20
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, PassbookResponseModel>) {
var result: ResultWrapper<GenericResponse<ArrayList<PassbookResponseModel>?>?>? = null
viewModelScope.launch {
result = Repository.getPassbookDetail(accessToken, assetName,1, PAGE_SIZE)
}.invokeOnCompletion {
if (result is ResultWrapper.Success) {
val successResult = result as ResultWrapper.Success
callback.onResult(successResult.value!!.result as MutableList<PassbookResponseModel>, null, 2)
} else {
it.handleServerError("vault/passbook")
}
}
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, PassbookResponseModel>) {
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, PassbookResponseModel>) {
var result: ResultWrapper<GenericResponse<ArrayList<PassbookResponseModel>?>?>? = null
viewModelScope.launch {
result = Repository.getPassbookDetail(accessToken,assetName, params.key, PAGE_SIZE)
}.invokeOnCompletion {
if (result is ResultWrapper.Success) {
val successResult = result as ResultWrapper.Success
callback.onResult(successResult.value!!.result as MutableList<PassbookResponseModel>, params.key+1)
} else {
it.handleServerError("vault/passbook")
}
}
}
}
PassbookDataSourceFactory.kt
class PassbookDataSourceFactory(private val accessToken:String,private val assetName:String,private val viewModelScope:CoroutineScope) : DataSource.Factory<Int,PassbookResponseModel>(){
val orderListMLD = MutableLiveData<PassbookDataSource>(null)
var orderDataSource : PassbookDataSource? = null
override fun create(): DataSource<Int, PassbookResponseModel> {
orderDataSource = PassbookDataSource(accessToken,assetName,viewModelScope)
orderListMLD.postValue(orderDataSource)
return orderDataSource!!
}
}
Viewmodel Function
fun getPassbookDetail(assetName: String) {
val accessToken = GenericMethods.getDataFromPreferences(Constants.SHARED_PREFS.LOGIN_PREFERENCES, Constants.SHARED_PREFS.ACCESS_TOKEN)
if (accessToken.isNullOrEmpty()) {
handleNullAccessTokenCase()
} else {
val factory = PassbookDataSourceFactory(accessToken, assetName, viewModelScope)
val config = PagedList.Config.Builder()
.setEnablePlaceholders(true)
.setInitialLoadSizeHint(10)
.setPrefetchDistance(4)
.build()
val executor = Executors.newFixedThreadPool(5)
passbookResponseMLD = LivePagedListBuilder(factory, config)
.setFetchExecutor(executor)
.build()
}
}
PassbookAdapter.kt
package co.indiagold.gold.buy.loan.home
import android.app.Activity
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.paging.PagedList
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import co.indiagold.gold.buy.loan.databinding.CardPassbookBinding
import co.indiagold.gold.buy.loan.helper.Constants
import co.indiagold.gold.buy.loan.orders.OrdersActivity
import kotlinx.android.synthetic.main.card_passbook.view.*
class PassbookAdapter(private val activity: Activity,private val emptyListListener: EmptyListListener) : PagedListAdapter<PassbookResponseModel,PassbookAdapter.MyHolder>(PASSBOOK_DIFF_CALLBACK) {
private lateinit var binding: CardPassbookBinding
class MyHolder(itemView : View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
binding = CardPassbookBinding.inflate(LayoutInflater.from(parent.context))
emptyListListener.onListLoad(currentList?.size)
return MyHolder(binding.root)
}
override fun onCurrentListChanged(previousList: PagedList<PassbookResponseModel>?, currentList: PagedList<PassbookResponseModel>?) {
super.onCurrentListChanged(previousList, currentList)
emptyListListener.onListLoad(currentList?.size)
}
override fun onBindViewHolder(holder: MyHolder, position: Int) {
binding.passbookEntry = getItem(position)
holder.itemView.passbookRootLayout.setOnClickListener {
val intent = Intent(activity,OrdersActivity::class.java)
intent.putExtra(Constants.BUNDLE_STRINGS.ORDER_ID,getItem(position)?.referenceId)
activity.startActivity(intent)
}
}
override fun getItemId(position: Int): Long {
return getItem(position)!!.createdAt
}
/*override fun getItemViewType(position: Int): Int {
return position
}*/
}
private val PASSBOOK_DIFF_CALLBACK = object : DiffUtil.ItemCallback<PassbookResponseModel>(){
override fun areItemsTheSame(oldItem: PassbookResponseModel, newItem: PassbookResponseModel): Boolean {
return oldItem.createdAt == newItem.createdAt
}
override fun areContentsTheSame(oldItem: PassbookResponseModel, newItem: PassbookResponseModel): Boolean {
return oldItem == newItem
}
}

The issue is, that you may put recycler view inside NextedScrollview or ScrollView, by removing scrollview it will work fine

Related

RecyclerView shows a list of copies of activity screen instead of data from database table

I am simply trying to setup the ClickListener for changing the user name. Google's tutorials emphasize fragments, which I will do later. Based on what I've seen in other examples and the Android documentation, I thought I had the View Binding set up properly. I don't have enough information to understand why I see a list of copies of the activity screen inside the RecyclerView.
UserListAdapter.kt
package com.neillbarrett.debitsandcredits
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.neillbarrett.debitsandcredits.database.UsersTable
import com.neillbarrett.debitsandcredits.databinding.ActivityManageUsersBinding
val inAdapter: String = "In UserListAdapter "
class UserListAdapter(private val userSelect: (UsersTable?) -> Unit) :
ListAdapter<UsersTable, UserListAdapter.UserViewHolder>(UsersComparator()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserListAdapter.UserViewHolder {
Log.w(inAdapter,"OnCreateViewHolder started")
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder.create(parent)
}
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(usersTable: UsersTable?, userSelect: (UsersTable?) -> Unit) {
Log.w(inAdapter,"UserViewHolder started")
itemView.setOnClickListener { View.OnClickListener {
/* if (View.) { }*/
val nameSelected = userSelect(usersTable)
//userSelect(usersTable)
//need to assign the result of the clicklistener to the editText
//binding.etEditName.setText(R.layout.activity_list_of_users.toString())
}}
}
companion object {
fun create(parent: ViewGroup) : UserViewHolder {
Log.w(inAdapter,"Companion object 'Create' function started")
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder(view)
}
}
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
Log.w(inAdapter,"OnBindViewHolder started")
val current = getItem(position)
holder.bind(current, userSelect)
}
class UsersComparator : DiffUtil.ItemCallback<UsersTable>() {
override fun areItemsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
Log.w(inAdapter,"areItemsTheSame function started")
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
Log.w(inAdapter,"areContentsTheSame function started")
return oldItem.userName == newItem.userName
}
}
}
ManageUsers.kt
package com.neillbarrett.debitsandcredits
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.activity.viewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.neillbarrett.debitsandcredits.database.CreditsAndDebitsApp
import com.neillbarrett.debitsandcredits.database.UsersTable
import com.neillbarrett.debitsandcredits.databinding.ActivityManageUsersBinding
class ManageUsers : AppCompatActivity() {
lateinit var binding: ActivityManageUsersBinding
lateinit var recyclerView: RecyclerView
lateinit var editTextAddUser: EditText
lateinit var editTextChangeUser: EditText
lateinit var newUser: String
var userSelect: ((UsersTable?) -> Unit) = {}
var position: Long = 0
val inWhichActivity: String = "In ManageUsers"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityManageUsersBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//setContentView(R.layout.activity_manage_users)
Log.w(inWhichActivity, "Setting up userViewModel & repository")
val userViewModel: UserViewModel by viewModels {
UserViewModelFactory((application as CreditsAndDebitsApp).repository)
}
recyclerView = findViewById(R.id.rec_view_userList)
editTextAddUser = findViewById(R.id.et_AddUser)
editTextChangeUser = findViewById(R.id.et_Edit_Name)
val adapter = UserListAdapter(userSelect)
binding.recViewUserList.adapter = adapter
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
userViewModel.allUsers.observe(this, Observer() {user ->
Log.w(inWhichActivity,"Starting Observer")
user?.let { adapter.submitList(it) }
Log.w(inWhichActivity, "Started Observer")
})
val btnAddUser = findViewById<Button>(R.id.btn_AddUser)
btnAddUser.setOnClickListener {
Log.w(inWhichActivity,"Started btnAddUser.setOnClickListener")
if (TextUtils.isEmpty(editTextAddUser.text)) {
Toast.makeText(this, "User name cannot be empty", Toast.LENGTH_SHORT).show()
} else {
newUser = editTextAddUser.text.toString()
// Log.w("Add user button", "Username put into newUser")
userViewModel.insertUser(UsersTable(0, newUser))
// Toast.makeText(this, "Username added to table", Toast.LENGTH_SHORT).show()
// Log.w("Add user button", "Username added to table")
}
}
val btnChangeUser = findViewById<Button>(R.id.btn_ChangeUserName)
btnChangeUser.setOnClickListener {
Log.w(inWhichActivity,"Started btnChangeUser.setOnClickListener")
Toast.makeText(this, "Selected position is ${recyclerView.getChildAdapterPosition(it)}", Toast.LENGTH_SHORT).show()
/* if (recyclerView.getChildAdapterPosition(it) == -1) {
Toast.makeText(this, "Select a name.", Toast.LENGTH_SHORT).show()
} else {
if (editTextChangeUser.text.toString() == recyclerView.adapter.toString()) {
Toast.makeText(this, "Name has not been changed.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Name would have been changed.", Toast.LENGTH_SHORT).show()
val rvItemRecId: Long
rvItemRecId = adapter.getItemId(position.toInt())
userViewModel.updateUser(UsersTable(rvItemRecId.toInt(), adapter.toString()))
}
}*/
}
}
}
UserViewModel.kt
package com.neillbarrett.debitsandcredits
import android.util.Log
import androidx.lifecycle.*
import com.neillbarrett.debitsandcredits.database.UsersTable
import kotlinx.coroutines.launch
import java.lang.IllegalArgumentException
val inViewModel: String = "In UserViewModel "
class UserViewModel(private val repository: UserRepository) : ViewModel() {
// Using LiveData and caching what allWords returns has several benefits:
// - We can put an observer on the data (instead of polling for changes) and only update the
// the UI when the data actually changes.
// - Repository is completely separated from the UI through the ViewModel.
val allUsers: LiveData<List<UsersTable>> = repository.allUsers.asLiveData()
/**
* Launching a new coroutine to insert the data in a non-blocking way
*/
fun insertUser(user: UsersTable) = viewModelScope.launch {
repository.insertUser(user)
Log.w(inViewModel,"insertUser called")
//repository.insertUser(usersTable = List<UsersTable>())
//repository.insertUser(UsersTable(0, userName = user.userName))
}
fun updateUser(user: UsersTable) = viewModelScope.launch {
repository.updateUser(user)
Log.w(inViewModel, "updateUser called")
}
fun deleteUser(user: UsersTable) = viewModelScope.launch {
repository.deleteUser(user)
Log.w(inViewModel, "deteUser called")
}
}
class UserViewModelFactory(private val repository: UserRepository) : ViewModelProvider.Factory{
override fun <T : ViewModel> create(modelClass: Class<T>): T {
Log.w(inViewModel,"UserViewModelFactory called")
if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return UserViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}

kotlin, firebase recycleview load only last element

The app must show all product but show only last (number 6). when i launch app, work, but return this error : E/RecyclerView: No adapter attached; skipping layout. I'm beginner in android language and searching online i didn't find nothing.
the app was composed from mainActivity and CartActivity
output
this is realtime DB
this is main code
package com.example.tpsi_maccagnola
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.GridLayoutManager
import com.example.tpsi_maccagnola.events.UpdateCarrello
import com.example.tpsi_maccagnola.listener.cartlistener
import com.example.tpsi_maccagnola.listener.foodListener
import com.example.tpsi_maccagnola.modelli.Carrello
import com.example.tpsi_maccagnola.modelli.Food
import com.google.android.material.snackbar.Snackbar
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import kotlinx.android.synthetic.main.activity_main.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class MainActivity : AppCompatActivity(), foodListener, cartlistener {
lateinit var foodListener: foodListener
lateinit var cartlistener1: cartlistener
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
super.onStop()
if (EventBus.getDefault().hasSubscriberForEvent(UpdateCarrello::class.java))
EventBus.getDefault().removeStickyEvent(UpdateCarrello::class.java)
EventBus.getDefault().unregister(this)
}
#Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onUpdateCart(event: UpdateCarrello) {
counCartDB()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
caricaDB()
counCartDB()
}
private fun counCartDB() {
val cartmodels: MutableList<Carrello> = ArrayList()
FirebaseDatabase.getInstance()
.getReference("carrello")
.child("UNIQUE_USER_ID")
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
for (cartsnapshot in snapshot.children) {
val cartmodel = cartsnapshot.getValue(Carrello::class.java)
cartmodel!!.key = cartsnapshot.key
cartmodels.add(cartmodel)
}
cartlistener1.onCartSuccess(cartmodels)
}
override fun onCancelled(error: DatabaseError) {
cartlistener1.onCartFailed(error.message)
}
})
}
private fun caricaDB() {
val foods: MutableList<Food> = ArrayList()
FirebaseDatabase.getInstance()
.getReference("Food").addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
for (FoodSnapshot in snapshot.children) {
val foodModel = FoodSnapshot.getValue(Food::class.java)
foodModel!!.key = FoodSnapshot.key
foods.add(foodModel)
}
foodListener.onloadsuccess(foods)
} else {
foodListener.onloadfailed("non esiste")
}
}
override fun onCancelled(error: DatabaseError) {
foodListener.onloadfailed(error.message)
}
})
}
private fun init() {
foodListener = this
cartlistener1 = this
val gridLayoutManager = GridLayoutManager(this, 2)
food_recycler.layoutManager = gridLayoutManager
food_recycler.addItemDecoration(decoration())
btnCart.setOnClickListener{ startActivity(Intent(this,CartActivity::class.java))}
}
override fun onloadsuccess(foodModelList: List<Food>?) {
val adapter = FoodAdapter(this, foodModelList!!, cartlistener1)
food_recycler.adapter = adapter
}
override fun onloadfailed(message: String?) {
Snackbar.make(layoutprincipale, message!!, Snackbar.LENGTH_LONG).show()
}
override fun onCartSuccess(carrelloList: List<Carrello>) {
var cartTot = 0
for (cartlist in carrelloList!!) {
cartTot += cartlist!!.quantita
/*badge!!.setNumber(cartTot)*/
}
}
override fun onCartFailed(message: String?) {
Snackbar.make(layoutprincipale, message!!, Snackbar.LENGTH_LONG).show()
}
}
this is adapter
package com.example.tpsi_maccagnola
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.tpsi_maccagnola.events.UpdateCarrello
import com.example.tpsi_maccagnola.listener.RecyclerListener
import com.example.tpsi_maccagnola.listener.cartlistener
import com.example.tpsi_maccagnola.modelli.Carrello
import com.example.tpsi_maccagnola.modelli.Food
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import org.greenrobot.eventbus.EventBus
class FoodAdapter(
private val context: Context,
private val list: List<Food>,
private val cartlistener1: cartlistener
) : RecyclerView.Adapter<FoodAdapter.FoodHolder>() {
class FoodHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
var imageView: ImageView?= null
var txtName: TextView?= null
var txtprice: TextView?= null
private var clickListener: RecyclerListener? = null
fun setClickListener (clickListener: RecyclerListener){
this.clickListener = clickListener;
}
init {
imageView = itemView.findViewById(R.id.imageView) as ImageView;
txtName = itemView.findViewById(R.id.txtName) as TextView;
txtprice = itemView.findViewById(R.id.txtprice) as TextView;
itemView.setOnClickListener(this)
}
override fun onClick(p0: View?) {
clickListener!!.onclcickProdotto(p0,adapterPosition )
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FoodHolder {
return FoodHolder(LayoutInflater.from(context).inflate(R.layout.layout_food,parent,false))
}
override fun onBindViewHolder(holder: FoodHolder, position: Int) {
Glide.with(context).load(list[position].image).into(holder.imageView!!)
holder.txtName!!.text =StringBuilder().append(list[position].name)
holder.txtprice!!.text =StringBuilder("€").append(list[position].price)
holder.setClickListener(object: RecyclerListener{
override fun onclcickProdotto(view: View?, position: Int) {
aggiungiCarrello(list[position])
}
} )
}
private fun aggiungiCarrello(food: Food) {
val utenteCart = FirebaseDatabase.getInstance().getReference("Cart").child("UNIQUE_USER_ID")
utenteCart.child(Food.key!!).addListenerForSingleValueEvent(object :ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if(snapshot.exists()){ //se è gia presente aggiorno
val cartmodel = snapshot.getValue(Carrello::class.java)
val updatecart: MutableMap<String,Any> = HashMap()
cartmodel!!.quantita = cartmodel!!.quantita+1
updatecart["quantita"] = cartmodel!!.quantita
updatecart["totale"] =cartmodel!!.quantita * cartmodel.price!!.toFloat()
utenteCart.child(food.key!!)
.updateChildren(updatecart)
.addOnSuccessListener {
EventBus.getDefault().postSticky(UpdateCarrello())
cartlistener1.onCartFailed("aggiunto correttamente")
}.addOnFailureListener{e -> cartlistener1.onCartFailed(e.message)
}
}else{
val carrello1 = Carrello
carrello1.key = food.key
carrello1.name = food.name
carrello1.image = food.image
carrello1.price = food.price
carrello1.quantita = 1
carrello1.totale = food.price!!.toFloat()
utenteCart.child(food.key!!)
.setValue(carrello1)
.addOnSuccessListener {
EventBus.getDefault().postSticky(UpdateCarrello())
cartlistener1.onCartFailed("aggiunto correttamente")
}.addOnFailureListener{e -> cartlistener1.onCartFailed(e.message)
}
}
}
override fun onCancelled(error: DatabaseError) {
cartlistener1.onCartFailed(error.message)
}
})
}
override fun getItemCount(): Int {
return list.size
}
}

Error: requestHandler has not been initialized

This project is a recycler view fragment that gets images from flickr and display it.
I used loopers and handlers to communicate between the recycler fragment and the downloading thumbnail fragment... I'm using Android Studio Bumblebee...
I’m facing an error with requestHandler… Maybe i’m not seeing a specific issue. but i checked the code several times but couldn’t find the missing part…
I get this error:
kotlin.UninitializedPropertyAccessException: lateinit property requestHandler has not been initialized at com.bignerdranch.android.photogallery.ThumbnailDownloader.queueThumbnail(ThumbnailDownloader.kt:94)
it is referring to this one…
fun queueThumbnail(target: T, url: String){ //page 510
Log.i(TAG, "Got a URL: $url")
requestMap[target] = url //page 521
requestHandler.obtainMessage(MESSAGE_DOWNLOAD, target)
.sendToTarget()
}
this code is being called by the Recyclerview from another fragment
override fun onBindViewHolder(holder: PhotoHolder, position: Int) {
val galleryItem = galleryItems[position]
//holder.bindTitle(galleryItem.title)
val placeholder: Drawable = ContextCompat.getDrawable(
requireContext(),
R.drawable.hala_atamleh
)?: ColorDrawable()
holder.bindTitle(placeholder) //should be bindDrawable but it didn't work
--> thumbnailDownloader.queueThumbnail(holder, galleryItem.url) //page 515
}
as far as i know, i Initialized it here:
#Suppress("UNCHECKED_CAST") //page 522
#SuppressLint("HandlerLeak")
override fun onLooperPrepared() {
requestHandler = object : Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message) {
if (msg.what == MESSAGE_DOWNLOAD){
val target = msg.obj as T
Log.i(TAG, "Got a request for URL: ${requestMap[target]}")
handleRequest(target)
}
}
}
}
what did i missed to get this error?
here is the file code:
import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.Message
import android.util.Log
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import java.util.concurrent.ConcurrentHashMap
private const val TAG = "ThumbnailDownloader"
private const val MESSAGE_DOWNLOAD = 0
class ThumbnailDownloader<in T>(private val responseHandler: Handler, //page. 524
private val onThumbnailDownloaded : (T, Bitmap) -> Unit)
: HandlerThread(TAG) /*, LifecycleObserver page 512*/ { //page. 510
private lateinit var requestHandler: Handler //page516++
private var hasQuit = false
private val requestMap = ConcurrentHashMap<T, String>()
private val flickrFetchr = FlickrFetchr()
val fragmentLifecycleObserver: LifecycleObserver = //page 527
object : LifecycleObserver{
#OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun setup() {
Log.i(TAG, "Starting background thread")
start() //page 514
looper
}
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun tearDown() {
Log.i(TAG, "Destroying background thread")
quit()
}
}
#Suppress("UNCHECKED_CAST") //page 522
#SuppressLint("HandlerLeak")
override fun onLooperPrepared() {
requestHandler = object : Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message) {
if (msg.what == MESSAGE_DOWNLOAD){
val target = msg.obj as T
Log.i(TAG, "Got a request for URL: ${requestMap[target]}")
handleRequest(target)
}
}
}
}
//page 528
val viewLifecycleObserver : LifecycleObserver =
object : LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun tearDown(){
Log.i(TAG, "Clearing all requests from Queue")
requestHandler.removeMessages(MESSAGE_DOWNLOAD)
requestMap.clear()
}
}
override fun quit(): Boolean {
hasQuit = true
return super.quit()
}
fun queueThumbnail(target: T, url: String){ //page 510
Log.i(TAG, "Got a URL: $url")
requestMap[target] = url //page 521
requestHandler.obtainMessage(MESSAGE_DOWNLOAD, target)
.sendToTarget()
}
fun clearQueue() {
requestHandler.removeMessages(MESSAGE_DOWNLOAD)
requestMap.clear()
}
private fun handleRequest(target: T){
val url = requestMap[target] ?: return
val bitmap = flickrFetchr.fetchPhoto(url) ?: return
responseHandler.post(Runnable { //page.526
if(requestMap[target] != url || hasQuit){
return#Runnable
}
requestMap.remove(target)
onThumbnailDownloaded(target, bitmap)
})
}
}
PHOTOGalleryFragment:
package com.bignerdranch.android.photogallery
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
private lateinit var photoRecyclerView : RecyclerView
private lateinit var photoGalleryViewModel: PhotoGalleryViewModel
private lateinit var thumbnailDownloader: ThumbnailDownloader<PhotoGalleryFragment.PhotoHolder> //it should be PhotoHolder not Type or Handler
private const val TAG = "PhotoGalleryFragment"
class PhotoGalleryFragment:Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true //page 511
photoGalleryViewModel = //page 494
ViewModelProvider(this).get(PhotoGalleryViewModel::class.java)
//thumbnailDownloader = ThumbnailDownloader() //p.513
val responseHandler = Handler(Looper.getMainLooper())//page 525
thumbnailDownloader =
ThumbnailDownloader(responseHandler){ photoHolder, bitmap ->
val drawable = BitmapDrawable(resources, bitmap)
photoHolder.bindDrawable(drawable)
}
lifecycle.addObserver(thumbnailDownloader.viewLifecycleObserver)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
viewLifecycleOwner.lifecycle.addObserver( //page. 529
thumbnailDownloader.viewLifecycleObserver
)
val view = inflater.inflate(R.layout.fragment_photo_gallery, container, false)
photoRecyclerView = view.findViewById(R.id.photo_recycler_view)
photoRecyclerView.layoutManager = GridLayoutManager(context,3)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
photoGalleryViewModel.galleryItemLiveData.observe(
viewLifecycleOwner,
Observer { galleryItems ->
Log.d(TAG,"Have gallery items from ViewModel $galleryItems" )
photoRecyclerView.adapter = PhotoAdapter(galleryItems)
}
)
}
class PhotoHolder(private val itemImageView: ImageView)
: RecyclerView.ViewHolder(itemImageView){ //page 496
val bindDrawable: (Drawable) -> Unit = itemImageView::setImageDrawable
}
private inner class PhotoAdapter(private val galleryItems: List<GalleryItem>)
:RecyclerView.Adapter<PhotoHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoHolder {
val view = layoutInflater.inflate(
R.layout.list_item_gallery,
parent,
false) as ImageView
return PhotoHolder(view)
}
override fun onBindViewHolder(holder: PhotoHolder, position: Int) {
val galleryItem = galleryItems[position]
//holder.bindTitle(galleryItem.title)
val placeholder: Drawable = ContextCompat.getDrawable(
requireContext(),
R.drawable.hala_atamleh
)?: ColorDrawable()
holder.bindDrawable(placeholder)
thumbnailDownloader.queueThumbnail(holder, galleryItem.url) //page 515
}
override fun getItemCount(): Int = galleryItems.size
}
companion object{
fun newInstance() = PhotoGalleryFragment()
}
override fun onDestroyView() { //page 530
super.onDestroyView()
thumbnailDownloader.clearQueue()
viewLifecycleOwner.lifecycle.removeObserver(
thumbnailDownloader.viewLifecycleObserver
)
}
override fun onDestroy() {
super.onDestroy()
lifecycle.removeObserver(
thumbnailDownloader.fragmentLifecycleObserver
)
}
}
I solved it in 2 steps
1- made requesthandler nullable... thanks to #Tenfour04
2- made the gallery this way
package com.bignerdranch.android.photogallery
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
private const val TAG = "PhotoGalleryFragment"
class PhotoGalleryFragment : Fragment() {
private lateinit var photoGalleryViewModel: PhotoGalleryViewModel
private lateinit var photoRecyclerView: RecyclerView
private lateinit var thumbnailDownloader: ThumbnailDownloader<PhotoHolder>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
photoGalleryViewModel =
ViewModelProviders.of(this).get(PhotoGalleryViewModel::class.java)
val responseHandler = Handler()
thumbnailDownloader =
ThumbnailDownloader(responseHandler) { photoHolder, bitmap ->
val drawable = BitmapDrawable(resources, bitmap)
photoHolder.bindDrawable(drawable)
}
lifecycle.addObserver(thumbnailDownloader.fragmentLifecycleObserver)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewLifecycleOwner.lifecycle.addObserver(
thumbnailDownloader.viewLifecycleObserver
)
val view = inflater.inflate(R.layout.fragment_photo_gallery, container, false)
photoRecyclerView = view.findViewById(R.id.photo_recycler_view)
photoRecyclerView.layoutManager = GridLayoutManager(context, 3)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
photoGalleryViewModel.galleryItemLiveData.observe(
viewLifecycleOwner,
Observer { galleryItems ->
Log.d(TAG, "Have gallery items from view model $galleryItems")
photoRecyclerView.adapter = PhotoAdapter(galleryItems)
})
}
override fun onDestroyView() {
super.onDestroyView()
thumbnailDownloader.clearQueue()
viewLifecycleOwner.lifecycle.removeObserver(
thumbnailDownloader.viewLifecycleObserver
)
}
override fun onDestroy() {
super.onDestroy()
lifecycle.removeObserver(
thumbnailDownloader.fragmentLifecycleObserver
)
}
private class PhotoHolder(private val itemImageView: ImageView) : RecyclerView.ViewHolder(itemImageView) {
val bindDrawable: (Drawable) -> Unit = itemImageView::setImageDrawable
}
private inner class PhotoAdapter(private val galleryItems: List<GalleryItem>) :
RecyclerView.Adapter<PhotoHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): PhotoHolder {
val view = layoutInflater.inflate(
R.layout.list_item_gallery,
parent,
false
) as ImageView
return PhotoHolder(view)
}
override fun getItemCount(): Int = galleryItems.size
override fun onBindViewHolder(holder: PhotoHolder, position: Int) {
val galleryItem = galleryItems[position]
val placeholder: Drawable = ContextCompat.getDrawable(
requireContext(),
R.drawable.bill_up_close
) ?: ColorDrawable()
holder.bindDrawable(placeholder)
thumbnailDownloader.queueThumbnail(holder, galleryItem.url)
}
}
companion object {
fun newInstance() = PhotoGalleryFragment()
}
}

Android Kotlin MVVM with dagger, JetPack components

Currently i am working in Kotlin android development. while writing the MVVM architecture, i am stuck with ViewModel Creation
Error : Only classes are allowed on the left hand side of a class literal
Related classes are
Detail
BaseViewModel class, while creating the object of this class, we getting the error
package com.logicipher.mvvm.ui.base
import androidx.databinding.ObservableBoolean
import androidx.lifecycle.ViewModel
import com.logicipher.mvvm.data.DataManager
import com.logicipher.mvvm.utils.rx.SchedulerProvider
import io.reactivex.disposables.CompositeDisposable
import java.lang.ref.WeakReference
/**
* Created by Shamji N S on 20-08-2020.
*/
abstract class BaseViewModel<N>(
dataManager: DataManager,
schedulerProvider: SchedulerProvider
) : ViewModel() {
private val mDataManager: DataManager
private val mIsLoading = ObservableBoolean()
private val mSchedulerProvider: SchedulerProvider
private val mCompositeDisposable: CompositeDisposable
private var mNavigator: WeakReference<N>? = null
override fun onCleared() {
mCompositeDisposable.dispose()
super.onCleared()
}
fun getCompositeDisposable(): CompositeDisposable {
return mCompositeDisposable
}
fun getDataManager(): DataManager {
return mDataManager
}
fun getIsLoading(): ObservableBoolean {
return mIsLoading
}
fun setIsLoading(isLoading: Boolean) {
mIsLoading.set(isLoading)
}
fun getNavigator(): N? {
return mNavigator!!.get()
}
fun setNavigator(navigator: N) {
mNavigator = WeakReference(navigator)
}
fun getSchedulerProvider(): SchedulerProvider {
return mSchedulerProvider
}
init {
mDataManager = dataManager
mSchedulerProvider = schedulerProvider
mCompositeDisposable = CompositeDisposable()
}
}
BaseActivity
package com.logicipher.mvvm.ui.base
import android.annotation.TargetApi
import android.app.ProgressDialog
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.view.inputmethod.InputMethodManager
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import com.logicipher.mvvm.utils.CommonUtils
import com.logicipher.mvvm.utils.NetworkUtils
import dagger.android.AndroidInjection
/**
* Created by Shamji N S on 20-08-2020.
*/
abstract class BaseActivity<T : ViewDataBinding?, V : BaseViewModel<*>?> :
AppCompatActivity(), BaseFragment.Callback {
// TODO
// this can probably depend on isLoading variable of BaseViewModel,
// since its going to be common for all the activities
private var mProgressDialog: ProgressDialog? = null
private var mViewDataBinding: T? = null
private var mViewModel: V? = null
/**
* Override for set binding variable
*
* #return variable id
*/
abstract fun getBindingVariable(): Int
/**
* #return layout resource id
*/
#LayoutRes
abstract fun getLayoutId(): Int
/**
* Override for set view model
*
* #return view model instance
*/
abstract fun getViewModel(): V
override fun onFragmentAttached() {
}
override fun onFragmentDetached(tag: String?) {
}
override fun onCreate(savedInstanceState: Bundle?) {
performDependencyInjection()
super.onCreate(savedInstanceState)
performDataBinding()
}
fun getViewDataBinding(): T? {
return mViewDataBinding
}
#TargetApi(Build.VERSION_CODES.M)
fun hasPermission(permission: String?): Boolean {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
checkSelfPermission(permission!!) == PackageManager.PERMISSION_GRANTED
}
fun hideKeyboard() {
val view = this.currentFocus
if (view != null) {
val imm =
getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm?.hideSoftInputFromWindow(view.windowToken, 0)
}
}
fun hideLoading() {
if (mProgressDialog != null && mProgressDialog!!.isShowing) {
mProgressDialog!!.cancel()
}
}
fun isNetworkConnected(): Boolean {
return NetworkUtils.isNetworkConnected(applicationContext)
}
fun openActivityOnTokenExpire() {
}
fun performDependencyInjection() {
AndroidInjection.inject(this)
}
#TargetApi(Build.VERSION_CODES.M)
fun requestPermissionsSafely(
permissions: Array<String?>?,
requestCode: Int
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(permissions!!, requestCode)
}
}
fun showLoading() {
hideLoading()
mProgressDialog = CommonUtils.showLoadingDialog(this)
}
private fun performDataBinding() {
mViewDataBinding = DataBindingUtil.setContentView<T>(this, getLayoutId())
mViewModel = if (mViewModel == null) getViewModel() else mViewModel
mViewDataBinding!!.setVariable(getBindingVariable(), mViewModel)
mViewDataBinding!!.executePendingBindings()
}
}
MainActivity
package com.logicipher.mvvm.ui.main
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.ViewModelProviders
import com.logicipher.mvvm.BR
import com.logicipher.mvvm.R
import com.logicipher.mvvm.ViewModelProviderFactory
import com.logicipher.mvvm.databinding.ActivityMainBinding
import com.logicipher.mvvm.ui.base.BaseActivity
import javax.inject.Inject
/**
* Created by Shamji N S on 21-08-2020.
*/
class MainActivity : BaseActivity<ActivityMainBinding?, MainViewModel<MainNavigator>?>(),
MainNavigator /*, HasSupportFragmentInjector*/ {
/* #Inject
DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;*/
#set:Inject
internal var factory: ViewModelProviderFactory? = null
var mViewModel: MainViewModel<MainNavigator>? = null
var mBinding: ActivityMainBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "onCreate: ")
mBinding = getViewDataBinding()
mViewModel?.setNavigator(this)
}
override fun getBindingVariable(): Int {
return BR.viewModel
}
override fun getLayoutId(): Int {
return R.layout.activity_main
}
override fun getViewModel(): MainViewModel<MainNavigator>? {
mViewModel =
ViewModelProviders.of(this, factory).get<MainViewModel<MainNavigator>>(MainViewModel<MainNavigator>::class.java)
return mViewModel
}
/*
#Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return fragmentDispatchingAndroidInjector;
}
*/
companion object {
private const val TAG = "MainActivity"
}
}
ViewModelProviderFactory
package com.logicipher.mvvm
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider.NewInstanceFactory
import com.logicipher.mvvm.data.DataManager
import com.logicipher.mvvm.ui.main.MainNavigator
import com.logicipher.mvvm.ui.main.MainViewModel
import com.logicipher.mvvm.utils.rx.SchedulerProvider
import java.lang.IllegalArgumentException
import javax.inject.Inject
/**
* Created by Shamji N S on 25-08-2020.
*/
public class ViewModelProviderFactory #Inject constructor(
dataManager: DataManager,
schedulerProvider: SchedulerProvider
) : NewInstanceFactory() {
private val dataManager: DataManager
private val schedulerProvider: SchedulerProvider
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return super.create(modelClass)
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
return MainViewModel<T>(dataManager, schedulerProvider) as T
}
throw IllegalArgumentException("Unknown view model" + modelClass.name)
}
init {
this.dataManager = dataManager
this.schedulerProvider = schedulerProvider
}
}
Your BaseViewModel is abstract, you cannot create an object or an instance of an abstract class.
After you create a ViewModelFactory the way to create (or obtain) an instance of the ViewModel is this:
val viewModel = ViewModelProvider(this, viewModelFactory).get(ViewModelName::class.java)
However, I would consider using Kotlin Property Delegate from here which can help reduce the code.

Android Paging library, Recyclerview, Retrofit, MVVM

I'm trying to build an app with Android Architecture Components. I'm using TMDB API in my app. In my app, a user searches for a movie or series and gets the result. I've achieved this but I want to get all the pages from API with Paging library. (Endless Recyclerview) I've looked at several tutorials but I didn't get what I wanted. Please help me, I'm new with this Android Architecture Components. Thank you in advance.
The API result:
RecyclerViewMovieAdapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.martiandeveloper.muuvi.R
import com.martiandeveloper.muuvi.model.Movie
import kotlinx.android.synthetic.main.recyclerview_movie_item.view.*
class RecyclerViewMovieAdapter(private val movieList: ArrayList<Movie>) :
RecyclerView.Adapter<RecyclerViewMovieAdapter.RecyclerViewMovieViewHolder>() {
lateinit var context: Context
class RecyclerViewMovieViewHolder(var view: View) : RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RecyclerViewMovieViewHolder {
context = parent.context
val view = LayoutInflater.from(context)
.inflate(R.layout.recyclerview_movie_item, parent, false)
return RecyclerViewMovieViewHolder(view)
}
override fun getItemCount(): Int {
return movieList.size
}
override fun onBindViewHolder(holder: RecyclerViewMovieViewHolder, position: Int) {
if (movieList[position].mediaType != "person") {
if (movieList[position].mediaType == "tv") {
val title =
movieList[position].originalName + " (" + movieList[position].firstAirDate?.split(
"-"
)?.get(0) + ")"
holder.view.recyclerview_movie_item_titleMTV.text = title
} else {
val title =
movieList[position].title + " (" + movieList[position].releaseDate?.split("-")
?.get(0) + ")"
holder.view.recyclerview_movie_item_titleMTV.text = title
}
holder.view.recyclerview_movie_item_voteAverageMTV.text =
movieList[position].voteAverage.toString()
Glide.with(context)
.load("https://image.tmdb.org/t/p/w300${movieList[position].posterPath}")
.placeholder(R.drawable.logo1)
.centerCrop()
.into(holder.view.recyclerview_movie_item_posterIV)
}
}
fun updateMovieList(newMovieList: List<Movie>) {
movieList.clear()
movieList.addAll(newMovieList)
notifyDataSetChanged()
}
}
Movie.kt
import com.google.gson.annotations.SerializedName
data class Movie(
#SerializedName("original_name")
val originalName: String?,
#SerializedName("genre_ids")
val genreIds: List<Int>?,
#SerializedName("media_type")
val mediaType: String?,
#SerializedName("name")
val name: String?,
#SerializedName("origin_country")
val originCountry: List<String>?,
#SerializedName("first_air_date")
val firstAirDate: String?,
#SerializedName("original_language")
val originalLanguage: String?,
#SerializedName("id")
val id: Int?,
#SerializedName("vote_average")
val voteAverage: Float?,
#SerializedName("overview")
val overview: String?,
#SerializedName("poster_path")
val posterPath: String?,
#SerializedName("title")
val title: String?,
#SerializedName("release_date")
val releaseDate: String?,
#SerializedName("original_title")
val originalTitle: String?)
MovieResult.kt
import com.google.gson.annotations.SerializedName
data class MovieResult(
#SerializedName("page")
val page: Int?,
#SerializedName("total_results")
val totalResults: Int?,
#SerializedName("total_pages")
val totalPages: Int?,
#SerializedName("results")
val results: ArrayList<Movie>?
)
MovieApi.kt
import com.martiandeveloper.muuvi.model.MovieResult
import io.reactivex.Single
import retrofit2.http.GET
import retrofit2.http.Query
interface MovieApi {
#GET("search/multi")
fun getMovie(
#Query("api_key") apiKey: String,
#Query("query") movie: String,
#Query("page") page: Int
): Single<MovieResult>
}
MovieService.kt
import com.martiandeveloper.muuvi.model.MovieResult
import io.reactivex.Single
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
const val API_KEY = "fb640042b4bc08f6f7f65fbd8573f2a9"
const val BASE_URL = "https://api.themoviedb.org/3/"
// https://api.themoviedb.org/3/search/multi?api_key=my_api_key&query=break&page=1
// https://image.tmdb.org/t/p/w342/or06FN3Dka5tukK1e9sl16pB3iy.jpg
class MovieService {
private val api =
Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build()
.create(MovieApi::class.java)
fun getData(movie: String, page: Int): Single<MovieResult> {
return api.getMovie(API_KEY, movie, page)
}
}
AddFragment.kt
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import com.martiandeveloper.muuvi.R
import com.martiandeveloper.muuvi.adapter.RecyclerViewMovieAdapter
import com.martiandeveloper.muuvi.databinding.FragmentAddBinding
import com.martiandeveloper.muuvi.viewmodel.AddViewModel
import kotlinx.android.synthetic.main.fragment_add.*
class AddFragment : Fragment(), View.OnClickListener {
private lateinit var vm: AddViewModel
private val adapter = RecyclerViewMovieAdapter(arrayListOf())
private lateinit var binding: FragmentAddBinding
private lateinit var layoutManager: LinearLayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
vm = activity?.run {
ViewModelProviders.of(this)[AddViewModel::class.java]
} ?: throw Exception("Invalid activity")
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding =
DataBindingUtil.inflate(inflater, R.layout.fragment_add, container, false)
binding.addViewModel = vm
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initUI()
}
private fun initUI() {
setRecyclerView()
observe()
setProgress(isRecyclerViewGone = false, isProgressLLViewGone = true)
binding.isClearIVGone = true
setListeners()
fragment_add_movieSeriesET.requestFocus()
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(fragment_add_movieSeriesET, InputMethodManager.SHOW_IMPLICIT)
}
private fun setRecyclerView() {
layoutManager = LinearLayoutManager(context)
fragment_add_mainRV.layoutManager = layoutManager
fragment_add_mainRV.adapter = adapter
}
private fun observe() {
vm.movieList.observe(viewLifecycleOwner, Observer { movieList ->
movieList?.let {
adapter.updateMovieList(it)
}
})
vm.isError.observe(viewLifecycleOwner, Observer { isError ->
isError?.let {
setProgress(isRecyclerViewGone = false, isProgressLLViewGone = true)
if (it) {
setToast(resources.getString(R.string.something_went_wrong_please_try_again_later))
}
}
})
vm.isLoading.observe(viewLifecycleOwner, Observer { isLoading ->
isLoading?.let {
if (it) {
setProgress(isRecyclerViewGone = true, isProgressLLViewGone = false)
} else {
setProgress(isRecyclerViewGone = false, isProgressLLViewGone = true)
}
}
})
vm.movieSeriesETContent.observe(viewLifecycleOwner, Observer {
if (it.isNotEmpty()) {
vm.refreshData(it, 1)
binding.isClearIVGone = false
} else {
adapter.updateMovieList(arrayListOf())
binding.isClearIVGone = true
}
})
}
private fun setProgress(isRecyclerViewGone: Boolean, isProgressLLViewGone: Boolean) {
if (vm.movieSeriesETContent.value != null) {
val text =
"${resources.getString(R.string.searching_for)} \"${vm.movieSeriesETContent.value}\"..."
binding.searchingFor = text
}
binding.isMainRVGone = isRecyclerViewGone
binding.isProgressLLGone = isProgressLLViewGone
}
private fun setToast(text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
private fun setListeners() {
fragment_add_clearIV.setOnClickListener(this)
}
override fun onClick(v: View?) {
if (v != null) {
when (v.id) {
R.id.fragment_add_clearIV -> fragment_add_movieSeriesET.text.clear()
}
}
}
}
AddViewModel.kt
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.martiandeveloper.muuvi.model.MovieResult
import com.martiandeveloper.muuvi.model.Movie
import com.martiandeveloper.muuvi.service.MovieService
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.observers.DisposableSingleObserver
import io.reactivex.schedulers.Schedulers
class AddViewModel : ViewModel() {
private val movieService = MovieService()
private val disposable = CompositeDisposable()
val movieList = MutableLiveData<ArrayList<Movie>>()
val isError = MutableLiveData<Boolean>()
val isLoading = MutableLiveData<Boolean>()
val movieSeriesETContent = MutableLiveData<String>()
fun refreshData(movie: String, page: Int) {
isLoading.value = true
disposable.add(
movieService.getData(movie, page).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<MovieResult>() {
override fun onSuccess(t: MovieResult) {
movieList.value = t.results
isError.value = false
isLoading.value = false
}
override fun onError(e: Throwable) {
isError.value = true
isLoading.value = false
}
})
)
}
}

Categories

Resources