How can I stop creating a quoteData variable every time in my view model?
This solution is working, but the amount of repetitive code is terrifying
class QuoteDetailsViewModel(application: Application) : AndroidViewModel(application) {
private val quoteRepository: QuoteRepository = QuoteRepository(application)
private val quoteId = MutableLiveData<String>("")
val quoteAuthor = MutableLiveData<String>("")
val quoteContent = MutableLiveData<String>("")
val quoteDescription = MutableLiveData<String>("")
val quoteLastUpdate = MutableLiveData<String>("")
val onSaveClickEvent = MutableLiveData<ViewModelEvent<Unit>>()
val onCancelClickEvent = MutableLiveData<ViewModelEvent<Unit>>()
val onDeleteClickEvent = MutableLiveData<ViewModelEvent<String?>>()
var isNewQuote = false
fun setInitialData(arguments: Bundle?) {
if (arguments != null) {
val quoteData = arguments.getSerializable(KEY_DATA) as Quote
isNewQuote = false
quoteData.let { quote ->
quoteId.postValue(quote.id)
quoteAuthor.postValue(quote.author)
quoteContent.postValue(quote.quote)
quoteDescription.postValue(quote.description)
quoteLastUpdate.postValue(quote.lastUpdated.getFormattedDate())
}
} else {
isNewQuote = true
}
}
fun onViewClick(viewId: Int) {
val currentTime = Calendar.getInstance().time
when (viewId) {
R.id.actionSave -> {
if (!isNewQuote) {
val quoteData = Quote(
id = this.quoteId.value ?: "",
author = this.quoteAuthor.value ?: "",
quote = this.quoteContent.value ?: "",
description = this.quoteDescription.value ?: "",
lastUpdated = currentTime
)
quoteRepository.update(quoteData)
} else {
val quoteData = Quote(
id = UUID.randomUUID().toString(),
author = this.quoteAuthor.value ?: "",
quote = this.quoteContent.value ?: "",
description = this.quoteDescription.value ?: "",
lastUpdated = currentTime
)
quoteRepository.insert(quoteData)
}
onSaveClickEvent.postValue(ViewModelEvent(Unit))
}
R.id.actionCancel -> onCancelClickEvent.postValue(ViewModelEvent(Unit))
R.id.actionDelete -> onDeleteClickEvent.postValue(ViewModelEvent(this.quoteId.value))
}
}
fun onDeleteItemResult(guid: String?) {
val currentTime = Calendar.getInstance().time
val quoteData = Quote(
id = guid ?: "",
author = this.quoteAuthor.value ?: "",
quote = this.quoteContent.value ?: "",
description = this.quoteDescription.value ?: "",
lastUpdated = currentTime
)
quoteRepository.delete(quoteData)
}
companion object {
fun newBundle(quote: Quote?): Bundle {
return Bundle().apply {
putSerializable(KEY_DATA, quote)
}
}
}
}
Also here is code of my Quote:
#Entity(tableName = "quotes")
class Quote(
#PrimaryKey
val id: String,
val author: String,
val quote: String,
val description: String,
#ColumnInfo(name = "last_updated")
val lastUpdated: Date?
) : Serializable
And here is the code when I click on a list item
val allQuotes: LiveData<List<Quote>>
init {
allQuotes = quoteRepository.allQuotes
}
fun onListItemClick(itemIndex: Int) {
onListItemClickEvent.postValue(
ViewModelEvent(QuoteDetailsViewModel.newBundle(allQuotes.value?.getOrNull(itemIndex)))
)
}
Then I get to my view model - QuoteDetailsViewModel
Related
i am trying to create a video player app using kotlin , First of all I got the videos files by using MediaStore , than store this in ArrayList so far it's been perfect but When I made a folder list of videos, I tried to find out the size of those folders and how many video files there are in those folders, but I failed. like this (Image)
Check this image for more clear
This is my data class code (VideoItem.Kt)
import android.net.Uri
data class VideoItem(
val id: String,
val title: String,
val duration: Long = 0,
val folderName: String,
val size: String,
val path: String,
val dateAdded: String,
val artUri: Uri
)
data class FolderItem(
val id: String,
val folderName: String,
val folderSize: Long
)
This is my MainActivity Code To get Allvideos Using MediaStore
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
companion object {
lateinit var videoList: ArrayList<VideoItem>
lateinit var folderList: ArrayList<FolderItem>
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
folderList = ArrayList()
videoList = getAllVideos()
setFragment(VideoviewFragment())
}
private fun setFragment(fragment: Fragment) {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.FrameLayout, fragment)
transaction.disallowAddToBackStack()
transaction.commit()
}
#SuppressLint("Recycle", "Range")
private fun getAllVideos(): ArrayList<VideoItem> {
val tempList = ArrayList<VideoItem>()
val tempFolderList = ArrayList<String>()
val projection = arrayOf(
MediaStore.Video.Media.TITLE,
MediaStore.Video.Media.SIZE,
MediaStore.Video.Media._ID,
MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
MediaStore.Video.Media.DATA,
MediaStore.Video.Media.DATE_ADDED,
MediaStore.Video.Media.DURATION,
MediaStore.Video.Media.BUCKET_ID
)
val cursor = this.contentResolver.query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
MediaStore.Video.Media.DATE_ADDED + " DESC"
)
if (cursor != null)
if (cursor.moveToNext())
do {
val titleC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.TITLE))
val idC = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media._ID))
val folderNameC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_DISPLAY_NAME))
val folderIdC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_ID))
val sizeC = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.SIZE))
val pathC = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA))
val dateAddedC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATE_ADDED))
val durationC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DURATION))
.toLong()
try {
val file = File(pathC)
val artUriC = Uri.fromFile(file)
val video = VideoItem(
title = titleC,
id = idC,
folderName = folderNameC,
size = sizeC,
path = pathC,
duration = durationC,
dateAdded = dateAddedC,
artUri = artUriC
)
if (file.exists()) tempList.add(video)
//for adding Folders
if (!tempFolderList.contains(folderNameC)) {
tempFolderList.add(folderNameC)
val folderSizeC = getFileLength(pathC)
folderList.add(
FolderItem(
id = folderIdC,
folderName = folderNameC,
folderSize = folderSizeC
)
)
}
} catch (_: Exception) {
}
} while (cursor.moveToNext())
cursor?.close()
return tempList
}
private fun getFileLength(path: String?): Long {
return if (!isExistFile(path)) 0 else File(path.toString()).length()
}
private fun isExistFile(path: String?): Boolean {
val file = File(path.toString())
return file.exists()
}
}
This is my RecyclerviwAdapter Code(FolderAdapter.kt)
class FoldersAdapter(private val context: Context, private var foldersList: ArrayList<FolderItem>) :
RecyclerView.Adapter<FoldersAdapter.MyHolder>() {
class MyHolder(binding: FolderItemBinding) : RecyclerView.ViewHolder(binding.root) {
val folderName = binding.folderName
val noofFiles = binding.nooffiles
val folderSize = binding.foldersize
val root = binding.root
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
return MyHolder(FolderItemBinding.inflate(LayoutInflater.from(context), parent, false))
}
override fun onBindViewHolder(holder: MyHolder, position: Int) {
holder.folderName.text = foldersList[position].folderName
val size: Long = foldersList[position].folderSize
holder.folderSize.text = android.text.format.Formatter.formatFileSize(context, (size))
holder.root.setOnClickListener {
val intent = Intent(context, FolderVideosActivity::class.java)
intent.putExtra("position", position)
ContextCompat.startActivity(context, intent, null)
}
}
override fun getItemCount(): Int {
return foldersList.size
}
}
This is my all codes now please check out all code and suggest the best.
Thank you
Use this function for size
private fun getFolderSize(f: File): Long {
var size: Long = 0
if (f.isDirectory) {
for (file in f.listFiles()!!) {
size += getFolderSize(file)
}
} else {
size = f.length()
}
return size
}
And Count number of files Use this
val length = File("/path/to/folder").listFiles()?.size
I'm trying to save this data class into database:
#Entity
#Parcelize
data class Rocket(
#SerializedName("active")
val active: Boolean = false,
#SerializedName("boosters")
val boosters: Int = 0,
#SerializedName("company")
val company: String = "",
#SerializedName("cost_per_launch")
val costPerLaunch: Int = 0,
#SerializedName("country")
val country: String = "",
#SerializedName("description")
val description: String = "",
#SerializedName("diameter")
val diameter: Diameter = Diameter(),
#SerializedName("engines")
val engines: Engines = Engines(),
#SerializedName("first_flight")
val firstFlight: String = "",
#SerializedName("first_stage")
val firstStage: FirstStage = FirstStage(),
#SerializedName("flickr_images")
val flickrImages: List<String> = listOf(),
#SerializedName("height")
val height: Height = Height(),
#PrimaryKey
#SerializedName("id")
val id: String = "",
#SerializedName("landing_legs")
val landingLegs: LandingLegs = LandingLegs(),
#SerializedName("mass")
val mass: Mass = Mass(),
#SerializedName("name")
val name: String = "",
#SerializedName("payload_weights")
val payloadWeights: List<PayloadWeight> = listOf(),
#SerializedName("second_stage")
val secondStage: SecondStage = SecondStage(),
#SerializedName("stages")
val stages: Int = 0,
#SerializedName("success_rate_pct")
val successRatePct: Int = 0,
#SerializedName("type")
val type: String = "",
#SerializedName("wikipedia")
val wikipedia: String = ""
): Parcelable
#Parcelize
data class Diameter(
#SerializedName("feet")
val feet: Double = 0.0,
#SerializedName("meters")
val meters: Double = 0.0
): Parcelable
#Parcelize
data class Engines(
#SerializedName("engine_loss_max")
val engineLossMax: Int = 0,
#SerializedName("isp")
val isp: Isp = Isp(),
#SerializedName("layout")
val layout: String = "",
#SerializedName("number")
val number: Int = 0,
#SerializedName("propellant_1")
val propellant1: String = "",
#SerializedName("propellant_2")
val propellant2: String = "",
#SerializedName("thrust_sea_level")
val thrustSeaLevel: ThrustSeaLevel = ThrustSeaLevel(),
#SerializedName("thrust_to_weight")
val thrustToWeight: Double = 0.0,
#SerializedName("thrust_vacuum")
val thrustVacuum: ThrustVacuum = ThrustVacuum(),
#SerializedName("type")
val type: String = "",
#SerializedName("version")
val version: String = ""
): Parcelable
#Parcelize
data class FirstStage(
#SerializedName("burn_time_sec")
val burnTimeSec: Int = 0,
#SerializedName("engines")
val engines: Int = 0,
#SerializedName("fuel_amount_tons")
val fuelAmountTons: Double = 0.0,
#SerializedName("reusable")
val reusable: Boolean = false,
#SerializedName("thrust_sea_level")
val thrustSeaLevel: ThrustSeaLevel = ThrustSeaLevel(),
#SerializedName("thrust_vacuum")
val thrustVacuum: ThrustVacuum = ThrustVacuum()
): Parcelable
#Parcelize
data class Height(
#SerializedName("feet")
val feet: Double = 0.0,
#SerializedName("meters")
val meters: Double = 0.0
): Parcelable {
fun toStringMetric(): String {
return "Height: $meters m"
}
fun toStringImperial(): String {
return "Height: $feet ft"
}
}
#Parcelize
data class LandingLegs(
#SerializedName("material")
val material: String = "",
#SerializedName("number")
val number: Int = 0
): Parcelable
#Parcelize
data class Mass(
#SerializedName("kg")
val kg: Int = 0,
#SerializedName("lb")
val lb: Int = 0
): Parcelable {
fun toStringMetric(): String {
return "Mass: $kg kg"
}
fun toStringImperial(): String {
return "Mass: $lb lbs"
}
}
#Parcelize
data class PayloadWeight(
#SerializedName("id")
val id: String = "",
#SerializedName("kg")
val kg: Int = 0,
#SerializedName("lb")
val lb: Int = 0,
#SerializedName("name")
val name: String = ""
): Parcelable
#Parcelize
data class SecondStage(
#SerializedName("burn_time_sec")
val burnTimeSec: Int = 0,
#SerializedName("engines")
val engines: Int = 0,
#SerializedName("fuel_amount_tons")
val fuelAmountTons: Double = 0.0,
#SerializedName("payloads")
val payloads: Payloads = Payloads(),
#SerializedName("reusable")
val reusable: Boolean = false,
#SerializedName("thrust")
val thrust: Thrust = Thrust()
): Parcelable
#Parcelize
data class Isp(
#SerializedName("sea_level")
val seaLevel: Int = 0,
#SerializedName("vacuum")
val vacuum: Int = 0
): Parcelable
#Parcelize
data class ThrustSeaLevel(
#SerializedName("kN")
val kN: Int = 0,
#SerializedName("lbf")
val lbf: Int = 0
): Parcelable
#Parcelize
data class ThrustVacuum(
#SerializedName("kN")
val kN: Int = 0,
#SerializedName("lbf")
val lbf: Int = 0
): Parcelable
#Parcelize
data class Payloads(
#SerializedName("composite_fairing")
val compositeFairing: CompositeFairing = CompositeFairing(),
#SerializedName("option_1")
val option1: String = ""
): Parcelable
#Parcelize
data class Thrust(
#SerializedName("kN")
val kN: Int = 0,
#SerializedName("lbf")
val lbf: Int = 0
): Parcelable
#Parcelize
data class CompositeFairing(
#SerializedName("diameter")
val diameter: Diameter = Diameter(),
#SerializedName("height")
val height: Height = Height()
): Parcelable
In order to do that, I have to write type converters that convert non-primitive types. My naive solution is to just convert everything into a string that's divided by `;`.
Here's what I wrote:
class RocketTypeConverter {
#TypeConverter
fun fromDiameterToString(diam: Diameter?): String? {
return diam?.let {
"${diam.feet};${diam.meters}"
}
}
#TypeConverter
fun fromStringToDiameter(str: String?): Diameter? {
str?.let {
str.split(";").also {
return Diameter(
feet = it[0].toDouble(),
meters = it[1].toDouble()
)
}
}
return null
}
#TypeConverter
fun fromEnginesToString(engines: Engines?): String? {
engines?.let {
engines.apply {
return "$engineLossMax" +
";" + fromIspToString(isp) +
";" + layout +
";" + "$number" +
";" + propellant1 +
";" + propellant2 +
";" + fromThrustSeaLevelToString(thrustSeaLevel) +
";" + "$thrustToWeight" +
";" + fromThrustVacuumToString(thrustVacuum) +
";" + type +
";" + version
}
}
return null
}
#TypeConverter
fun fromStringToEngines(str: String?): Engines? {
str?.let {
str.split(";").also {
val isp = fromStringToIsp(it[1])
val thrustSeaLevel = fromStringToThrustSeaLevel(it[6])
val thrustVacuum = fromStringToThrustVacuum(it[8])
return if(isp != null && thrustSeaLevel != null && thrustVacuum != null) {
Engines(
engineLossMax = it[0].toInt(),
isp = isp,
layout = it[2],
number = it[3].toInt(),
propellant1 = it[4],
propellant2 = it[5],
thrustSeaLevel = thrustSeaLevel,
thrustToWeight = it[7].toDouble(),
thrustVacuum = thrustVacuum,
type = it[9],
version = it[10]
)
} else {
null
}
}
}
return null
}
#TypeConverter
fun fromFirstStageToString(firstStage: FirstStage?): String? {
firstStage?.let {
firstStage.apply {
return "$burnTimeSec" +
";" + "$engines" +
";" + "$fuelAmountTons" +
";" + (if(reusable) "1" else "0") +
";" + fromThrustSeaLevelToString(thrustSeaLevel) +
";" + fromThrustVacuumToString(thrustVacuum)
}
}
return null
}
#TypeConverter
fun fromStringToFirstStage(str: String?): FirstStage? {
str?.let {
str.split(";").also {
val thrustSeaLevel = fromStringToThrustSeaLevel(it[4])
val thrustVacuum = fromStringToThrustVacuum(it[5])
return if(thrustSeaLevel != null && thrustVacuum != null) {
FirstStage(
burnTimeSec = it[0].toInt(),
engines = it[1].toInt(),
fuelAmountTons = it[2].toDouble(),
reusable = it[3] == "1",
thrustSeaLevel = thrustSeaLevel,
thrustVacuum = thrustVacuum
)
} else {
null
}
}
}
return null
}
#TypeConverter
fun fromHeightToString(height: Height?): String? {
height?.let {
return "${height.feet};${height.meters}"
}
return null
}
#TypeConverter
fun fromStringToHeight(str: String?): Height? {
str?.let {
str.split(";").also {
return Height(
feet = it[0].toDouble(),
meters = it[1].toDouble()
)
}
}
return null
}
#TypeConverter
fun fromLadingLegsToString(landingLegs: LandingLegs?): String? {
landingLegs?.let {
return "${landingLegs.material};${landingLegs.number}"
}
return null
}
#TypeConverter
fun fromStringToLandingLegs(str: String?): LandingLegs? {
str?.let {
str.split(";").also {
return LandingLegs(
material = it[0],
number = it[1].toInt()
)
}
}
return null
}
#TypeConverter
fun fromMassToString(mass: Mass?): String? {
mass?.let {
return "${mass.kg};${mass.lb}"
}
return null
}
#TypeConverter
fun fromStringToMass(str: String?): Mass? {
str?.let {
str.split(";").also {
return Mass(
kg = it[0].toInt(),
lb = it[1].toInt()
)
}
}
return null
}
#TypeConverter
fun fromPayLoadWeightToString(payloadWeight: PayloadWeight?): String? {
payloadWeight?.let {
return "${payloadWeight.id};${payloadWeight.kg};${payloadWeight.lb};${payloadWeight.name}"
}
return null
}
#TypeConverter
fun fromStringToPayloadWeight(str: String?): PayloadWeight? {
str?.let {
str.split(";").also {
return PayloadWeight(
id = it[0],
kg = it[1].toInt(),
lb = it[2].toInt(),
name = it[3]
)
}
}
return null
}
#TypeConverter
fun fromSecondStageToString(secondStage: SecondStage?): String? {
secondStage?.let {
secondStage.apply {
return "$burnTimeSec" +
";" + "$engines" +
";" + "$fuelAmountTons" +
";" + fromPayloadsToString(payloads) +
";" + (if(reusable) "1" else "0") +
";" + fromThrustToString(thrust)
}
}
return null
}
#TypeConverter
fun fromStringToSecondStage(str: String?): SecondStage? {
str?.let {
str.split(";").also {
val payloads = fromStringToPayloads(it[3])
val thrust = fromStringToThrust(it[5])
return if(payloads != null && thrust != null) {
return SecondStage(
burnTimeSec = it[0].toInt(),
engines = it[1].toInt(),
fuelAmountTons = it[2].toDouble(),
payloads = payloads,
reusable = it[4] == "1",
thrust = thrust
)
} else {
null
}
}
}
return null
}
#TypeConverter
fun fromThrustToString(thrust: Thrust?): String? {
thrust?.let {
return "${thrust.kN};${thrust.lbf}"
}
return null
}
#TypeConverter
fun fromStringToThrust(str: String?): Thrust? {
str?.let {
str.split(";").also {
return Thrust(
kN = it[0].toInt(),
lbf = it[1].toInt()
)
}
}
return null
}
#TypeConverter
fun fromPayloadsToString(payloads: Payloads?): String? {
payloads?.let {
return fromCompositeFairingToString(payloads.compositeFairing) + payloads.option1
}
return null
}
#TypeConverter
fun fromStringToPayloads(str: String?): Payloads? {
str?.let {
str.split(";").also {
fromStringToCompositeFairing(it[0])?.let { compositeFairing ->
return Payloads(
compositeFairing = compositeFairing,
option1 = it[1]
)
} ?: return null
}
}
return null
}
#TypeConverter
fun fromCompositeFairingToString(compositeFairing: CompositeFairing?): String? {
compositeFairing?.let {
return fromDiameterToString(compositeFairing.diameter) +
fromHeightToString(compositeFairing.height)
}
return null
}
#TypeConverter
fun fromStringToCompositeFairing(str: String?): CompositeFairing? {
str?.let {
str.split(";").also {
val diameter = fromStringToDiameter(it[0])
val height = fromStringToHeight(it[1])
return if(diameter != null && height != null) {
CompositeFairing(
diameter = diameter,
height = height
)
} else {
null
}
}
}
return null
}
#TypeConverter
fun fromIspToString(isp: Isp?): String? {
isp?.let {
return "${isp.seaLevel};${isp.vacuum}"
}
return null
}
#TypeConverter
fun fromStringToIsp(str: String?): Isp? {
str?.let {
str.split(";").also {
return Isp(
seaLevel = it[0].toInt(),
vacuum = it[1].toInt()
)
}
}
return null
}
#TypeConverter
fun fromThrustSeaLevelToString(thrustSeaLevel: ThrustSeaLevel?): String? {
thrustSeaLevel?.let {
return "${thrustSeaLevel.kN};${thrustSeaLevel.lbf}"
}
return null
}
#TypeConverter
fun fromStringToThrustSeaLevel(str: String?): ThrustSeaLevel? {
str?.let {
str.split(";").also {
return ThrustSeaLevel(
kN = str[0].toInt(),
lbf = str[1].toInt()
)
}
}
return null
}
#TypeConverter
fun fromThrustVacuumToString(thrustVacuum: ThrustVacuum?): String? {
thrustVacuum?.let {
return "${thrustVacuum.kN};${thrustVacuum.lbf}"
}
return null
}
#TypeConverter
fun fromStringToThrustVacuum(str: String?): ThrustVacuum? {
str?.let {
str.split(";").also {
return ThrustVacuum(
kN = str[0].toInt(),
lbf = str[1].toInt()
)
}
}
return null
}
#TypeConverter
fun fromFlickrImagesToString(flickrImages: List<String>?): String? {
flickrImages?.let {
var imagesString = ""
for(image in flickrImages) {
imagesString += "$image;"
}
return imagesString.substring(0, (imagesString.length - 1))
}
return null
}
#TypeConverter
fun fromStringToFlickrImages(str: String?): List<String>? {
str?.let {
return str.split(";")
}
return null
}
#TypeConverter
fun fromPayloadWeightsToString(payloadWeights: List<PayloadWeight>?): String? {
payloadWeights?.let {
var weightsString = ""
for(weight in payloadWeights) {
weightsString += fromPayLoadWeightToString(weight) + ";"
}
return weightsString.substring(0, (weightsString.length - 1))
}
return null
}
#TypeConverter
fun fromStringToPayloadWeights(str: String?): List<PayloadWeight>? {
str?.let {
val weights = mutableListOf<PayloadWeight>()
str.split(";").also {
for(payloadWeight in it) {
fromStringToPayloadWeight(payloadWeight)?.let { payloadWeight ->
weights.add(payloadWeight)
} ?: return null
}
}
return weights
}
return null
}
}
The way I have it is that I have two modules: launch and rocket. Each has their own repository and each repository builds the database:
private val dao: Database = Room.databaseBuilder(
context,
Database::class.java,
Database.NAME_DB
).build()
The problem I'm having is that I get this error: java.lang.IndexOutOfBoundsException: Index: 1, Size: 1.
Right now, the error happens because of fromStringToIsp(). That doesn't seem like an algorithmic error because Isp class only has two fields, so I couldn't have erred with indices. And if I brute-force the fromStringToIsp() to pass, some other function will also report out of bound error.
So where did I go wrong implementing my TypeConverters?
The string that you're passing to fromStringToIsp() does not contain a semicolon because in fromStringToEngines() you're doing the following:
str.split(";").also {
val isp = fromStringToIsp(it[1])
/* ... */
}
Then, in fromStringToIsp(), you do:
str.split(";").also {
return Isp(
seaLevel = it[0].toInt(),
vacuum = it[1].toInt()
)
}
When trying to access it[1] you'll get an exception, because it only has one element, i.e. it[0].
I just make a recyclerview and the data is doubled with this logic :
if(itemsList != null)
{
while (itemsList.hasNext()) {
val inFormat: DateFormat = SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'")
val outFormat: DateFormat = SimpleDateFormat("MM/dd/yyyy hh:mm")
val inFormatTwo: DateFormat = SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'")
val outFormatTwo: DateFormat = SimpleDateFormat("MM/dd/yyyy hh:mm")
val detail = itemsList.iterator().next().listIterator()
while(detail.hasNext())
{
val item = detail.next()
val status = item.status
val type = item.itemType
val pickupPoint = item.pickupPoint
val name = item.requesterDetails.elementAt(0).name
val reqDate = item.requestDate
val pickupTime = item.pickupTime
val reviewDate = item.pickupTime
val purpose = item.rejectReason
val quantity = item.quantity
val isActive = item.isActive
val isLatest = item.isLatest
var itemList : Any
val reqDateFormatted : Date?
val reqDateFix : String
reqDateFormatted = inFormat.parse(reqDate)
reqDateFix = outFormat.format(reqDateFormatted!!)
var pickupTimeFormatted : Date?
var pickupTimeFix = ""
if(pickupTime.length == 24)
{
pickupTimeFormatted = inFormat.parse(pickupTime)
pickupTimeFix = outFormat.format(pickupTimeFormatted)
} else if(pickupTime.length == 20)
{
pickupTimeFormatted = inFormatTwo.parse(pickupTime)
pickupTimeFix = outFormatTwo.format(pickupTimeFormatted!!)
}
if(isLatest == "Y")
{
itemList = DataModel(
type + " Request",
status,
name,
type,
quantity.toString(),
reqDateFix,
reviewDate,
pickupPoint,
pickupTimeFix,
purpose,
isActive,
isLatest
)
itemsData.add(itemList)
adapter.notifyDataSetChanged()
}
}
}
}
Basically, it is used to inform the user of the item they request, so the data comes from API. So, if itemsList is not null then while itemsList array has elements and inside it also exists array(s) and inside the array, I add each string to ArrayList and then isLatest string is equal to Y then add those arrays to itemList as a DataModel and then save them by adding itemList to itemsData. What makes the array doubled, thank you for your help
Notify the changes to adapter after the while loop and not after each add.
Try this:
if(itemsList != null)
{
while (itemsList.hasNext()) {
val inFormat: DateFormat = SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'")
val outFormat: DateFormat = SimpleDateFormat("MM/dd/yyyy hh:mm")
val inFormatTwo: DateFormat = SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'")
val outFormatTwo: DateFormat = SimpleDateFormat("MM/dd/yyyy hh:mm")
val detail = itemsList.iterator().next().listIterator()
while(detail.hasNext())
{
val item = detail.next()
val status = item.status
val type = item.itemType
val pickupPoint = item.pickupPoint
val name = item.requesterDetails.elementAt(0).name
val reqDate = item.requestDate
val pickupTime = item.pickupTime
val reviewDate = item.pickupTime
val purpose = item.rejectReason
val quantity = item.quantity
val isActive = item.isActive
val isLatest = item.isLatest
var itemList : Any
val reqDateFormatted : Date?
val reqDateFix : String
reqDateFormatted = inFormat.parse(reqDate)
reqDateFix = outFormat.format(reqDateFormatted!!)
var pickupTimeFormatted : Date?
var pickupTimeFix = ""
if(pickupTime.length == 24)
{
pickupTimeFormatted = inFormat.parse(pickupTime)
pickupTimeFix = outFormat.format(pickupTimeFormatted)
} else if(pickupTime.length == 20)
{
pickupTimeFormatted = inFormatTwo.parse(pickupTime)
pickupTimeFix = outFormatTwo.format(pickupTimeFormatted!!)
}
if(isLatest == "Y")
{
itemList = DataModel(
type + " Request",
status,
name,
type,
quantity.toString(),
reqDateFix,
reviewDate,
pickupPoint,
pickupTimeFix,
purpose,
isActive,
isLatest
)
itemsData.add(itemList)
}
}
}
adapter.notifyDataSetChanged()
}
Hello) I'm new in Android dev. And I have a problem with my program.
It's my model:
data class Test (val id: Int,
val numberQuestion: String,
val question: String,
val questionImageSrc: String,
val examination: Boolean,
val typeQuestion: String,
val singleChoiceAnswers: ArrayList<singleChoiceAnswer>,
val multipleChoiceAnswers: ArrayList<multipleChoiceAnswers>,
val inputAnswer: ArrayList<inputAnswer>)
data class multipleChoiceAnswers(val letter: String,
val text: String,
val correctAnswer: Boolean,
val checked: Boolean)
data class singleChoiceAnswer(val letter: String,
val text: String,
val correctAnswer: Boolean,
val checked: Boolean)
data class inputAnswer(val correctAnswer: String,
val userAnswer: String)
It's how i get the data from JSON:
private fun jsonResult(jsonString: String?) {
val jsonArray = JSONArray(jsonString)
val list = ArrayList<Test>()
val slist = ArrayList<singleChoiceAnswer>()
val mlist = ArrayList<multipleChoiceAnswers>()
val ilist = ArrayList<inputAnswer>()
for (i in 0 until jsonArray.length()){
val jsonObject = jsonArray.getJSONObject(i)
val typeQuestion = jsonObject.getString("typeQuestion")
val curentId = jsonObject.optInt("id")
val curentNQ = jsonObject.optString("numberQuestion")
val curentQ = jsonObject.optString("question")
val curentQIS = jsonObject.optString("questionImageSrc")
val curentEx = jsonObject.optBoolean("examination")
if (typeQuestion.contains("multipleChoice")){
val multipleChoiceAnswers = jsonObject.getJSONArray("multipleChoiceAnswers")
for (sc in 0 until multipleChoiceAnswers.length()){
val curentMCA = multipleChoiceAnswers.getJSONObject(sc)
val letter = curentMCA.optString("letter")
val text = curentMCA.optString("text")
val correctAnswer = curentMCA.optBoolean("correctAnswer")
val checked = curentMCA.optBoolean("checked")
mlist.add(multipleChoiceAnswers(letter, text, correctAnswer, checked))
}
}
if (typeQuestion.contains("singleChoice")){
val singleChoiceAnswer = jsonObject.getJSONArray("singleChoiceAnswers")
for (sc in 0 until singleChoiceAnswer.length()){
val curentSCA = singleChoiceAnswer.getJSONObject(sc)
val letter = curentSCA.optString("letter")
val text = curentSCA.optString("text")
val correctAnswer = curentSCA.optBoolean("correctAnswer")
val checked = curentSCA.optBoolean("checked")
slist.add(singleChoiceAnswer(letter, text, correctAnswer, checked))
}
}
if (typeQuestion.contains("input")){
val inputAnswer = jsonObject.getJSONArray("inputAnswer")
for (sc in 0 until inputAnswer.length()){
val curentIA = inputAnswer.getJSONObject(sc)
val correctAnswer = curentIA.optString("correctAnswer")
val userAnswer = curentIA.optString("userAnswer")
ilist.add(inputAnswer(correctAnswer,userAnswer))
}
}
list.add(Test(curentId, curentNQ, curentQ, curentQIS, curentEx, typeQuestion, slist, mlist, ilist))
}
val adapter = TestAdapter(list) { item ->
testAdapterItemClick(item)
}
val recView = findViewById<RecyclerView>(R.id.testRecyclerView)
recView.adapter = adapter
}
Here is link to my JSON. If you need it)
Then i am doing something like that:
private fun testAdapterItemClick(item: Test) {
val fT: FragmentTransaction = supportFragmentManager.beginTransaction()
val frag1: Fragment1 = Fragment1()
val frag2: Fragment2 = Fragment2()
if (item.typeQuestion == "input") {
val bundle = Bundle()
bundle.putString("NUMBER_KEY", item.numberQuestion)
bundle.putString("QUESTION_KEY", item.question)
if(!item.questionImageSrc.isNullOrEmpty())
bundle.putString("IMAGE_KEY", item.questionImageSrc)
frag1.setArguments(bundle)
fT.add(R.id.frameLayout, frag1)
}
if (item.typeQuestion == "singleChoice") {
val bundle = Bundle()
bundle.putString("NUMBER_KEY", item.numberQuestion)
bundle.putString("QUESTION_KEY", item.question)
val count = item.singleChoiceAnswers.size
Toast.makeText(this, count.toString(), Toast.LENGTH_LONG).show()
// bundle.putInt("COUNT_KEY", count)
for (i in 0 until count)
{
val curentSCA = item.singleChoiceAnswers[i]
bundle.putString("letterSCA$i", curentSCA.letter)
bundle.putString("textSCA$i", curentSCA.text)
}
frag2.setArguments(bundle)
fT.add(R.id.frameLayout, frag2)
I need to get ArrayList of the definite item and put it data in fragment using bundle.
But I have a problem in fragment:
public override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val rootView = inflater.inflate(R.layout.fragment2, container, false)
val questionNumber = rootView.findViewById(R.id.questionNumber) as TextView
val questionText = rootView.findViewById(R.id.Question) as TextView
val questionImage = rootView.findViewById(R.id.questionImage) as ImageView
val qN : String = getArguments()?.getString("NUMBER_KEY").toString()
val quest: String = getArguments()?.getString("QUESTION_KEY").toString()
val qI: String = getArguments()?.getString("IMAGE_KEY").toString()
questionNumber.text=qN
questionText.text=quest
Picasso.get().load(qI).into(questionImage)
val radioGroup = rootView.findViewById(R.id.radioGroupSetectTest) as RadioGroup
val count : Int = getArguments()!!.getInt("COUNT_KEY")
val context = getContext()
for (i in 0 until count)
{
val curentRB = RadioButton(context)
val curLetter = getArguments()?.getString("letterSCA$i")
val curText = getArguments()?.getString("textSCA$i")
curentRB.setText(curLetter+" "+curText)
radioGroup.addView(curentRB)
}
It put the all values of singleChoiseAnswer from all items like this screen. Please, help me) I know that it is a simple problem but i realy dont understand)
Thanks in advance)
P.S. SOrry for my English)
First of all, you need to convert your Json into Kotlin classes, you can use GSON
to convert your json to kotlin data classes like this
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
data class QuestionResponse(
#field:SerializedName("multipleChoiceAnswers")
val multipleChoiceAnswers: List<MultipleChoiceAnswersItem?>? = null,
#field:SerializedName("inputAnswer")
val inputAnswer: List<InputAnswer?>? = null,
#field:SerializedName("numberQuestion")
val numberQuestion: String? = null,
#field:SerializedName("question")
val question: String? = null,
#field:SerializedName("typeQuestion")
val typeQuestion: String? = null,
#field:SerializedName("examination")
val examination: Boolean? = null,
#field:SerializedName("id")
val id: Int? = null,
#field:SerializedName("singleChoiceAnswers")
val singleChoiceAnswers: List<MultipleChoiceAnswersItem?>? = null,
#field:SerializedName("questionImageSrc")
val questionImageSrc: String? = null
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.createTypedArrayList(MultipleChoiceAnswersItem),
parcel.createTypedArrayList(InputAnswer),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.createTypedArrayList(MultipleChoiceAnswersItem),
parcel.readString()
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeTypedList(multipleChoiceAnswers)
parcel.writeTypedList(inputAnswer)
parcel.writeString(numberQuestion)
parcel.writeString(question)
parcel.writeString(typeQuestion)
parcel.writeValue(examination)
parcel.writeValue(id)
parcel.writeTypedList(singleChoiceAnswers)
parcel.writeString(questionImageSrc)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<QuestionResponse> {
override fun createFromParcel(parcel: Parcel): QuestionResponse {
return QuestionResponse(parcel)
}
override fun newArray(size: Int): Array<QuestionResponse?> {
return arrayOfNulls(size)
}
}
}
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
data class MultipleChoiceAnswersItem(
#field:SerializedName("letter")
val letter: String? = null,
#field:SerializedName("checked")
val checked: Boolean? = null,
#field:SerializedName("text")
val text: String? = null,
#field:SerializedName("correctAnswer")
val correctAnswer: Boolean? = null
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readString(),
parcel.readValue(Boolean::class.java.classLoader) as? Boolean
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(letter)
parcel.writeValue(checked)
parcel.writeString(text)
parcel.writeValue(correctAnswer)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<MultipleChoiceAnswersItem> {
override fun createFromParcel(parcel: Parcel): MultipleChoiceAnswersItem {
return MultipleChoiceAnswersItem(parcel)
}
override fun newArray(size: Int): Array<MultipleChoiceAnswersItem?> {
return arrayOfNulls(size)
}
}
}
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
data class InputAnswer(
#field:SerializedName("correctAnswer")
val correctAnswer: String? = null,
#field:SerializedName("userAnswer")
val userAnswer: String? = null) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString()
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(correctAnswer)
parcel.writeString(userAnswer)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<InputAnswer> {
override fun createFromParcel(parcel: Parcel): InputAnswer {
return InputAnswer(parcel)
}
override fun newArray(size: Int): Array<InputAnswer?> {
return arrayOfNulls(size)
}
}
}
To know about Parcelable go to this link
Then when this is complete, parse the JSON like this
private fun jsonResult(jsonString: String?) {
val jsonArray = JSONArray(jsonString)
val list = ArrayList<QuestionResponse>()
val slist = ArrayList<MultipleChoiceAnswersItem>()
val mlist = ArrayList<MultipleChoiceAnswersItem>()
val ilist = ArrayList<InputAnswer>()
for (i in 0 until jsonArray.length()){
val jsonObject = jsonArray.getJSONObject(i)
val typeQuestion = jsonObject.getString("typeQuestion")
val curentId = jsonObject.optInt("id")
val curentNQ = jsonObject.optString("numberQuestion")
val curentQ = jsonObject.optString("question")
val curentQIS = jsonObject.optString("questionImageSrc")
val curentEx = jsonObject.optBoolean("examination")
if (typeQuestion.contains("multipleChoice")){
val multipleChoiceAnswers = jsonObject.getJSONArray("multipleChoiceAnswers")
for (sc in 0 until multipleChoiceAnswers.length()){
val curentMCA = multipleChoiceAnswers.getJSONObject(sc)
val letter = curentMCA.optString("letter")
val text = curentMCA.optString("text")
val correctAnswer = curentMCA.optBoolean("correctAnswer")
val checked = curentMCA.optBoolean("checked")
mlist.add(MultipleChoiceAnswersItem(letter,checked, text, correctAnswer))
}
}
if (typeQuestion.contains("singleChoice")){
val singleChoiceAnswer = jsonObject.getJSONArray("singleChoiceAnswers")
for (sc in 0 until singleChoiceAnswer.length()){
val curentSCA = singleChoiceAnswer.getJSONObject(sc)
val letter = curentSCA.optString("letter")
val text = curentSCA.optString("text")
val correctAnswer = curentSCA.optBoolean("correctAnswer")
val checked = curentSCA.optBoolean("checked")
slist.add(MultipleChoiceAnswersItem(letter, checked,text, correctAnswer))
}
}
if (typeQuestion.contains("input")){
val inputAnswer = jsonObject.getJSONArray("inputAnswer")
for (sc in 0 until inputAnswer.length()){
val curentIA = inputAnswer.getJSONObject(sc)
val correctAnswer = curentIA.optString("correctAnswer")
val userAnswer = curentIA.optString("userAnswer")
ilist.add(InputAnswer(correctAnswer,userAnswer))
}
}
val questionResponse = QuestionResponse(mlist,ilist,curentNQ,curentQ,typeQuestion,curentEx,curentId,slist,curentQIS)
//pass this questionResponse to your adapter
}
}
Then in Adapter Item click
private fun testAdapterItemClick(item: Test) {
val fT: FragmentTransaction = supportFragmentManager.beginTransaction()
val frag1: Fragment1 = Fragment1()
val frag2: Fragment2 = Fragment2()
if (item.typeQuestion == "input") {
val bundle = Bundle()
bundle.putParcelable("input", item)
frag1.setArguments(bundle)
fT.add(R.id.frameLayout, frag1)
}
// do the same for the rest
}
Then in the Fragment
public override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val rootView = inflater.inflate(R.layout.fragment2, container, false)
val questionNumber = rootView.findViewById(R.id.questionNumber) as TextView
val questionText = rootView.findViewById(R.id.Question) as TextView
val questionImage = rootView.findViewById(R.id.questionImage) as ImageView
val item = arguments.getParcelable("input")
// val arraylist = arguments.getParcelableArrayList<YOUR_CLASS_TYPE>("key") //for arrays you can do it like this
//then set it in the radiobutton like this item.getCurrentText and so on
}
I want to update my object in Room by id, but it doesn't work.
Here is my DAO class
#Query("UPDATE greeting SET id =:id")
abstract fun updateGreetingByID(id: String)
Here is my DBHelper
fun updateGreetingByID(id: String) {
Thread { dataBase.greetingDao().updateGreetingByID(id) }.start()
}
My model
#Entity(tableName = "greeting")
class GreetingModel(
id: String? = "",
greetingCategory: String = "",
isFavourite: Boolean = false,
position: Int = 0
) : Parcelable {
#PrimaryKey(autoGenerate = true)
var _id: Int = 0
#ColumnInfo(name = "id")
var id: String? = null
#ColumnInfo(name = "greetingCategory")
var greetingCategory: String? = null
#ColumnInfo(name = "isFavourite")
var isFavourite: Boolean? = null
#ColumnInfo(name = "imageId")
var imageId: ByteArray? = null
#ColumnInfo(name = "position")
var position: Int = 0
#ColumnInfo(name = "saved")
var saved: Int = 0
constructor(parcel: Parcel) : this(
) {
_id = parcel.readInt()
id = parcel.readString()
greetingCategory = parcel.readString()
isFavourite = parcel.readValue(Boolean::class.java.classLoader) as? Boolean
imageId = parcel.createByteArray()
position = parcel.readInt()
saved = parcel.readInt()
}
init {
this.id = id
this.greetingCategory = greetingCategory
if (position != null) {
this.position = position
}
this.isFavourite = isFavourite
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(_id)
parcel.writeString(id)
parcel.writeString(greetingCategory)
parcel.writeValue(isFavourite)
parcel.writeByteArray(imageId)
parcel.writeInt(position)
parcel.writeInt(saved)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<GreetingModel> {
override fun createFromParcel(parcel: Parcel): GreetingModel {
return GreetingModel(parcel)
}
override fun newArray(size: Int): Array<GreetingModel?> {
return arrayOfNulls(size)
}
}
}
Where id is val id = UUID.randomUUID().toString()
I want to update object when user click to favourite button
val greetingModel = GreetingModel()
greetingModel.isFavourite = true
greetingModel.greetingCategory = " "
greetingModel.position = postImage
greetingModel.id?.let { helper.updateGreetingByID(greetingModel.id!!) }
My update method doesn't work. Hope, you will help!
Your issue is that you are trying to update according to a newly constructed Greeting that has the id as whatever value the id is set to when the Greeting is constructed using the default (no parameters constructor).
The fix is to retrieve the specific id of the Greeting that was clicked and set the id to that value before doing the update.