Retrieve data from api and put into room - kotlin

I'm facing this error when putting data into room from my api:
java.lang.RuntimeException: Unable to invoke no-args constructor for
retrofit2.Call<com.example.youbank.models.Customer>. Registering an
InstanceCreator with Gson for this type may fix this problem.
I have looked up this issue and tried multiple things to fix it but, i think there is something else wrong, which probably comes down to my lack of knowledge on this subject.
Sorry for the big copypaste of my code but i don't know where to fix this problem so im just including what i think is needed.
HomeScreenFragment:
class HomeScreenMotionFragment: Fragment(), CoroutineScope {
private var job: Job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + job
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
private var _binding: FragmentHomeScreenMotionBinding? = null
private val binding get() = _binding!!
private val vm: CustomerViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launch {
vm.addCustomerToRoomDB(14)
}
}
CustomerViewModel:
class CustomerViewModel(application: Application): AndroidViewModel(application) {
val readCustomer: LiveData<RoomCustomer>
val readAccount: LiveData<List<RoomAccount>>
val readCard: LiveData<List<RoomCard>>
private val customerRepo: CustomerRepository
private val accountRepo: AccountRepository
private val cardRepo: CardRepository
var cus: Customer
var a = listOf<Account>()
var cards = listOf<Card>()
init {
cus = Customer()
val customerDao = CustomerDatabase.getDatabase(application).customerDao()
customerRepo = CustomerRepository(customerDao)
readCustomer = customerRepo.readCustomer
val accountDao = CustomerDatabase.getDatabase(application).accountDao()
accountRepo = AccountRepository(accountDao)
readAccount = accountRepo.readAccounts
val cardDao = CustomerDatabase.getDatabase(application).cardDao()
cardRepo = CardRepository(cardDao)
readCard = cardRepo.readCards
}
suspend fun addCustomerToRoomDB(id: Int) {
val service: CustomerService = ApiService.buildService(CustomerService::class.java)
val req: Call<Customer> = service.getCustomerById(id)
req.enqueue(object: Callback<Customer> {
override fun onResponse(call: Call<Customer>, response: Response<Customer>) {
cus = response.body()!!
a = response.body()!!.accounts
cards = response.body()!!.accounts[0].cards
}
override fun onFailure(call: Call<Customer>, t: Throwable) {
Log.d("get customer failed", t.cause.toString())
}
})
val roomCustomer = RoomCustomer(
0, cus.customerId, cus.fullName, cus.phone, cus.address, cus.birthday.toString(), cus.email, cus.password)
val roomAccount = RoomAccount(0, a[0].accountId, a[0].accountNumber, a[0].accountType, a[0].balance)
val roomCard = RoomCard(
0, cards[0].cardId, cards[0].cardNumber, cards[0].ccv, cards[0].expirationDate, cards[0].cardType,
cards[0].cardStatus)
// Adding customer to roomdatabase
customerRepo.addCustomer(roomCustomer)
accountRepo.addAccounts(roomAccount)
cardRepo.addCards(roomCard)
}
}
CustomerService:
interface CustomerService {
#GET("Customers/{id}")
suspend fun getCustomerById(#Path("id") id: Int): Call<Customer>
}
My models:
class Customer {
var customerId: Int = -1
var fullName: String = ""
var phone: String = ""
var address: String = ""
var birthday: Date? = null
var email: String = ""
var password: String = ""
lateinit var accounts: List<Account>
}
class Account {
var accountId: Int = -1
var accountNumber: String = generateAccNumber()
lateinit var accountType: AccountType
var balance: Double = 0.0
lateinit var cards: List<Card>
}
class Card {
var cardId: Int = -1
var cardNumber: Int = -1
var ccv: Int = -1
lateinit var expirationDate: String
lateinit var cardType: CardType
lateinit var cardStatus: CardStatus
}
My repositories:
class CustomerRepository(private val customerDao: CustomerDao) {
val readCustomer: LiveData<RoomCustomer> = customerDao.getCustomer()
fun addCustomer(c: RoomCustomer) {
customerDao.addCustomer(c)
}
}
class AccountRepository (private val accountDao: AccountDao) {
val readAccounts: LiveData<List<RoomAccount>> = accountDao.getAccounts()
fun addAccounts(a: RoomAccount) {
accountDao.addAccount(a)
}
}
class CardRepository(private val cardDao: CardDao) {
val readCards: LiveData<List<RoomCard>> = cardDao.getCards()
fun addCards(c: RoomCard) {
cardDao.addCards(c)
}
}
My daos:
#Dao
interface CustomerDao {
#Query("SELECT * FROM customer_table")
fun getCustomer(): LiveData<RoomCustomer>
#Insert
fun addCustomer(c: RoomCustomer)
#Delete
fun deleteCustomer(c: RoomCustomer)
}
#Dao
interface AccountDao {
#Query("SELECT * FROM accounts_table")
fun getAccounts(): LiveData<List<RoomAccount>>
#Insert
fun addAccount(a: RoomAccount)
#Delete
fun deleteAccount(a: RoomAccount)
}
#Dao
interface CardDao {
#Query("SELECT * FROM cards_table")
fun getCards(): LiveData<List<RoomCard>>
#Insert
fun addCards(c: RoomCard)
#Delete
fun deleteCard(c: RoomCard)
}
My room models:
#Entity(tableName = "customer_table")
data class RoomCustomer(
#PrimaryKey(autoGenerate = true)
val CID: Int,
val customerId: Int,
val fullName: String,
val phone: String,
val address: String,
val birthday: String,
val email: String,
val password: String
)
#Entity(tableName = "accounts_table")
data class RoomAccount(
#PrimaryKey(autoGenerate = true)
val AID: Int,
val accountId: Int,
val accountNumber: String,
val accountType: AccountType,
val balance: Double
)
#Entity(tableName = "cards_table")
data class RoomCard(
#PrimaryKey(autoGenerate = true)
val CID: Int,
val cardId: Int,
val cardNumber: Int,
val ccv: Int,
val expirationDate: String,
val cardType: CardType,
val cardStatus: CardStatus
)

Related

How to implement database to BroadcastReceiver (Alarm)

I'm making a app, that will notificate when time of task expiring. For creating notifications I'm using BroadcastReceiver.
There is my code of class AlarmReceiver
class AlarmReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.e("yeah", "yeah")
val dao = TasksDatabase.getInstance(context = context!!.applicationContext).dao
//val repository = TasksRepositoryImpl(dao)
var list: List<Task> = listOf()
runBlocking {
launch {
Log.e("sad", "dsa")
list = dao.getTasks().flattenToList()
Log.e("list", list.toString())
}
}
val builder = NotificationCompat.Builder(context, "DoneApp")
.setSmallIcon(R.drawable.icon)
.setContentTitle("Done App")
.setContentText("")
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
val notificationManager = NotificationManagerCompat.from(context)
if(list.isNotEmpty()){
for(i in list){
Log.e("string", i.toString())
}
} else {
Log.e("log---", "---------------------------------")
}
notificationManager.notify(123, builder.build())
}
suspend fun <T> Flow<List<T>>.flattenToList() = flatMapConcat { it.asFlow() }.toList()
}
But it stops on list = dao.getTasks().flattenToList()
There is TasksDao
#Dao
interface TasksDAO {
#Insert
suspend fun insertTask(task: Task)
#Query("UPDATE task_table SET checked = :checked WHERE id = :id")
suspend fun checkTask(id: Int, checked: Boolean)
#Query("UPDATE task_table SET text = :text WHERE id = :id")
suspend fun textTask(id: Int, text: String)
#Query("DELETE FROM task_table WHERE id = :id")
suspend fun deleteTask(id: Int)
#Query("SELECT * FROM task_table")
fun getTasks(): Flow<List<Task>>
}

Unresolved reference: myViewHolder after switching to View Binding

After switching from removing kotlin_extensions and switching to view binding, I received a "Unresolved reference: myViewHolder" in my onBindViewHolder method and when I replace "myViewHolder" with "holder", it then gives me a "Unresolved reference: bind". How do I resolve this.
MyAdapter
class MyAdapter(private val context: Context, private val mHelper : TaskDbHelper) : RecyclerView.Adapter<MyAdapter.MyViewHolder>(),
SwipeAndDragHelper.ActionCompletionContract {
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun binding() {
}
}
private var touchHelper: ItemTouchHelper? = null
private var list = mutableListOf<MyObject>()
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
initList()
super.onAttachedToRecyclerView(recyclerView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(CellCardsBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
#RequiresApi(Build.VERSION_CODES.P)
#SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
myViewHolder.bind(myObject)
val activity: Activity = context as Activity
holder.binding.text.setOnClickListener{
activity.launchActivity<AddNoteActivity>(42) {
putExtra("PositionInList", position.toString())
putExtra("TaskTitle", myObject.title)
putExtra("TaskText", myObject.text)
}
}
activity.findViewById<RecyclerView>(R.id.recyclerView).setOnTouchListener { _, event ->
when (event.actionMasked) {
MotionEvent.ACTION_UP -> {
updateNotesPositionInDb()
false
}
else -> {
false
}
}
}
holder.binding.title.setOnTouchListener { _, event ->
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
touchHelper!!.startDrag(holder)
false
}
else -> {
false
}
}
}
}
private fun initList() {
list.clear()
val db = mHelper.readableDatabase
val cursor = db.query(
TaskContract.TaskEntry.TABLE,
arrayOf(
TaskContract.TaskEntry.ID,
TaskContract.TaskEntry.COL_TASK_TITLE,
TaskContract.TaskEntry.COL_TASK_TEXT,
TaskContract.TaskEntry.COL_TASK_DATE),null, null, null, null, TaskContract.TaskEntry.ID)
while (cursor.moveToNext()) {
val id = cursor.getColumnIndex(TaskContract.TaskEntry.ID)
val idTitle = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TITLE)
val idText = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TEXT)
val idDate = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_DATE)
list.add(MyObject(cursor.getString(id), cursor.getString(idTitle), cursor.getString(idText), cursor.getString(idDate)))
}
notifyDataSetChanged()
cursor.close()
db.close()
}
override fun getItemCount(): Int {
return list.size
}
override fun onViewMoved(oldPosition: Int, newPosition: Int) {
val target = list[oldPosition]
list.removeAt(oldPosition)
list.add(newPosition, target)
notifyItemMoved(oldPosition, newPosition)
}
override fun onViewSwiped(position: Int) {
deleteTask(list[position].ID)
list.removeAt(position)
notifyItemRemoved(position)
updateNotesPositionInDb()
}
fun setTouchHelper(touchHelper: ItemTouchHelper) {
this.touchHelper = touchHelper
}
fun addTask(taskTitle : String, taskText: String) {
val values = ContentValues()
val sdf = SimpleDateFormat("dd/MM/yyyy/", Locale.US)
val date = sdf.format(Date())
values.put(TaskContract.TaskEntry.ID, list.size)
values.put(TaskContract.TaskEntry.COL_TASK_TITLE, taskTitle)
values.put(TaskContract.TaskEntry.COL_TASK_TEXT, taskText)
values.put(TaskContract.TaskEntry.COL_TASK_DATE, date)
val db = mHelper.readableDatabase
db.insertWithOnConflict(TaskContract.TaskEntry.TABLE,
null,
values,
SQLiteDatabase.CONFLICT_REPLACE)
db.close()
list.add(MyObject(list.size.toString(), taskTitle, taskText, date))
notifyItemInserted(list.size)
}
fun addTask() {
val test: Activity = context as Activity
test.launchActivity<AddNoteActivity>(42) {
/* putExtra("user", "854")
p utExtra("user2", "46850") */
}
}
private fun deleteTask(taskId: String) {
val db = mHelper.readableDatabase
db.delete(TaskContract.TaskEntry.TABLE,
"id=$taskId", null)
db.close()
}
fun modifyTask(taskPosition: String, taskTitle: String, taskText: String) {
val target = list[taskPosition.toInt()]
target.title = taskTitle
target.text = taskText
val values = ContentValues()
val sdf = SimpleDateFormat("dd/MM/yyyy/", Locale.US)
val date = sdf.format(Date())
values.put(TaskContract.TaskEntry.ID, taskPosition)
values.put(TaskContract.TaskEntry.COL_TASK_TITLE, taskTitle)
values.put(TaskContract.TaskEntry.COL_TASK_TEXT, taskText)
values.put(TaskContract.TaskEntry.COL_TASK_DATE, date)
val db = mHelper.readableDatabase
db.update(TaskContract.TaskEntry.TABLE,
values, TaskContract.TaskEntry.ID + "=" + target.ID, null)
db.close()
notifyItemChanged(taskPosition.toInt())
}
private fun updateNotesPositionInDb() {
val db = mHelper.readableDatabase
var i = 0
while (i < list.size) {
val values = ContentValues()
values.put(TaskContract.TaskEntry.ID, i)
db.update(TaskContract.TaskEntry.TABLE,
values, TaskContract.TaskEntry.ID + "=? AND " + TaskContract.TaskEntry.COL_TASK_TITLE + "=?", arrayOf(list[i].ID, list[i].title))
i++
}
db.close()
}
I've tried reading Android Studio's official documentation, but it cannot solve my specific problem.
in your class MyViewHolder you have method called binding and you need also to implement it and add paramter
shoud be
class MyViewHolder(private val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun bind(data:MyObject) {
binding.yourView=data.title ...
}
}
in onBindViewHolder
..
holder.bind(myObject)
After switching from removing kotlin_extensions and switching to view binding, I received a "Unresolved reference: myViewHolder" in my onBindViewHolder method
Well, your onBindViewHolder method is passing a variable called holder and you're trying to use a variable called myViewHolder, so that seems like a problem.
// --------------------this-----v
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
// v--- doesn't match this
myViewHolder.bind(myObject)
and when I replace "myViewHolder" with "holder", it then gives me a "Unresolved reference: bind". How do I resolve this.
Your MyViewHolder class has a method called binding that takes no arguments. There is no bind method that takes a "myObject".
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun binding() {
}
}
Edit
You should pass an instance of the data class
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun bind(object: MyObject) {
// Set variables on binding
}
}
Then pass an instance from your list via onBindViewHolder:
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
holder.bind(myObject)
Please check this blog post for more.
I got my answer.
class MyAdapter(private val context: Context, private val mHelper : TaskDbHelper) : RecyclerView.Adapter<MyAdapter.MyViewHolder>(),
SwipeAndDragHelper.ActionCompletionContract {
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
private val titleView: TextView = itemView.findViewById<View>(R.id.title) as TextView
val textView: TextView = itemView.findViewById<View>(R.id.text) as TextView
private val dateTextView: TextView = itemView.findViewById<View>(R.id.date) as TextView
fun binding (myObject: MyObject) {
titleView.text = myObject.title
textView.text = myObject.text
dateTextView.text = myObject.date
}
}
I simply initialised the view I wanted to reference in my layout and called them in the binding() function.

How to compare one table with name

Hi i have a searchbar on maps and I want it to compare when I search for a city if it is in my City Table
I wrote a Dao and a method for this but every time when I write a wrote a city that doesn't exist the apps crashed`
My data serviceinterface
fun getCitiesFindbyName(name: String, getName: GetName)
fun startBtn(view: View){
lateinit var location: String
val dataService: DataService = (requireActivity().application as MyApp).dataService
val searchView = view.findViewById<SearchView>(R.id.sv_location)
location = searchView.query.toString()
dataService.getCitiesFindbyName(location, this)
the callback
interface GetName {
fun onFinish( city: City)
}
and the dao
#Query ("SELECT * FROM City WHERE name = :name")
fun getCitybyId(name:String):City
and the map view fragment
fun startBtn(view: View){
lateinit var location: String
val dataService: DataService = (requireActivity().application as MyApp).dataService
val searchView = view.findViewById<SearchView>(R.id.sv_location)
location = searchView.query.toString()
dataService.getCitiesFindbyName(location, this)
override fun onFinish(city:City ) {
val dataService: DataService = (requireActivity().application as MyApp).dataService
if (city.name.isNotEmpty()){
val latLng = LatLng(city.coord.lat!!, city.coord.lon!!)
val lat= (city.coord.lat)
val long = (city.coord.lon)
val name =(city.name)
nMap.addMarker(MarkerOptions().position(latLng).title(city.name))
retrofitResponse2(lat,long,city.name)
nMap.animateCamera(CameraUpdateFactory.newLatLng(latLng))
dataService.getCitiesFindbyName(name, this)
}
else{
Toast.makeText(requireContext(), "There is no info about this city", Toast.LENGTH_LONG).show()
}
}
}

Cannot access elements with a specific attribute from my dao (Android Room)

I am making a toDoList app with multiple users. I have two entities. One is User and the other one is Task
#Entity(tableName = "tasks")
data class Task(
#PrimaryKey(autoGenerate = true)
val id : Int,
val taskUserId : Int,
val taskName : String,
val taskDescription : String,
var taskPriority : Int)
#Entity(tableName = "users")
data class User(
#PrimaryKey
val userId : Int,
val userName : String,
val password : String)
As you can see the two entities need to have one thing in common and that would be the users id so I could query for tasks based on the users id. This is what my dao looks like:
#Dao
interface UserDao {
#Query("SELECT * FROM users")
fun getAllUsers() : LiveData<List<User>>
#Query("SELECT * FROM tasks WHERE taskUserId LIKE :userId ORDER BY taskPriority DESC")
fun getAllUsersTasks(userId : Int) : LiveData<List<Task>>
#Insert
suspend fun saveUser(user : User)
#Insert
suspend fun saveTask(task : Task)
}
And here are the other model elements:
class TaskRepository(private val userDao: UserDao) {
var userId : Int = 0
val allTasks = userDao.getAllUsersTasks(userId)
val allUsers : LiveData<List<User>> = userDao.getAllUsers()
suspend fun saveUser(user : User){
userDao.saveUser(user)
}
suspend fun saveTask(task : Task){
userDao.saveTask(task)
}
}
And the ViewModel:
class MainViewModel(application: Application) : AndroidViewModel(application) {
private val taskRepository : TaskRepository
val allTasks : LiveData<List<Task>>
val allUsers : LiveData<List<User>>
private var userId : Int = 0
init {
val taskDao = UserTasksDatabase.getDatabase(application)!!.userDao()
taskRepository = TaskRepository(taskDao)
allTasks = taskRepository.allTasks
allUsers = taskRepository.allUsers
taskRepository.userId = this.userId
}
fun saveUser(user: User) = viewModelScope.launch(Dispatchers.IO){
taskRepository.saveUser(user)
}
fun saveTask(task: Task) = viewModelScope.launch(Dispatchers.IO){
taskRepository.saveTask(task)
}
fun setUserId(id : Int){
this.userId = id
}
fun getUserId() : Int{
return this.userId
}
}
When the user pushes the login or the sign up button I set the user's id in the viewModel
logInButton.setOnClickListener {
val userName: String = userNameEditText.text.toString()
val password: String = passwordEditText.text.toString()
val id: Int = IdMaker.generateId(userName)
val user = User(id, userName, password)
if(listOfAllUsers.contains(user)){
sharedViewModel.setUserId(id)
Toast.makeText(requireContext(), "Welcome $userName", Toast.LENGTH_LONG).show()
fragmentManager!!.beginTransaction().apply {
replace(R.id.fl_activity_main, MainFragment())
commit()
}
}else{
Toast.makeText(requireContext(), "The user already exists", Toast.LENGTH_LONG).show()
}
}
But when i try to access the users in the main fragment and set them on my recyclerview I get nothing. And I can't really see what am I missing.
sharedViewModel.allTasks.observe(this, Observer {tasks ->
tasks.let { adapter.setTasks(it) }
})
I'm sorry if a question is a bit simple but I hope I will gain some insight in what am I missing.
Side note: The recyclerview is set up properly and tested.
I think you are not setting the TaskRepository.userId correctly.
class MainViewModel(application: Application) : AndroidViewModel(application) {
private val taskRepository : TaskRepository
private var userId : Int = 0
init {
...
// init is called only once at the start of this viewModel's instance creation
// Here, you set your repository's userId to 0
taskRepository.userId = this.userId
}
}
At the instance creation of your MainViewModel, you set the userId of its TaskRepository instance to 0, which in your following codes is not changed anymore. Upon your login button click, you do call:
sharedViewModel.setUserId(id)
but the function alters only the viewModel's userId property, not the userId property of your TaskRepository instance. So try:
class MainViewModel(application: Application) : AndroidViewModel(application) {
private lateinit var taskRepository : TaskRepository
val allTasks = taskRepository.allTasks
val allUsers = taskRepository.allUsers
// remove init {}
fun setUserId(id : Int){
// set up the repository only after userId is known
val taskDao = UserTasksDatabase.getDatabase(application)!!.userDao()
taskRepository = TaskRepository(taskDao, id)
}
}
And alter your TaskRepository class:
class TaskRepository(private val userDao: UserDao, private val userId: Int) {
val allTasks: LiveData<List<Task>>
get() = userDao.getAllUsersTasks(userId)
val allUsers: LiveData<List<User>>
get() = userDao.getAllUsers()
...
}

mutableListOf to Bundle (Kotlin)

I have a mutableLIst:
var books = mutableListOf<Book>()
model "Book" is:
data class Book(val title: String, val id: Int)
My code is:
button2.setOnClickListener{
val delFragment = DelFragment()
val booksforDel = Bundle()
booksforDel.putStringArrayList("books", books as ArrayList<String>)
delFragment.setArguments(booksforDel)
val manager = supportFragmentManager
delFragment.show(manager,"Delete Book")
}
in Fragment I try to get data:
val booksForDelete = getArguments()?.getStringArrayList("books")!!
And get Error:
java.lang.ArrayStoreException: source[0] of type com.example.http_example.model.Book cannot be stored in destination array of type java.lang.String[]
How send a data from mutableList "books" to Bundle in DialogFragment?
You can implement Parcelable interface:
data class Book(val title: String, val id: Int) : Parcelable {
constructor(source: Parcel) : this(
source.readString()!!,
source.readInt()
)
override fun describeContents() = 0
override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
writeString(title)
writeInt(id)
}
companion object {
#JvmField
val CREATOR: Parcelable.Creator<Book> = object : Parcelable.Creator<Book> {
override fun createFromParcel(source: Parcel): Book = Book(source)
override fun newArray(size: Int): Array<Book?> = arrayOfNulls(size)
}
}
}
And use it like the following:
var books = mutableListOf<Book>()
val booksforDel = Bundle()
booksforDel.putParcelableArray("books", books.toTypedArray())
Ann to retrieve books in a Fragment:
val booksForDelete = arguments?.getParcelableArray("books")