I make a navigation that navigates a set destination when a user click an app bar menu item.
First navigation flow: RuleFragment ->(menu item clicked) CsvTopFragment
Above navigation works correctly.
Second navigation flow: RuleFragment ->(menu item clicked) CsvTopFragment ->(automated navigation) RuleFragment ->(menu item clicked) CsvTopFragment
But above last nagigation does not work. Why does this happen? How can I fix this problem?
RuleFragment.kt
import android.app.ActionBar
import android.os.Bundle
import android.util.Log
import android.view.*
import android.widget.LinearLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.marginBottom
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.NavigationUI
import com.example.sleeprecorder.R
import com.example.sleeprecorder.database.SleepRecorderDatabase
import com.example.sleeprecorder.databinding.FragmentRuleBinding
import java.util.*
class RuleFragment: Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentRuleBinding.inflate(inflater)
binding.addRule.setOnClickListener {
findNavController().navigate(RuleFragmentDirections.actionRuleFragmentToAddRuleFragment())
}
val application = requireNotNull(this.activity).application
val dataSource = SleepRecorderDatabase.getInstance(application).ruleDao
val viewModelFactory = RuleViewModelFactory(dataSource, application)
val ruleViewModel = ViewModelProvider(this, viewModelFactory).get(RuleViewModel::class.java)
ruleViewModel.oneMonthRules.observe(this, Observer { oneMonthRules ->
binding.rulesContainer.removeAllViews()
oneMonthRules.forEach { rule ->
Log.i("RuleFragment", "rule ${rule}")
Log.i("RuleFragment" ,"rule date ${rule!!.date.let { timestamp ->
val calendar = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"))
calendar.timeInMillis = timestamp
calendar.time
}}")
val dateTextView = TextView(context)
dateTextView.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val calendarRule = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"))
calendarRule.timeInMillis = rule.date
val dateText = "${calendarRule.get(Calendar.YEAR)}/${calendarRule.get(Calendar.MONTH).let{ "00${it + 1}".substring("00${it + 1}".length - 2)}}/${calendarRule.get(Calendar
.DATE).let{ "00${it}".substring("00${it}".length - 2)}}"
dateTextView.text = dateText
val prohibitSleepTwice = TextView(context)
prohibitSleepTwice.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val prohibitSleepTwiceText = "${resources.getString(R.string.prohibit_sleep_twice)} ${if(rule.prohibitSleepTwice) resources.getString(R.string.maru) else resources.getString(R.string.batsu)}"
prohibitSleepTwice.text = prohibitSleepTwiceText
val stretchBeforeSleep = TextView(context)
stretchBeforeSleep.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val stretchBeforeSleepText = "${resources.getString(R.string.stretch_before_sleep)} ${if(rule.stretchBeforeSleep) resources.getString(R.string.maru) else resources.getString(R.string.batsu)}"
stretchBeforeSleep.text = stretchBeforeSleepText
val fixingWakeUpTime = TextView(context)
fixingWakeUpTime.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val fixingWakeUpTimeText = "${resources.getString(R.string.fixing_wake_up_time)} ${if(rule.fixingWakeUpTime) resources.getString(R.string.maru) else resources.getString(R.string.batsu)}"
fixingWakeUpTime.text = fixingWakeUpTimeText
val exerciseAfterWakeUp = TextView(context)
exerciseAfterWakeUp.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val exerciseAfterWakeUpText = "${resources.getString(R.string.exercise_after_wake_up)} ${if(rule.exerciseAfterWakeUp) resources.getString(R.string.maru) else resources.getString(R.string.batsu)}"
exerciseAfterWakeUp.text = exerciseAfterWakeUpText
val exerciseAfterGoHome = TextView(context)
exerciseAfterGoHome.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val exerciseAfterGoHomeText = "${resources.getString(R.string.exercise_after_go_home)} ${if(rule.exerciseAfterGoHome) resources.getString(R.string.maru) else resources.getString(R.string.batsu)}"
exerciseAfterGoHome.text = exerciseAfterGoHomeText
val ruleLinearLayout = LinearLayout(context)
val layoutParams = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT)
layoutParams.setMargins(16, 16, 0, 0)
ruleLinearLayout.layoutParams = layoutParams
ruleLinearLayout.orientation = LinearLayout.VERTICAL
ruleLinearLayout.addView(dateTextView)
ruleLinearLayout.addView(prohibitSleepTwice)
ruleLinearLayout.addView(stretchBeforeSleep)
ruleLinearLayout.addView(fixingWakeUpTime)
ruleLinearLayout.addView(exerciseAfterWakeUp)
ruleLinearLayout.addView(exerciseAfterGoHome)
binding.rulesContainer.addView(ruleLinearLayout)
ruleLinearLayout.setOnClickListener {
this.findNavController().navigate(RuleFragmentDirections.actionRuleFragmentToEditRuleFragment(rule.id))
}
}
})
val calendar = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"))
val thisYear = calendar.get(Calendar.YEAR)
val thisMonthIndex = calendar.get(Calendar.MONTH)
binding.yearMonth.text = getYearMonthStr(thisYear, thisMonthIndex)
binding.yearMonth.setTag(R.id.rule_year, thisYear)
binding.yearMonth.setTag(R.id.rule_month_index, thisMonthIndex)
binding.goToNextMonth.setOnClickListener {
val shownYear = binding.yearMonth.getTag(R.id.rule_year).toString().toInt()
val shownMonthIndex = binding.yearMonth.getTag(R.id.rule_month_index).toString().toInt()
val calendarToNextMonth = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"))
calendarToNextMonth.timeInMillis = 0
calendarToNextMonth.set(Calendar.YEAR, shownYear)
calendarToNextMonth.set(Calendar.MONTH, shownMonthIndex)
calendarToNextMonth.add(Calendar.MONTH, 1)
val targetYear = calendarToNextMonth.get(Calendar.YEAR)
val targetMonthIndex = calendarToNextMonth.get(Calendar.MONTH)
binding.yearMonth.text = getYearMonthStr(targetYear, targetMonthIndex)
binding.yearMonth.setTag(R.id.rule_year, targetYear)
binding.yearMonth.setTag(R.id.rule_month_index, targetMonthIndex)
ruleViewModel.onChangeMonth(targetYear, targetMonthIndex)
}
binding.goToPreviousMonth.setOnClickListener {
val shownYear = binding.yearMonth.getTag(R.id.rule_year).toString().toInt()
val shownMonthIndex = binding.yearMonth.getTag(R.id.rule_month_index).toString().toInt()
val calendarToPreviousMonth = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"))
calendarToPreviousMonth.timeInMillis = 0
calendarToPreviousMonth.set(Calendar.YEAR, shownYear)
calendarToPreviousMonth.set(Calendar.MONTH, shownMonthIndex)
calendarToPreviousMonth.add(Calendar.MONTH, -1)
val targetYear = calendarToPreviousMonth.get(Calendar.YEAR)
val targetMonthIndex = calendarToPreviousMonth.get(Calendar.MONTH)
binding.yearMonth.text = getYearMonthStr(targetYear, targetMonthIndex)
binding.yearMonth.setTag(R.id.rule_year, targetYear)
binding.yearMonth.setTag(R.id.rule_month_index, targetMonthIndex)
ruleViewModel.onChangeMonth(targetYear, targetMonthIndex)
}
ruleViewModel.setThisYearMonth(thisYear, thisMonthIndex)
setHasOptionsMenu(true)
return binding.root
}
private fun getYearMonthStr(year: Int, monthIndex: Int): String {
val yearStr = year.toString()
val monthStr = (monthIndex + 1).toString()
val zeroPaddingMonthStr = "00${monthStr}".substring("00${monthStr}".length - 2)
return "${yearStr}/${zeroPaddingMonthStr}"
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.defalut_items,menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val controller = requireView().findNavController()
val destination = controller.currentDestination
Log.i("RuleFragment", "onOptionsItemSelected: ${(NavigationUI.onNavDestinationSelected(item, requireView().findNavController()) || super.onOptionsItemSelected(item))}")
Log.i("RuleFragment", "onOptionsItemSelected findNavController: ${NavigationUI.onNavDestinationSelected(item, requireView().findNavController())}")
Log.i("RuleFragment", "super.onOptionsItemSelected: ${super.onOptionsItemSelected(item)}")
Log.i("RuleFragment", "item title :${item.title}")
Log.i("RuleFragment", "itemId: ${item.itemId}")
Log.i("RuleFragment", "destination: ${destination!!.displayName}")
return NavigationUI.onNavDestinationSelected(item, requireView().findNavController()) || super.onOptionsItemSelected(item)
}
}
CsvTopFragment.kt
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.example.sleeprecorder.R
import com.example.sleeprecorder.database.Rule
import com.example.sleeprecorder.database.SleepRecord
import com.example.sleeprecorder.database.SleepRecorderDatabase
import com.example.sleeprecorder.databinding.FragmentCsvTopBinding
import com.example.sleeprecorder.rule.RuleViewModel
import com.example.sleeprecorder.rule.RuleViewModelFactory
import com.example.sleeprecorder.sleeprecord.SleepRecordViewModel
import com.example.sleeprecorder.sleeprecord.SleepRecordViewModelFactory
import com.github.doyaaaaaken.kotlincsv.dsl.csvReader
import java.io.File
import java.util.*
class CsvTopFragment: Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentCsvTopBinding.inflate(inflater)
val application = requireNotNull(this.activity).application
Log.i("CsvTopFragment", "isExternalWritable() is ${isExternalWritable()}")
Log.i("CsvTopFragment", "isExternalStorageReadable() is ${isExternalStorageReadable()}")
Log.i("CsvTopFragment", "Environment.getExternalStorageDirectory() ${Environment.getExternalStorageDirectory()}")
Log.i("CsvTopFragment", "Environment.getRootDirectory() ${Environment.getRootDirectory()}")
Log.i("CsvTopFragment", " context.fileList() ${ requireContext().fileList()}")
Log.i("CsvTopFragment", "context.getExternalFilesDir(null) ${requireContext().getExternalFilesDir(null)}")
Log.i("CsvTopFragment", "can write: ${requireContext().getExternalFilesDir(null)!!.canWrite()}")
Log.i("CsvTopFragment", "Environment.getExternalStorageDirectory(): ${Environment.getExternalStorageDirectory()}")
if(!isExternalFilesDirWritable()) {
return binding.root
}
val csvDir = File(requireContext().getExternalFilesDir(null).toString(), "csv")
val fileNameListSleepRecord = getSleepRecordFileNameList(csvDir)
val fileNameListRuleRecord = getRuleRecordFileNameList(csvDir)
val fileNameListSleepPressureFactor = getSleepPressureFactorRecordFileNameList(csvDir)
binding.sleepRecordFileListSpinner.adapter = ArrayAdapter(
requireActivity(),
android.R.layout.simple_spinner_dropdown_item,
fileNameListSleepRecord
)
binding.ruleRecordFileListSpinner.adapter = ArrayAdapter(
requireActivity(),
android.R.layout.simple_spinner_dropdown_item,
fileNameListRuleRecord
)
binding.sleepPressureFactorRecordFileListSpinner.adapter = ArrayAdapter(
requireActivity(),
android.R.layout.simple_spinner_dropdown_item,
fileNameListSleepPressureFactor
)
binding.readSleepRecordCsvButton.setOnClickListener SleepRecord# { button ->
Log.i("CsvTopFragment", "read sleep record button pressed")
val sleepRecordDataSource = SleepRecorderDatabase.getInstance(application).sleepRecordDao
val sleepRecordViewModelFactory = SleepRecordViewModelFactory(sleepRecordDataSource, requireNotNull(this.activity).application)
val sleepRecordViewModel = ViewModelProvider(this, sleepRecordViewModelFactory).get(
SleepRecordViewModel::class.java)
val selectedFile = File(csvDir, binding.sleepRecordFileListSpinner.selectedItem.toString())
if(selectedFile.name == resources.getString(R.string.no_file)) {
Log.i("CsvTopFragment", resources.getString(R.string.no_file))
return#SleepRecord
}
val sleepRecordCsvList = getListFromCsv(selectedFile)
// Log.i("CsvTopFragment", sleepRecordCsvList.toString())
val sleepRecordList = csvListToSleepRecordList(sleepRecordCsvList)
Log.i("CsvTopFragment", "sleep record[0]: ${sleepRecordList[0]}")
sleepRecordViewModel.onClickInsertSleepRecordList(sleepRecordList)
findNavController().navigate(CsvTopFragmentDirections.actionCsvTopFragmentToSleepRecordFragment())
}
binding.readRuleRecordCsvButton.setOnClickListener RuleRecord# {
button ->
val ruleDataSource = SleepRecorderDatabase.getInstance(application).ruleDao
val ruleViewModelFactory = RuleViewModelFactory(ruleDataSource, application)
val ruleViewModel = ViewModelProvider(this, ruleViewModelFactory).get(RuleViewModel::class.java)
Log.i("CsvTopFragment", "read rule button pressed")
val selectedFile = File(csvDir, binding.ruleRecordFileListSpinner.selectedItem.toString())
Log.i("CsvTopFragment", "selected item: ${binding.ruleRecordFileListSpinner.selectedItem}")
if(selectedFile.name == resources.getString(R.string.no_file)) {
Log.i("CsvTopFragment", resources.getString(R.string.no_file))
return#RuleRecord
}
val ruleCsvList = getListFromCsv(selectedFile)
val ruleList = csvListToRuleList(ruleCsvList)
ruleViewModel.onClickInsertRuleList(ruleList)
this.findNavController().navigate(CsvTopFragmentDirections.actionCsvTopFragmentToRuleFragment())
}
return binding.root
}
private fun isExternalWritable(): Boolean {
return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
}
private fun isExternalStorageReadable(): Boolean {
return Environment.getExternalStorageState() in
setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY)
}
fun isExternalFilesDirWritable(): Boolean {
return requireContext().getExternalFilesDir(null)!!.canWrite()
}
private fun getSleepRecordFileNameList(dir: File): List<String> {
val fileListSleep = dir.listFiles()?.filter {
Regex("sleep_record-[0-9]{4}_[0-9]{1,2}\\.csv$").containsMatchIn(it.name)
}
val fileNameList = fileListSleep!!.map {
it.name
}
return if(fileNameList.size > 0) fileNameList else mutableListOf(resources.getString(R.string.no_file))
}
private fun getRuleRecordFileNameList(dir: File): List<String> {
val fileListSleep = dir.listFiles()?.filter {
Regex("rule_record-[0-9]{4}_[0-9]{1,2}\\.csv$").containsMatchIn(it.name)
}
val fileNameList = fileListSleep!!.map {
it.name
}
return if(fileNameList.size > 0) fileNameList else mutableListOf(resources.getString(R.string.no_file))
}
private fun getSleepPressureFactorRecordFileNameList(dir: File): List<String> {
val fileListSleep = dir.listFiles()?.filter {
Regex("sleep_pressure_factor-[0-9]{4}_[0-9]{1,2}\\.csv$").containsMatchIn(it.name)
}
val fileNameList = fileListSleep!!.map {
it.name
}
return if(fileNameList.size > 0) fileNameList else mutableListOf(resources.getString(R.string.no_file))
}
private fun getListFromCsv(csvFile: File): List<List<String>> {
val csvRows = csvReader().readAll(csvFile)
// Log.i("CsvTopFragment", "file name: ${csvFile.name}")
// Log.i("CsvTopFragment", "csvRows: ${csvRows}")
val rows = mutableListOf<List<String>>()
(1 .. csvRows.size - 1).forEach { index ->
rows.add(csvRows[index])
}
// Log.i("CsvTopFragment", "rows: ${rows}")
Log.i("CsvTopFragment", "first row ${rows[0]}")
Log.i("CsvTopFragment", "last row: ${rows[rows.size - 1]}")
return rows
}
private fun csvListToSleepRecordList(csvList: List<List<String>>): List<SleepRecord> {
val sleepRecordList = mutableListOf<SleepRecord>()
csvList.forEach {
val sleepStartStr = it[0]
val sleepEndStr = it[1]
val kindOfSleep = it[2]
val memo = it[4]
if(isDatetimeStringReadable(sleepStartStr)) {
// Log.i("CsvTopFragment", "${sleepStartStr} is unmatched")
val sleepStartTimeMilli = datetimeStringToTimeStamp(sleepStartStr)
val sleepEndTimeMilli = datetimeStringToTimeStamp(sleepEndStr)
val sleepRecord = SleepRecord(
startTimeMilli = sleepStartTimeMilli,
endTimeMilli = sleepEndTimeMilli,
kind = kindOfSleep,
memo = memo
)
sleepRecordList.add(sleepRecord)
}
}
return sleepRecordList
}
private fun csvListToRuleList(csvList: List<List<String>>): List<Rule> {
val ruleList = mutableListOf<Rule>()
// Log.i("CsvTopFragment", "csvList[0] ${csvList[0]}")
csvList.forEach {
val dateStr = it[0]
val prohibitSleepTwice = if(it[1].toInt() == 1) true else false
val stretchBeforeSleep = if(it[2].toInt() == 1) true else false
val fixingWakeUpTime = if(it[3].toInt() == 1) true else false
val exerciseAfterWakeUp = if(it[4].toInt() == 1) true else false
val exerciseAfterGoHome = if(it[5].toInt() == 1) true else false
if(isDateStringReadable(dateStr)) {
val rule = Rule(
date = dateStringToTimeStamp(dateStr),
prohibitSleepTwice = prohibitSleepTwice,
stretchBeforeSleep = stretchBeforeSleep,
fixingWakeUpTime = fixingWakeUpTime,
exerciseAfterWakeUp = exerciseAfterWakeUp,
exerciseAfterGoHome = exerciseAfterGoHome
)
ruleList.add(rule)
}
}
return ruleList
}
private fun datetimeStringToTimeStamp(datetimeStr: String): Long {
val regex = Regex("^([0-9]{4})/([0-9]{2})/([0-9]{2}) ([0-9]{1,2}):([0-9]{1,2})")
val match = regex.find(datetimeStr)
Log.i("CsvTopFragment", "datetimeStr: ${datetimeStr}")
Log.i("CsvTopFragment", "match: ${match}")
val group = match!!.groupValues
// Log.i("CsvTopFragment", "group: ${group}")
val year = group[1].toInt()
val month = group[2].toInt()
val date = group[3].toInt()
val hour = group[4].toInt()
val minute = group[5].toInt()
val calendarSleepDatetime = Calendar.getInstance()
calendarSleepDatetime.timeZone = TimeZone.getTimeZone("Asia/Tokyo")
calendarSleepDatetime.set(Calendar.YEAR, year)
calendarSleepDatetime.set(Calendar.MONTH, month - 1)
calendarSleepDatetime.set(Calendar.DATE, date)
calendarSleepDatetime.set(Calendar.HOUR_OF_DAY, hour)
calendarSleepDatetime.set(Calendar.MINUTE, minute)
calendarSleepDatetime.set(Calendar.SECOND, 0)
calendarSleepDatetime.set(Calendar.MILLISECOND, 0)
// Log.i("CsvTopFragment", "datetime: ${calendarSleepDatetime.time}")
return calendarSleepDatetime.time.time
}
private fun isDatetimeStringReadable(datetimeStr: String): Boolean {
val regex = Regex("^([0-9]{4})/([0-9]{2})/([0-9]{2}) ([0-9]{1,2}):([0-9]{1,2})$")
return regex.containsMatchIn(datetimeStr)
}
private fun dateStringToTimeStamp(dateStr: String): Long {
val regex = Regex("^([0-9]{4})/([0-9]{2})/([0-9]{2})")
val group = regex.find(dateStr)!!.groupValues
val year = group[1].toInt()
val month = group[2].toInt()
val date = group[3].toInt()
val calendarFromDateString = Calendar.getInstance()
calendarFromDateString.timeZone = TimeZone.getTimeZone("Asia/Tokyo")
calendarFromDateString.set(Calendar.YEAR, year)
calendarFromDateString.set(Calendar.MONTH, month - 1)
calendarFromDateString.set(Calendar.DATE, date)
calendarFromDateString.set(Calendar.HOUR_OF_DAY, 0)
calendarFromDateString.set(Calendar.MINUTE, 0)
calendarFromDateString.set(Calendar.SECOND, 0)
calendarFromDateString.set(Calendar.MILLISECOND, 0)
return calendarFromDateString.time.time
}
private fun isDateStringReadable(datetimeStr: String):Boolean {
val regex = Regex("^([0-9]{4})/([0-9]{2})/([0-9]{2})")
return regex.containsMatchIn(datetimeStr)
}
}
NavigationUI.onNavDestinationSelected(item, requireView().findNavController()) returns true in first navigation flow but returns false in second navigation flow's last navigation.
Related
We are developing an app that determines whether or not to board an escalator by applying changes in sensor data to a function. Here, I made a separate escalator judgment part, but the change of the sensor is not applied and only the initial declared value of 0 continues to come in. Why does this happen?
import android.content.Context
import android.graphics.Color
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.core.view.ContentInfoCompat.Flags
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.w3c.dom.Text
import kotlin.math.abs
import kotlin.math.sign
class MainActivity : AppCompatActivity(), SensorEventListener {
// ----------------------- Sensor var -------------------------
// Pressure
private var thresholdPreMin = 0.01f
private var thresholdPreMax = 0.1f
private var currPressure = 0f
// Acceleration
private var thresholdAccMin = 0.15f
private var currAccX = 0f
private var currAccY = 0f
private var currAccZ = 0f
// Common
private var onEsCheckCount = 4
// ----------------------- Filter -----------------------
private var filterWindow = 8
// Sensor Manager
private val mSensorManager by lazy {
getSystemService(Context.SENSOR_SERVICE) as SensorManager
}
private lateinit var escalatorDetector: EscalatorDetector
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnPreVar : Button = findViewById(R.id.btn_pre_min)
val preMinVar : EditText = findViewById(R.id.pre_min_var)
val btnAccVar : Button = findViewById(R.id.btn_acc_min)
val accMinVar : EditText = findViewById(R.id.acc_min_var)
val btnCheckCount : Button = findViewById(R.id.btn_pre_check_count)
val etCheckCount : EditText = findViewById(R.id.pre_check_count)
btnPreVar.setOnClickListener{
val minpreVar = preMinVar.text.toString()
thresholdPreMin = minpreVar.toFloat()
}
btnAccVar.setOnClickListener{
val minaccVar = accMinVar.text.toString()
thresholdAccMin = minaccVar.toFloat()
}
btnCheckCount.setOnClickListener{
val checkVar = etCheckCount.text.toString()
onEsCheckCount = checkVar.toInt()
}
start()
}
private fun start(){
// FindViewById List
val tvPreList : TextView = findViewById(R.id.tv_pre_list)
val tvPre : TextView = findViewById(R.id.tv_pre)
val tvPreCount : TextView = findViewById(R.id.tv_pre_count)
val tvSituation : TextView = findViewById(R.id.tv_situation)
val tvAccXCount : TextView = findViewById(R.id.tv_accXCount)
val tvAccYCount : TextView = findViewById(R.id.tv_accYCount)
val tvAccZCount : TextView = findViewById(R.id.tv_accZCount)
val tvPreBigCount : TextView = findViewById(R.id.tv_pre_big_count)
escalatorDetector = EscalatorDetector(onEsCheckCount, filterWindow, thresholdPreMin, thresholdPreMax, thresholdAccMin)
escalatorDetector.esJudgmentStart(currPressure, currAccX, currAccY, currAccZ, tvPreList, tvPre, tvPreCount, tvSituation, tvAccXCount, tvAccYCount, tvAccZCount, tvPreBigCount)
}
// -------------------- Sensor -----------------------
override fun onResume() {
super.onResume()
mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE)?.also {
pressure -> mSensorManager.registerListener(
this, pressure, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI
)
}
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also {
accelerometer -> mSensorManager.registerListener(
this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI
)
}
}
override fun onPause() {
super.onPause()
mSensorManager.unregisterListener(this)
}
// pressure and acceleration
override fun onSensorChanged(event: SensorEvent) {
val tvPreValue: TextView = findViewById(R.id.tv_pre_value)
if (event.sensor.type == Sensor.TYPE_PRESSURE) {
currPressure = event.values[0]
tvPreValue.text = "PreMin:$thresholdPreMin\nAccMin:$thresholdAccMin\nCheckCount:${onEsCheckCount}\nCurrPre:${currPressure}\nWindowSize:${filterWindow}"
}
if(event.sensor.type == Sensor.TYPE_ACCELEROMETER){
currAccX = event.values[0]
currAccY = event.values[1]
currAccZ = event.values[2]
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
// Do nothing
}
}
package com.example.checkonescalator
import android.os.Looper
import android.widget.TextView
import kotlin.math.abs
class EscalatorDetector(private val onEsCheckCount : Int, private val filterWindow: Int,
private val thresholdPreMin : Float, private val thresholdPreMax : Float,
private val thresholdAccMin : Float) {
// Pressure Variable
private var preList : ArrayList<Float> = arrayListOf()
private var preChanges : ArrayList<Float> = arrayListOf()
private var preUpCount = 0
private var preDownCount = 0
private var preBigUpCount = 0
private var preBigDownCount = 0
private var prevPressure = 0f
// Acc Variable
private val accXList : ArrayList<Float> = arrayListOf()
private val accYList : ArrayList<Float> = arrayListOf()
private val accZList : ArrayList<Float> = arrayListOf()
private val accXChanges : ArrayList<Float> = arrayListOf()
private val accYChanges : ArrayList<Float> = arrayListOf()
private val accZChanges : ArrayList<Float> = arrayListOf()
private var accXCount = 0
private var accYCount = 0
private var accZCount = 0
private var prevAccX = 0f
private var prevAccY = 0f
private var prevAccZ = 0f
fun esJudgmentStart(currPre : Float, currAccX : Float, currAccY : Float, currAccZ : Float, // 이 앱에서만 쓰이는 변수
tvPreList: TextView,
tvPre: TextView,
tvPreCount: TextView,
tvSituation: TextView,
tvAccXCount: TextView,
tvAccYCount: TextView,
tvAccZCount: TextView,
tvPreBigCount: TextView){
val filter = MovingAvgFilter(filterWindow)
val handler = android.os.Handler(Looper.getMainLooper())
val thread = Thread(Runnable{
while(true){
// Calculate Avg Value
val finalPre = filter.makeAverageFilter(preList, currPre)
val finalAccX = filter.makeAverageFilter(accXList, currAccX)
val finalAccY = filter.makeAverageFilter(accYList, currAccY)
val finalAccZ = filter.makeAverageFilter(accZList, currAccZ)
// Calculate Change in Value
val preChange = finalPre - prevPressure
val accXChange = abs(finalAccX - prevAccX)
val accYChange = abs(finalAccY - prevAccY)
val accZChange = abs(finalAccZ - prevAccZ)
// Add an acceleration value and see if it moves
addAccXChange(accXChange)
addAccYChange(accYChange)
addAccZChange(accZChange)
val isMoving = checkMoving()
// Add an Pressure value and check
addPreChange(preChange)
// --------------- Using in this app -----------------------
// Update UI background thread
handler.post {
// Update UI here
val esSituation = judgmentEscalator(isMoving, preChange)
tvSituation.text = esSituation
// Output Up or Down Count and each preList with a new line
updateAccCountUI(tvAccXCount, tvAccYCount, tvAccZCount)
updatePreCountUI(tvPreCount, tvPreBigCount)
updatePreChangesUI(tvPreList)
// Display Value
tvPre.text = "preChange : ${preChange}\navgPre : ${finalPre}\nprePre : ${prevPressure}\n" +
"avgAccX : ${finalAccX}\navgAccY : ${finalAccY}\navgAccZ : ${finalAccZ}\n" +
"accChanges : ${accXChange}\n${accYChange}\n${accZChange}"
}
// Save the value on cycle ago
prevPressure = finalPre
prevAccX = finalAccX
prevAccY = finalAccY
prevAccZ = finalAccZ
// Delay
Thread.sleep(500)
}
})
thread.start()
}
fun judgmentEscalator(isMoving : Boolean, preChange : Float): String {
// Count Up or Down Count
preDownCount = preChanges.count(){it > thresholdPreMin}
preUpCount = preChanges.count(){it < -thresholdPreMin}
// Count Up or Down Count for check Elevator
preBigDownCount = preChanges.count(){it > thresholdPreMax}
preBigUpCount = preChanges.count(){it < -thresholdPreMax}
// Display
if(!isMoving){
if(preDownCount >= onEsCheckCount && preChange >= 0){
if(preBigDownCount >= onEsCheckCount){
return "Going down the Elevator"
} else{
return "Going down the escalator"
}
} else if(preUpCount >= onEsCheckCount && preChange <= 0){
if(preBigUpCount >= onEsCheckCount){
return "Going Up the Elevator"
} else {
return "Going up the escalator"
}
} else {
return "Currently on the ground"
}
} else {
return "It's not stationary"
}
}
fun updatePreCountUI(tvPreCount : TextView, tvPreBigCount : TextView){
tvPreCount.text = "Escalator\nUp : ${preUpCount}\nDown : ${preDownCount}"
tvPreBigCount.text = "Elevator\nUp : ${preBigUpCount}\nDown : ${preBigDownCount}"
}
fun updatePreChangesUI(tvPreList: TextView){
val preList = preChanges.joinToString(separator = "\n")
tvPreList.text = preList
}
fun addPreChange(change: Float){
if(preChanges.size == filterWindow) preChanges.removeAt(0)
preChanges.add(change)
}
fun checkMoving():Boolean{
accXCount = accXChanges.count(){it > thresholdAccMin}
accYCount = accYChanges.count(){it > thresholdAccMin}
accZCount = accZChanges.count(){it> thresholdAccMin}
if (accXCount >= onEsCheckCount || accYCount >= onEsCheckCount || accZCount >= onEsCheckCount) {
return true
} else {
return false
}
}
fun updateAccCountUI(tvAccXCount :TextView, tvAccYCount : TextView, tvAccZCount : TextView){
tvAccXCount.text = "accX\n${accXCount}"
tvAccYCount.text = "accY\n${accYCount}"
tvAccZCount.text = "accZ\n${accZCount}"
}
fun addAccXChange(change:Float){
if(accXChanges.size == filterWindow) accXChanges.removeAt(0)
accXChanges.add(change)
}
fun addAccYChange(change: Float){
if(accYChanges.size == filterWindow) accYChanges.removeAt(0)
accYChanges.add(change)
}
fun addAccZChange(change: Float){
if(accZChanges.size == filterWindow) accZChanges.removeAt(0)
accZChanges.add(change)
}
package com.example.checkonescalator
class MovingAvgFilter(private val filterWindow : Int) {
fun makeAverageFilter(list: ArrayList<Float>, component: Float):Float{
if(list.size == filterWindow){
list.removeAt(0)
}
list.add(component)
val sumComponent = sumArrayList(list)
val avgComponent = sumComponent / (list.size)
return avgComponent
}
fun sumArrayList(list: ArrayList<Float>):Float{
var sum = 0f
for(element in list){
sum += element
}
return sum
}
}
}
here is my code
I am really stuck.
In my code below, it shows the user the contents of his/her cart and gets information from the user. Once the user is satisfied and has entered the neededd information, then the user can click on continue, which will open up a new activity. In this new activity, I want to access the information the user entered in the previous activity.
But when I use Intent, it says NULL for both variables. In my Cart.kt I have put a comment on the line of code/variable that I need to transfer/pass to CheckoutScreen.kt
Here is my code:
Cart.kt (first activity)
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
class Cart : AppCompatActivity() {
var numKids = 0
var numAdults = 0
var totalPax = 0
var getSubtotal = 0.0
var displayTax = 0.0
var finTotal = 0.0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_cart)
val listview = findViewById<ListView>(R.id.CartItems)
listview.adapter = MyCustomAdapter(this)
}
inner class MyCustomAdapter(context: Context): BaseAdapter() {
private val mContext: Context
val listOfCartItems = intent.getStringArrayListExtra("CuriseCart")
val sizeOfCart = listOfCartItems?.size
init {
mContext = context
}
override fun getCount(): Int {
return sizeOfCart!!
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItem(position: Int): Any {
return "Testing"
}
override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup?): View {
val layoutInflater = LayoutInflater.from(mContext)
val rowFormat = layoutInflater.inflate(R.layout.listview_custom, viewGroup, false)
val cruiseTitle = rowFormat.findViewById<TextView>(R.id.cruiseTitle)
cruiseTitle.text = listOfCartItems?.get(position)
numAdults = intent.getStringExtra("numAdults")?.toInt()!!
numKids = intent.getStringExtra("numKids")?.toInt()!!
totalPax = numAdults + numKids // I need to pass this to second act
val durationLabel = rowFormat.findViewById<TextView>(R.id.durationLabel)
val costOfCruise = rowFormat.findViewById<TextView>(R.id.costOfCruise)
val totalPaxTV = rowFormat.findViewById<TextView>(R.id.totalPaxCount)
val subtotal = findViewById<TextView>(R.id.subtotal)
val tax = findViewById<TextView>(R.id.tax)
var total = findViewById<TextView>(R.id.total)
if (listOfCartItems?.get(position).equals("Bahamas Cruise")) {
val getDurationOfCruise = getString(R.string.bahamasDuration)
val getCostOfCruise = getString(R.string.bahamasCPrice)
val intCost = getString(R.string.bahamasCPriceInt)
durationLabel.text = getDurationOfCruise
totalPaxTV.text = (totalPax.toString() + " Passengers")
costOfCruise.text = getCostOfCruise
getSubtotal = (getSubtotal + intCost.toDouble())
getSubtotal = String.format("%.2f", getSubtotal).toDouble()
} else if (listOfCartItems?.get(position).equals("Caribbean Cruise")) {
val getDurationOfCruise = getString(R.string.caribbeanDuration)
val getCostOfCruise = getString(R.string.caribbeanCPrince)
val intCost = getString(R.string.caribbeanCPrinceInt)
durationLabel.text = getDurationOfCruise
totalPaxTV.text = (totalPax.toString() + " Passengers")
costOfCruise.text = getCostOfCruise
getSubtotal = (getSubtotal + intCost.toDouble())
getSubtotal = String.format("%.2f", getSubtotal).toDouble()
} else if (listOfCartItems?.get(position).equals("Cuba Cruise")) {
val getDurationOfCruise = getString(R.string.cubaDuration)
val getCostOfCruise = getString(R.string.cubaCPrice)
val intCost = getString(R.string.cubaCPriceInt)
durationLabel.text = getDurationOfCruise
totalPaxTV.text = (totalPax.toString() + " Passengers")
costOfCruise.text = getCostOfCruise
getSubtotal = (getSubtotal + intCost.toDouble())
getSubtotal = String.format("%.2f", getSubtotal).toDouble()
} else if (listOfCartItems?.get(position).equals("Sampler Cruise")) {
val getDurationOfCruise = getString(R.string.samplersDuration)
val getCostOfCruise = getString(R.string.samplersPrice)
val intCost = getString(R.string.samplersPriceInt)
durationLabel.text = getDurationOfCruise
totalPaxTV.text = (totalPax.toString() + " Passengers")
costOfCruise.text = getCostOfCruise
getSubtotal = (getSubtotal + intCost.toDouble())
getSubtotal = String.format("%.2f", getSubtotal).toDouble()
} else if (listOfCartItems?.get(position).equals("Star Cruise")) {
val getDurationOfCruise = getString(R.string.starDuration)
val getCostOfCruise = getString(R.string.starCPrice)
val intCost = getString(R.string.starCPriceInt)
durationLabel.text = getDurationOfCruise
totalPaxTV.text = (totalPax.toString() + " Passengers")
costOfCruise.text = getCostOfCruise
getSubtotal = (getSubtotal + intCost.toDouble())
getSubtotal = String.format("%.2f", getSubtotal).toDouble()
}
displayTax = getSubtotal * 0.13
finTotal = displayTax + getSubtotal // I need to pass this to second act
subtotal.text = "$" + getSubtotal.toString()
tax.text = "$" + String.format("%.2f", displayTax)
total.text = "$" + String.format("%.2f", finTotal)
return rowFormat
}
}
fun continueToCheckoutInformation(view: View) {
val intent = Intent(this#Cart, CheckoutScreen::class.java)
intent.putExtra("toalPax", totalPax)
intent.putExtra("finTotal", finTotal)
startActivity(intent)
}
}
CheckoutScreen.kt (second activity)
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
class CheckoutScreen : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_checkout_screen)
val pax = intent.getStringExtra("toalPax")
val cst = intent.getStringExtra("finTotal")
Toast.makeText(applicationContext, "Testing = $pax and $cst", Toast.LENGTH_SHORT).show()
}
}
You are putting Int values into the extras; you need to use getIntExtra() to retrieve them.
Note that getIntExtra() takes two parameters: the name of the extra and a default value to return if the extra does not have an Int with your specified name.
I get a NumberFormatException if an entry field is left blank in the app. Previously, I had a working try/catch block that would handle it but I needed to add a switch on if/else statement to determine which function to run. I've tried everything I can think of to reformat the try/catch so that error is being handled, but everything just breaks the application. What am I doing wrong here, or is it even possible to do?
package com.WordPlay.awcc
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
class Setup3 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main
)
editTextNumber2.text.toString().toInt()
editTextNumber3.text.toString().toInt()
editTextNumber4.text.toString().toInt()
editTextNumber5.text.toString().toInt()
editTextNumber6.text.toString().toInt()
editTextNumber15.text.toString().toInt()
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/// Define button for checked
fun essentialexpert() {
val int11 = editTextNumber.text.toString().toInt()
val pay21 = 5
val product11 = int11 * pay21
val int12 = editTextNumber2.text.toString().toInt()
val pay22 = 12.5
val product12 = int12 * pay22
val int13 = editTextNumber3.text.toString().toInt()
val pay23 = 15
val product13 = int13 * pay23
val int16 = editTextNumber6.text.toString().toInt()
val pay24 = 5
val product14 = int16 * pay24
val int15 = editTextNumber5.text.toString().toInt()
val pay25 = 5
val product15 = int15 * pay25
val int14 = editTextNumber4.text.toString().toInt()
val pay26 = 2
val product16 = int14 / pay26
val final =
product11 + product12 + product13 + product14 + product15 + product16
val complete = final.toString()
try {
editTextNumber15?.setText(complete)
} catch (e: NumberFormatException) {
Toast.makeText(
applicationContext,
"Please enter a 0 in the blank field",
Toast.LENGTH_LONG
).show()
}
}
/// Define function for unchecked
fun essential() {
val int1 = editTextNumber.text.toString().toInt()
val pay1 = 5
val product1 = int1 * pay1
val int2 = editTextNumber2.text.toString().toInt()
val pay2 = 7.5
val product2 = int2 * pay2
val int3 = editTextNumber3.text.toString().toInt()
val pay3 = 10
val product3 = int3 * pay3
val int6 = editTextNumber6.text.toString().toInt()
val pay4 = 5
val product4 = int6 * pay4
val int5 = editTextNumber5.text.toString().toInt()
val pay5 = 0
val product5 = int5 * pay5
val int4 = editTextNumber4.text.toString().toInt()
val pay6 = 2
val product6 = int4 / pay6
val final = product1 + product2 + product3 + product4 + product5 + product6
val complete = final.toString()
try {
editTextNumber15?.setText(complete)
} catch (e: NumberFormatException) {
Toast.makeText(
applicationContext,
"Please enter a 0 in the blank field",
Toast.LENGTH_LONG
).show()
}
}
try {
essential()
} catch (e: java.lang.NumberFormatException) {
Toast.makeText(
applicationContext,
"Please enter a 0 in the blank fields",
Toast.LENGTH_LONG
).show()
/// Define buttons to change activity
val button2 = findViewById<Button>(R.id.button2)
button2.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
val button3 = findViewById<Button>(R.id.button3)
button3.setOnClickListener {
val intent2 = Intent(this, ThirdActivity::class.java)
startActivity(intent2)
}
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
if (switch1.isChecked) {
essentialexpert()
} else {
essential()
}
}
}
}
}
You shouldn't catch errors when writing to an EditText, but when reading. Something like that:
fun essentialexpert() {
val int11 = try {
editTextNumber.text.toString().toInt()
} catch (e: NumberFormatException) {
Toast.makeText(
applicationContext,
"Please enter a 0 in the blank field",
Toast.LENGTH_LONG ).show()
return
}
val pay21 = 5
val product11 = int11 * pay21
// ...
// If you reach here, all fields are ok
val final = product11 + /* ... */ + product16
val complete = final.toString()
editTextNumber15?.setText(complete)
}
With Kotlin, you can simplify this try/catch using the toIntOrNull method.
var areAllFieldsValid = false
fun essentialexpert() {
val int11 = editTextNumber.text.toString().toIntOrNull() ?: return
val pay21 = 5
val product11 = int11 * pay21
// ...
// If you reach here, all fields are ok
areAllFieldsValid = true
val final = product11 + /* ... */ + product16
val complete = final.toString()
editTextNumber15?.setText(complete)
}
override fun onCreate(savedInstanceState: Bundle?) {
// ...
btnFinish.setOnClickListener {
essentialexpert()
if (!areAllFieldsValid) {
Toast.makeText(
applicationContext,
"Please enter a 0 in the blank fields",
Toast.LENGTH_LONG).show()
} else {
// Do something when all fields are valid
}
}
}
Some tips for you:
Replace final.toString() with "$final"
Replace Toast.makeText(applicationContext, ... with Toast.makeText(this, .... An Activity is a Context.
I'm not sure what Setup3 is, but if it only exists for getting the EditText values, it won't work. You can do it in MainActivity.
The expression 5/2 may not return what you think it returns.
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
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
}