Entity classes have some issues while using retrofit - kotlin

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
)

Related

Identifying relationships in Kotlin

Are nested classes a good way to model identifying relationships in Kotlin?
Requirements:
an issue can not be without a serno
a revocation can not be without an issue
an affirmation can not be without a revocation
Looks quite verbose:
class Have {
inner class Serno(val value: String) {
override fun toString(): String = "serno: $value"
fun nothing () = this#Have
inner class Issue(val value: String) {
override fun toString(): String = "issue: $value (${serno()})"
fun serno () = this#Serno
inner class Revocation(val value: String) {
override fun toString(): String = "revocation: $value (${issue()})"
fun issue () = this#Issue
inner class Affirmation(val value: String) {
override fun toString(): String = "affirmation: $value (${revocation()})"
fun revocation () = this#Revocation
}
}
}
}
}
val serno: Have.Serno = Have().Serno("123")
val issue: Have.Serno.Issue = serno.Issue("SUP-1")
val revocation: Have.Serno.Issue.Revocation = issue.Revocation("2020")
val affirmation: Have.Serno.Issue.Revocation.Affirmation = revocation.Affirmation("2022")
println(serno)
println(issue)
println(revocation)
println(affirmation)
println(serno == affirmation.revocation().issue().serno())
Is there a simpler way to achieve the same?
This would usually be achieved with simple non-null properties:
class Serno(val value: String) {
override fun toString(): String = "serno: $value"
}
class Issue(val value: String, val serno: Serno) {
override fun toString(): String = "issue: $value ($serno)"
}
class Revocation(val value: String, val issue: Issue) {
override fun toString(): String = "revocation: $value ($issue)"
}
class Affirmation(val value: String, val revocation: Revocation) {
override fun toString(): String = "affirmation: $value ($revocation)"
}
val serno = Serno("123")
val issue = Issue("SUP-1", serno)
val revocation = Revocation("2020", issue)
val affirmation = Affirmation("2022", revocation)
And if you're not strict on the toString format, you could even use the built-in toString of data classes, and simplify further:
data class Serno(val value: String)
data class Issue(val value: String, val serno: Serno)
data class Revocation(val value: String, val issue: Issue)
data class Affirmation(val value: String, val revocation: Revocation)

Why retrofit returns null in response body?

Get null response results in response body. Using retrofit, dagger and repository. Why get null in response I don't know. My model seem right. Whats the problem?
MainViewModel.kt
#HiltViewModel
class MainViewModel#Inject constructor(
private val repository: MovieRepository,
#ApplicationContext private val context: Context
) : ViewModel() {
val movieList = MutableLiveData<Resource<Movie>>()
fun getAllMovies(movieName: String) {
movieList.postValue(Resource.Loading())
viewModelScope.launch {
try {
if (hasInternetConnection(context)) {
val response = repository.getMovies(movieName, "ffe9123f")
movieList.postValue(Resource.Success(response.body()!!))
} else
movieList.postValue(Resource.Error("Internet yok"))
} catch (ex: Exception) {
when (ex) {
is IOException -> movieList.postValue(Resource.Error("Network Failure " + ex.localizedMessage))
else -> movieList.postValue(Resource.Error("Conversion Error"))
}
}
}
}
}
Resource.kt
sealed class Resource<T>(
val data: T? = null,
val message: String? = null
) {
class Success<T>(data: T): Resource<T>(data)
class Error<T>(message: String, data: T? = null): Resource<T>(data, message)
class Loading<T> : Resource<T>()
}
MovieRepository.kt
#Singleton
class MovieRepository #Inject constructor(private val movieAppService: MovieAppService) {
suspend fun getMovies(title: String, aKey: String): Response<Movie> = withContext(
Dispatchers.IO
) {
val movies = movieAppService.getMovies(title = title, aKey = aKey)
movies
}
Movie.kt
data class Movie(
val title: String,
val year: String,
val rated: String,
val released: String,
val runtime: String,
val genre: String,
val director: String,
val writer: String,
val actors: String,
val plot: String,
val language: String,
val country: String,
val awards: String,
val poster: String,
val ratings: List<Rating>,
val metascore: String,
val imdbRating: String,
val imdbVotes: String,
val imdbID: String,
val type: String,
val dvd: String,
val boxOffice: String,
val production: String,
val website: String,
val response: String
)
MovieAppService.kt
interface MovieAppService {
companion object {
const val ENDPOINT = "http://www.omdbapi.com/"
}
#GET(".")
suspend fun getMovies(#Query("t") title: String,#Query("apikey") aKey: String): Response<Movie>
}
{"Title":"A Beautiful Mind","Year":"2001","Rated":"PG-13","Released":"04 Jan 2002","Runtime":"135 min","Genre":"Biography, Drama","Director":"Ron Howard","Writer":"Akiva Goldsman, Sylvia Nasar","Actors":"Russell Crowe, Ed Harris, Jennifer Connelly","Plot":"After John Nash, a brilliant but asocial mathematician, accepts secret work in cryptography, his life takes a turn for the nightmarish.","Language":"English","Country":"United States","Awards":"Won 4 Oscars. 37 wins & 69 nominations total","Poster":"https://m.media-amazon.com/images/M/MV5BMzcwYWFkYzktZjAzNC00OGY1LWI4YTgtNzc5MzVjMDVmNjY0XkEyXkFqcGdeQXVyMTQxNzMzNDI#._V1_SX300.jpg","Ratings":[{"Source":"Internet Movie Database","Value":"8.2/10"},{"Source":"Rotten Tomatoes","Value":"74%"},{"Source":"Metacritic","Value":"72/100"}],"Metascore":"72","imdbRating":"8.2","imdbVotes":"908,920","imdbID":"tt0268978","Type":"movie","DVD":"25 Jun 2002","BoxOffice":"$170,742,341","Production":"N/A","Website":"N/A","Response":"True"}
Movie model is wrong. I didn't realize the starting letter is uppercase letter.
Movie.kt
data class Movie(
val Title: String,
val Year: String,
val Rated: String,
val Released: String,
val Runtime: String,
val Genre: String,
val Director: String,
val Writer: String,
val Actors: String,
val Plot: String,
val Language: String,
val Country: String,
val Awards: String,
val Poster: String,
val Ratings: List<Rating>,
val Metascore: String,
val imdbRating: String,
val imdbVotes: String,
val imdbID: String,
val Type: String,
val Dvd: String,
val boxOffice: String,
val Production: String,
val Website: String,
val Response: 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
}

Attempt to invoke interface method 'int java.util.List.size()' on a null object reference on passing Parcelable in Intent Kotlin

So I have a sample app that is currently using Retrofit to fetch data and display it in a recyclerview with its custom adapter. I want to pass the data to the a more details page when I click on a character name on my recyclerView. I found some tutorials and decided to use the Parcelize annotation like this:
#Parcelize data class Character (
val charID: Long,
val name: String,
val birthday: String,
val occupation: List<String>,
val img: String,
val status: String,
val nickname: String,
val appearance: List<Long>,
val portrayed: String,
val category: String,
val betterCallSaulAppearance: List<Long> ) : Parcelable
My adapter looks like this:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val character = characters[position]
holder.characterNameText.text = character.name
holder.characterNameText.setOnClickListener {
holder.passData(character)
}
}
override fun getItemCount() = characters.size
class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView) {
val characterNameText = itemView.findViewById<TextView>(R.id.character_name)
fun passData(character : Character) {
val intent = Intent(itemView.context, CharacterDetails::class.java)
intent.putExtra(CHARACTER, character)
itemView.context.startActivity(intent)
}
}
And in the CharacterDetails Activity it looks like this:
companion object {
const val CHARACTER = "character"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_character_details)
val bundle = intent.extras
val character = bundle!!.getParcelable<Character>(CHARACTER)
val characterName = findViewById<TextView>(R.id.character_details_name)
Yet I get a
java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
at com.example.myapplication.models.Character.writeToParcel(Unknown Source:85)
I'm still new to this so I really need your help.Thanks!
Obviously, the error caused by the list reference of the data class is null. You can modify the code like this.
#Parcelize data class Character (
val charID: Long,
val name: String,
val birthday: String,
val occupation: List<String>?,
val img: String,
val status: String,
val nickname: String,
val appearance: List<Long>?,
val portrayed: String,
val category: String,
val betterCallSaulAppearance: List<Long>? ) : Parcelable

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")