mutableListOf to Bundle (Kotlin) - 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")

Related

how can I convert my room entity to my data class in kotlin?

I have a data class that I pull from internet and I want to save room database but there is a problem like that.
It always gives an error like this, how can I overcome this problem?
my room entity class
#Entity(tableName = "ExchangeValues")
data class ExchangeEntity(
#ColumnInfo(name = "base_code") val base_code: String,
#ColumnInfo(name = "conversion_rates") val conversion_rates: ConversionRates,
#ColumnInfo(name = "result") val result: String,
#PrimaryKey(autoGenerate = true) val uid:Int?=null
)
my dao
#Dao
interface ExchangeDao {
#Query("SELECT * FROM ExchangeValues")
suspend fun getAll() : List<ExchangeEntity>
#Query("UPDATE ExchangeValues SET base_code=:base_code,conversion_rates=:conversion_rates , result=:result")
suspend fun update(base_code:String,conversion_rates:ConversionRates,result:String)
}
my exchange data class
#Serializable
data class Exchange(
val base_code: String,
val conversion_rates: ConversionRates,
val documentation: String,
val result: String,
val terms_of_use: String,
val time_last_update_unix: Int,
val time_last_update_utc: String,
val time_next_update_unix: Int,
val time_next_update_utc: String
) {
fun toEntity() = ExchangeEntity(
base_code = base_code,
conversion_rates = conversion_rates,
result = result
)
}
#Serializable
data class ConversionRates(
val conversionRates : Map<String,Double>
)
I cant use toEntity function in getAll()
exchangeRepositoryImpl
class ExchangeRepositoryImpl #Inject constructor(
private val dao:ExchangeDao
) : ExchangeRepository{
override suspend fun getAll() : Flow<List<Exchange>> {
return flow {
emit(dao.getAll())
}
}
override suspend fun update(exchange: Exchange) {
dao.update(exchange.base_code,exchange.result,exchange.conversion_rates)
}
}
my exchange converter
class ExchangeConverter {
#TypeConverter
fun fromSource(conversionRates: ConversionRates) : String{
val gson = Gson()
return gson.toJson(conversionRates)
}
#TypeConverter
fun toSource(json: String): ConversionRates {
val gson = Gson()
val typeToken = object : TypeToken<List<ConversionRates>>() {}.type
return Gson().fromJson(json, typeToken)
}
}
I wrote a converter like this, but it might not be correct, I'm not so sure. How can I solve this problem?
Inside flow you have created call map function the call to toEntity() eg
flow{
emit (dao.getAll().map{it.toEntity()})
}
Well your flow returns a flow of
List<Exchange>
and your repo returns
List<ExchangeEntity>
and there's nothing in your code to map an ExchangeEntity to an Exchange.
So you need something like:
override suspend fun getAll() : Flow<List<Exchange>> {
return flow {
emit(dao.getAll().map{Exchange(base_code = it.baseCode)})// add in other fields on exchange constructor
}
}

typealias for global kotlinx.serialization not work

First I'll post the code
#OptIn(ExperimentalSerializationApi::class)
#Serializer(forClass = UUID::class)
object UUIDserializer : KSerializer<UUID> {
override fun deserialize(decoder: Decoder): UUID = UUID.fromString(decoder.decodeString())
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: UUID) {
encoder.encodeString(value.toString())
}
}
typealias SID = #Serializable(with = UUIDserializer::class) UUID
fun randomSid() = UUID.randomUUID() as SID
#Serializable
data class Example(val id:SID = randomSid())
class SerializeId {
#Test
fun nestedTypeUsage() {
val example = Example()
val string = Json.encodeToString(example)
println(string)
}
#Test
fun directTypeUsage () {
val hi = randomSid()
val string = Json.encodeToString(hi)
println(string)
}
}
nestedTypeUsage run and passes, but directTypeUsage fails.
Serializer for class 'UUID' is not found.
Mark the class as #Serializable or provide the serializer explicitly.
kotlinx.serialization.SerializationException: Serializer for class 'UUID' is not found
I can't apply the #Serializable annotation directly to a val or a function parameter.
almost immediately after posting this. I realized I can
#Test
fun directTypeUsage () {
val hi = randomSid()
val string = hi.toString()
println(string)
}

TypeConverter not working for android Room DB

So I am trying to save a list of string pairs into my database but having some issues with the TypeConverter, I tried following guides ans other SO posts but can't figure out whats wrong...
My Entity:
#Entity
data class Credential(
#PrimaryKey()
val id: String,
#ColumnInfo(name = "name")
val name: String,
#ColumnInfo(name = "url")
val url: String?,
#TypeConverters(ListPairTypeConverter::class)
#ColumnInfo(name = "fields")
val fields: List<Pair<String, String>>
)
My Type Converter:
class ListPairTypeConverter {
#TypeConverter
fun storedStringToPairList(value: String): List<Pair<String, String>> {
return value.split("~!!!!!~").map {
val vals = it.split("!~~~~~!")
Pair(vals[0], vals[1])
}
}
#TypeConverter
fun pairListToStoredString(pl: List<Pair<String, String>>): String {
return pl.joinToString(separator = "~!!!!!~") { it.first + "!~~~~~!" + it.second }
}
}
My Error:
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
private final java.util.List<kotlin.Pair<java.lang.String, java.lang.String>> fields = null;
^
You are adding type converter at wrong place. instead of
#TypeConverters(ListPairTypeConverter::class)
#ColumnInfo(name = "fields")
val fields: List<Pair<String, String>>
You need to add at here
#Database(entities = [User::class], version = 1)
#TypeConverters(ListPairTypeConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}

Entity classes have some issues while using retrofit

I am getting this error while getting a response from an API.
Sometimes, it works fine but 8/10 times it crashes and shows me this error
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
at com.example.newsapi.db.Entities.Source.hashCode(Unknown Source:2)
at com.example.newsapi.db.Entities.Article.hashCode(Unknown Source:50)
This is the Article entity
#Entity(tableName = "Article_Table")
data class Article(
#PrimaryKey(autoGenerate = true)
val id: Int?=null,
val author: String,
val content: String,
val description: String,
val publishedAt: String,
val source: Source,
val title: String,
val url: String,
val urlToImage: String
): Serializable
This is the Source class declared in the Article class
data class Source(
val id: String,
val name: String
)
Here is where I'm trying to put that Article object inside a Bundle
setOnClickListener {
val bundle = Bundle().apply {
putSerializable("article", currentArticle)
}
when (from) {
"BreakingNewsFragment" -> findNavController().navigate(R.id.action_breakingNewsFragment_to_articleFragment, bundle)
"SearchNewsFragment" -> findNavController().navigate(R.id.action_searchNewsFragment_to_articleFragment, bundle)
"SavedNewsFragment" -> findNavController().navigate(R.id.action_savedNewsFragment_to_articleFragment, bundle)
}
}
Here is the API call function
#GET("v2/top-headlines")
suspend fun getBreakingNews(
#Query("country")
countryCode: String = "us",
#Query("page")
pageNumber : Int = 1,
#Query("apiKey")
apiKey: String = API_KEY
): Response<NewsResponse>
calling that function from the repository
suspend fun getBreakingNews(countryCode: String, pageNumber: Int): Response<NewsResponse> {
return RetrofitInstance.api.getBreakingNews(
countryCode = countryCode,
pageNumber = pageNumber
)
}
calling that repository function from the viewmodel
fun getBreakingNews(countryCode: String){
viewModelScope.launch(Dispatchers.IO) {
breakingNews.postValue(Resource.Loading())
val response = articleRepository.getBreakingNews(countryCode, breakingNewsPage)
breakingNews.postValue(handleBreakingNewsResponse(response))
}
}
Here is the NewsResponse Class
data class NewsResponse(
val articles: List<Article>,
val status: String,
val totalResults: Int
)

Retrieve data from api and put into room

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
)