How to receive Parcelable object parameters in separate activity? - android

I would like to be able to receive the individual parameters of my paracable object in the next activity. I believe that I am using the correct method, just do not understand how to output the parameter inputs once in the second activity.
My MainActivty.kt:
package com.example.favouritefood
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.ImageButton
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val food1: ImageButton = findViewById(R.id.imageButtonFood1)
val food2: ImageButton = findViewById(R.id.imageButtonFood2)
val food3: ImageButton = findViewById(R.id.imageButtonFood3)
val food4: ImageButton = findViewById(R.id.imageButtonFood4)
val pizza = Food(
"Pizza", "www.test.com", "Italy, Tomato",
"1/1/2018", "pizza#mail.com", 5)
food1.setOnClickListener()
{
val intent = Intent(this, MetaDataActivity::class.java).apply {
putExtra("Food", pizza)
}
startActivity(intent)
}
}
}
My second activity (to receive the parcelable):
package com.example.favouritefood
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Parcelable
import android.widget.EditText
class MetaDataActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_meta_data)
val name: EditText = findViewById(R.id.editTextName)
val food = intent.getParcelableExtra("Food") as Parcelable
val test = food.writeToParcel(food, 0)
name.setText(test.toString())
}
}
Where food.wrtieToParcel(food, 0) throws a type mismatch of Required: Parcel! Found: Parcelable over food.
How would I go about reading the parameters in the object?
My object class for reference:
package com.example.favouritefood
import android.os.Parcel
import android.os.Parcelable
class Food(val name: String?, val location: String?, val keywords: String?, val date: String?, val email: String?, val rating: Int) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readInt()) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name)
parcel.writeString(location)
parcel.writeString(keywords)
parcel.writeString(date)
parcel.writeString(email)
parcel.writeInt(rating)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Food> {
override fun createFromParcel(parcel: Parcel): Food {
return Food(parcel)
}
override fun newArray(size: Int): Array<Food?> {
return arrayOfNulls(size)
}
}
}

You don't need to call val test = food.writeToParcel(food, 0).
use val food = intent.getParcelableExtra("Food") as Food instead.

Related

Format data from API

I'm currently writing a currency converter app. Everything is working fine however I'm having trouble formatting the API response. I'm getting "CurrencyResponse(conversion_result=1)" when what I really want is to get the value of conversion_result alone.
Below is the relevant code.
Thank you.
CurrencyApi.kt
package com.example.currencyconverter.data
import com.example.currencyconverter.BuildConfig
import retrofit2.http.GET
import retrofit2.http.Path
interface CurrencyApi {
companion object {
const val BASE_URL = "https://v6.exchangerate-api.com/v6/"
}
#GET("{access_key}/pair/{currency_from}/{currency_to}/{amount}")
suspend fun convert(
#Path("access_key") access_key: String = BuildConfig.API_KEY,
#Path("currency_from") currency_from: String,
#Path("currency_to") currency_to: String,
#Path("amount") amount: Double? = 0.0
): CurrencyResponse
}
CurrencyResponse.kt
package com.example.currencyconverter.data
import com.squareup.moshi.Json
data class CurrencyResponse(
#Json(name="conversion_result") var conversion_result: String
){
}
CurrencyRepository.kt
package com.example.currencyconverter.data
import com.example.currencyconverter.BuildConfig
import javax.inject.Inject
import javax.inject.Singleton
#Singleton
class CurrencyRepository #Inject constructor(private val currencyApi: CurrencyApi) {
suspend fun getConversionRate(baseCurrency: String, toCurrency: String, amount: Double?): CurrencyResponse {
return currencyApi.convert(BuildConfig.API_KEY, baseCurrency, toCurrency, amount)
}
}
CurrencyViewModel.kt
package com.example.currencyconverter.ui
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.currencyconverter.data.CurrencyRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
#HiltViewModel
class CurrencyViewModel #Inject constructor(private val repository: CurrencyRepository): ViewModel() {
private val _conversionResult = MutableLiveData<String>()
val conversionResult: LiveData<String> = _conversionResult
fun getConversionRate(baseCurrency: String, toCurrency: String, amount: Double?) {
viewModelScope.launch {
_conversionResult.value = repository.getConversionRate(baseCurrency, toCurrency, amount).toString()
}
}
}
HomeFragment.kt
package com.example.currencyconverter.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.example.currencyconverter.R
import com.example.currencyconverter.databinding.FragmentHomeBinding
import dagger.hilt.android.AndroidEntryPoint
#AndroidEntryPoint
class HomeFragment : Fragment(R.layout.fragment_home) {
private val viewModel by viewModels<CurrencyViewModel>()
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentHomeBinding.bind(view)
lateinit var firstCurrency: String
lateinit var secondCurrency: String
binding.apply {
spinnerFirst.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
firstCurrency = adapterView?.getItemAtPosition(position).toString()
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
spinnerSecond.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
secondCurrency = adapterView?.getItemAtPosition(position).toString()
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
}
binding.button.setOnClickListener {
val stringInTextField = binding.amountText.text.toString()
val amount = stringInTextField.toDoubleOrNull()
if(amount==null) {
binding.resultText.text = " "
}
viewModel.getConversionRate(firstCurrency, secondCurrency, amount)
viewModel.conversionResult.observe(viewLifecycleOwner) {
binding.resultText.text = it
}
}
}
}
The problem lies in
fun getConversionRate(baseCurrency: String, toCurrency: String, amount: Double?) {
viewModelScope.launch {
_conversionResult.value = repository.getConversionRate(baseCurrency, toCurrency, amount).toString()
}
}
to call toString() on a data class formats it the way you see it.
Since you only want the conversion_result you need to change it to
fun getConversionRate(baseCurrency: String, toCurrency: String, amount: Double?) {
viewModelScope.launch {
_conversionResult.value = repository.getConversionRate(baseCurrency, toCurrency, amount).conversion_result
}
}

How to add or find textview outside of the mainactivity in Android Kotlin?

package com.example.asyntask
import android.content.ContentValues.TAG
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnStart.setOnClickListener {
val tvCounter=findViewById<TextView>(R.id.tvCounter)
CountTask().execute(5)
}
}
}
class CountTask() : AsyncTask<Int, Int, Unit>() {
private val tag: String? = "Async"
override fun doInBackground(vararg params: Int?) {
Log.d(tag, "doInBackground: started")
val n: Int = params[0]!!
for (i in 0 until n) {
wait1Sec()
publishProgress(i)
}
}
private fun wait1Sec():Unit{
val start =System.currentTimeMillis()
while(System.currentTimeMillis()<start+1000){}
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
//WANT TO WRITE TEXT IN TEXT VIEW BUT HOW TO GET VIEW HERE
}
}
Please try to create the Callback with progressupdate from asynchtask to mainactivity
like:
Interface UpdateListener{
fun onProgressUpdate(progress:Int);
}
class CountTask(val listener:UpdateListener) : AsyncTask<Int, Int, Unit>() {
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
//WANT TO WRITE TEXT IN TEXT VIEW BUT HOW TO GET VIEW HERE
listener.onpreogressupdate(...)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnStart.setOnClickListener {
val tvCounter=findViewById<TextView>(R.id.tvCounter)
CountTask(object:listener:UpdateListener{
override fun onProgressUpdate(progress:Int){
//TODO update here
}
}).execute(5)
}
}

ViewHolder with onClicklistener Issue with UninitializedPropertyAccessException

I'm a beginner so please bare with me.
I have a viewholder that has an onclicklistener.
The aim of the click is to send a Url string into another fragment using Jetpack Navigation (hopefully i did it right)
the Url is being created within the dataclass itself.
but i keep getting this error:
kotlin.UninitializedPropertyAccessException: lateinit property galleryItem has not been initialized
I tried working around using companion object and other ways, nothing worked... is there a solution for this?
here is the view holder and data class
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import androidx.core.os.bundleOf
import androidx.navigation.Navigation
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.saheralsous.android.R
import com.saheralsous.android.database.remote.model.PagingData
class RecyclerViewPhotoAdapter() :
PagingDataAdapter<PagingData.GalleryItem, PhotoHolder>(
diffCallback = DiffCallback
) {
override fun onBindViewHolder(holder: PhotoHolder, position: Int) {
getItem(position)?.let {
holder.bind(it)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.photo_item_view,parent,false)
return PhotoHolder(view)
}
}
class PhotoHolder(view: View):
RecyclerView.ViewHolder(view), View.OnClickListener {
private val imageButtom: ImageButton = view.findViewById(R.id.ImageButton)
private lateinit var galleryItem: PagingData.GalleryItem //page. 594
#SuppressLint("ResourceType")
fun bind(galleryItem : PagingData.GalleryItem){
/*
idTextView.text = galleryItem.id
ownerTextView.text = galleryItem.owner
titleTextView.text = galleryItem.title
urlTextView.text = galleryItem.url
*/
galleryItem.url.let { url ->
Glide.with(itemView)
.load(url)
.override(350,350)
.into(imageButtom)
}
}
init {
imageButtom.setOnClickListener(this)
}
override fun onClick(v: View?) {
println("item was clicked")
val bundle = bundleOf("url" to galleryItem.photoPageUri ) <-- here is the issue
Navigation.findNavController(v!!).navigate(
R.id.action_photoGalleryFragment_to_photoPageFragment,
bundle)
}
}
object DiffCallback : DiffUtil.ItemCallback<PagingData.GalleryItem>() {
override fun areItemsTheSame(oldItem: PagingData.GalleryItem, newItem: PagingData.GalleryItem): Boolean {
// Id is unique.
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: PagingData.GalleryItem, newItem: PagingData.GalleryItem): Boolean {
return oldItem == newItem
}
}
data class
import android.net.Uri
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
data class PagingData(
val total : Int =0,
val Page: Int = 0,
val photos : List<GalleryItem>
){
val endOfPage = total == Page
#Keep
data class GalleryItem(
#SerializedName("id")
val id: String,
#SerializedName("owner")
val owner: String,
#SerializedName("title")
var title: String,
#SerializedName("url_s")
val url: String) {
val photoPageUri : Uri <-- here is the value
get() {
return Uri.parse("https://www.flickr.com/photos/")
.buildUpon()
.appendPath(owner)
.appendPath(id)
.build()
}
}
}
As the error states, you haven't initialised the galleryItem instance variable in your PhotoHolder. Add this inside your bind method:
this.galleryItem = galleryItem

Kotlin Add Parcelable implementation for Array of Double

I'm trying to add the parcelable implementation of a variable declared as an Array of Double.
When I try to generate this implementation automatically with Android Studio, the coordinateskey stays in TODO:
import android.os.Parcel
import android.os.Parcelable
class PointGeoJson (
val type: String = "",
val coordinates: Array<Double> = emptyArray()
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
TODO("coordinates")
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(type)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<PointGeoJson> {
override fun createFromParcel(parcel: Parcel): PointGeoJson {
return PointGeoJson(parcel)
}
override fun newArray(size: Int): Array<PointGeoJson?> {
return arrayOfNulls(size)
}
}
}
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
#Parcelize
class PointGeoJson(
val type: String = "",
val coordinates: Array<Double> = emptyArray()
) : Parcelable
This should work properly

how to add filter to RecyclerViewAdapter using kotlin?

I want to add a filter to this RecyclerViewAdapter please help in this code
NOTE : I USE A CLASS To fill the LIST ,this Class TAkes four
parameters as INPUTS (three Strings and an Image (Int)).
like the following example :
booksList.add(Book("The Wild Robot", "A 2 Categorie Book", "Description book", R.drawable.thewildrobot))
booksList.add(Book("Maria Semples", "Categorie Book", "Description book", R.drawable.mariasemples))
RecyclerView Adapter
import android.content.Intent
import android.support.v7.widget.CardView
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import kotlin.collections.ArrayList
#Suppress("UNREACHABLE_CODE")
class RecyclerViewAdapter(private val mContext: List_of_Books, private val mData: MutableList<Book>) : RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder>() {
private var mFilteredList: MutableList<Book>? = null
init {
mFilteredList = mData
}
override fun getItemCount(): Int {
return mData.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view: View
val mInflater = LayoutInflater.from(mContext)
view = mInflater.inflate(R.layout.cardveiw_item_book, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.tv_book_title.text = mData[position].getTitle()
holder.img_book_thumbnail.setImageResource(mData[position].thumbnail)
holder.cardView.setOnClickListener {
val intent = Intent(mContext, Book_Activity::class.java)
// passing data to the book activity
intent.putExtra("Title", mData[position].getTitle())
intent.putExtra("Description", mData[position].description)
intent.putExtra("Thumbnail", mData[position].thumbnail)
intent.putExtra("Category", mData[position].category)
// start the activity
mContext.startActivity(intent)
}
}
fun setfilter(listitem: MutableList<Book>): MutableList<Book>? {
mFilteredList!!.clear()
/*mFilteredList = ArrayList()*/
mFilteredList!!.addAll(listitem)
notifyDataSetChanged()
return mFilteredList
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal var tv_book_title: TextView = itemView.findViewById(R.id.book_title_id) as TextView
internal var img_book_thumbnail: ImageView = itemView.findViewById(R.id.book_img_id) as ImageView
internal var cardView: CardView = itemView.findViewById(R.id.cardview_id) as CardView
}
}
When I write in the EdiText nothing is changing , i'm using this code for the Editext:
fun filter(pl: MutableList<Book>, query: String): MutableList<Book> {
var query = query
query = query.toLowerCase()
val filteredModeList = ArrayList<Book>()
for (model in pl) {
val text = model.getTitle()!!.toLowerCase()
if (text.startsWith(query) || (text.contains(query))) {
filteredModeList.add(model)
}
}
return filteredModeList
}
val editText = findViewById<EditText>(R.id.editText)
editText.hint = getString(R.string.Search_here)
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
val filtermodelist = filter(booksList, p0.toString())
myAdapter.setfilter(filtermodelist)
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})
BOOK CLASS
import com.medanis.fneclisbooks.R.attr.title
import com.medanis.fneclisbooks.R.string.category
import com.medanis.fneclisbooks.R.string.description
import java.util.ArrayList
import kotlin.reflect.KMutableProperty1
import android.R.attr.name
class Book(title: String, category: String, description: String, var thumbnail: Int) {
private var title: String? = title
var category: String? = category
var description: String? = description
fun getTitle(): String? {
return title
}
fun setTitle(title: String) {
this.title = title
}}
I THINK THE PROBLEM IS IN THE FILTER IN THE ADAPTER THERE IS SOMETHING
WRONG THERE PLEASE HELP ME TO FIX IT
Please Help me , and if you need any more informations let me know in comments.

Categories

Resources