setting language before setContentView results in unending loop - android

I have a fragment with settings of an app that saves the user selected mode and language
( a bit of code:
binding.languageButton.setOnClickListener{
builder = AlertDialog.Builder(requireContext())
builder.setTitle(getString(R.string.setLanguage))
builder.setItems(langArray) { _, which ->
val sharedPref = requireActivity().getSharedPreferences("Settings", Context.MODE_PRIVATE).edit()
sharedPref.putString("LANGUAGE", langArray[which]).apply()
checkUserPreferences()
changeLanguage(langArray[which])
binding.languageButton.text = langArray[which]
}
builder.create()
builder.show()
}
}
private fun changeLanguage(language: String) {
if(language != binding.languageButton.text.toString()){
val local = Locale(language)
val dm = resources.displayMetrics
val con = resources.configuration
con.locale = local
resources.updateConfiguration(con, dm)
val refresh = Intent(
requireContext(),
MainActivity::class.java
)
refresh.putExtra(binding.languageButton.text.toString(), language)
startActivity(refresh)
}
}
and that part (as mentioned) saves mode and selected language to sharedPreferences that I later want to use in mainActivity and other fragments, and I've put in MainActivity:
private fun loadPreferences(){
val preferences = getSharedPreferences("Settings", Activity.MODE_PRIVATE)
Log.i(TAG, preferences.getString("LANGUAGE", "eng").toString())
Log.i(TAG, preferences.getInt("MODE", 1).toString())
val local = Locale(preferences.getString("LANGUAGE", "eng").toString())
val dm = resources.displayMetrics
val con = resources.configuration
con.locale = local
resources.updateConfiguration(con, dm)
val refresh = Intent(
this.baseContext,
MainActivity::class.java
)
refresh.putExtra(preferences.getString("LANGUAGE", "eng").toString(),
preferences.getString("LANGUAGE", "eng"))
startActivity(refresh)
}
and this is referenced in:
class MainActivity : AppCompatActivity() {
// TODO: IT'S THE ONE
companion object {
private const val TAG = "MainActivity"
private const val CHANNEL_ID = "plantz_app_channel_01"
private const val notificationId = 909
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadPreferences()
setContentView(R.layout.activity_main)
// ...
and after running the app It only shows the app logo and logs sharedPreferences but the app doesn't go any further, I've tried to tweak with it for a bit but It hasn't done much, any ideas what should I change to make it work?
Thanks in advance :)

This infinity loop happens because when the MainActivity is going to be opened you call loadPreferences() in onCreate method where you open a new instance of MainActivity from there.
So the infinity loop goes like below:
onCreate() method of MainActivity is called
loadPreferences() method is called
in loadPreferences() after you have set the language from SharedPreferences you start a new instance of your MainActivity which redirects you back to step 1.
To avoid this infinity loop remove the below lines from your loadPreferences() method:
val refresh = Intent(this.baseContext,MainActivity::class.java)
refresh.putExtra(preferences.getString("LANGUAGE", "eng").toString(), preferences.getString("LANGUAGE", "eng"))
startActivity(refresh)

Related

How do I store a String for later use in Kotlin?

I am trying to store the String from the Hours and Number for use in a new activity. I have found ways of doing it with intent but do not want the strings sent through to the next activity. Is there any way of me saving my String data and being able to call it to a different activity?
class ChecksStartUp : AppCompatActivity() {
lateinit var hours: EditText
lateinit var number: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getSupportActionBar()?.hide()
setContentView(R.layout.activity_Checks_pg1)
val number = findViewById(R.id.machineFleetNumberId)
val hours = findViewById(R.id.machineHoursId)
val dButton = findViewById<RadioButton>(R.id.dButtonId)
val eButton = findViewById<RadioButton>(R.id.eButtonId)
val hButton = findViewById<RadioButton>(R.id.hButtonId)
val proceedButton = findViewById<Button>(R.id.proceedButtonId)
proceedButton.setOnClickListener {
if (dButton.isChecked()) {
val intent = Intent(this, DPage::class.java)
startActivity(intent)
}
if (eButton.isChecked()) {
val intent = Intent(this, EPage::class.java)
startActivity(intent)
}
if (hButton.isChecked()) {
val intent = Intent(this, HPage::class.java)
}
}
}
}
Alot of different ways to store data. Simplest way is to create singleton class - object.
In Android studio create new Kotlin file/class, choose Object.
this is short example:
object GlobalVariables { var hourses:String?="" var number:Int?= null }
access:
GlobalVariables.number=3 - set
val number =GlobalVariables.number - get
You can access this data everywhere you want in your classes, fragments and activites , but remember - when app is terminated data is immediately lost.

view binding isn't working on Button setOnClickListner... [*ERROR = Variable 'getOTPButton' is never used] (KOTLIN)

please check out this error. I have used view binding on this activity to save data in Firebase. I have used the If... else statement on setOnClickListner.
Code for the Activity:
class RegisterSelection : AppCompatActivity() {
private lateinit var binding: ActivityRegisterSelectionBinding
private lateinit var database: DatabaseReference
#SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityRegisterSelectionBinding.inflate(layoutInflater)
setContentView(binding.root)
setContentView(R.layout.activity_register_selection)
val checkPasswordTwo = findViewById<CheckBox>(R.id.checkPasswordTwo)
val passwordText = findViewById<EditText>(R.id.passwordText)
val confirmPassText = findViewById<EditText>(R.id.confirmPassText)
val fullName=findViewById<EditText>(R.id.fullName).text.toString()
val phoneNumberEdit=findViewById<EditText>(R.id.phoneNumberEdit).text.toString()
val getOTPButton = findViewById<Button>(R.id.getOTPButton)
binding.getOTPButton.setOnClickListener {
if(fullName.isEmpty() && phoneNumberEdit.isEmpty()){
Toast.makeText(applicationContext,"Fill all the Fields!",Toast.LENGTH_LONG).show()
}
else{
database = FirebaseDatabase.getInstance().getReference("UserDataClass")
val users = UserDataClass(fullName, phoneNumberEdit)
database.child(fullName).setValue(users).addOnSuccessListener {
binding.fullName.text.clear()
binding.phoneNumberEdit.text.clear()
Toast.makeText(applicationContext,"Success!",Toast.LENGTH_SHORT).show()
}.addOnFailureListener {
Toast.makeText(applicationContext,"Failed to Save!",Toast.LENGTH_SHORT).show()
}
val intent = Intent(this, getOTP::class.java)
startActivity(intent)
}
}
checkPasswordTwo.setOnClickListener {
if (checkPasswordTwo.text.toString() == "Show Password"){
passwordText.transformationMethod = HideReturnsTransformationMethod.getInstance()
confirmPassText.transformationMethod = HideReturnsTransformationMethod.getInstance()
checkPasswordTwo.text = "Hide Password"
} else{
passwordText.transformationMethod = PasswordTransformationMethod.getInstance()
confirmPassText.transformationMethod = PasswordTransformationMethod.getInstance()
checkPasswordTwo.text = "Show Password"
}
}
}
}
build.gradle
buildFeatures{ viewBinding true }
UserDataClass
data class UserDataClass(val fullName: String? = null, val phoneNumberEdit: String? = null)
Output only shows the Toast "fill all the fields."
Any suggestions would be grateful.
I recommend,if(fullName.isEmpty() && phoneNumberEdit.isEmpty()) change this to if(fullName.isEmpty() || phoneNumberEdit.isEmpty()){ - you want to ensure that both are filled I assume.
Secondly, since you are using binding, I would also recommend that you check for null this way: binding.<edit text id>.text.isNullOrEmpty().
Lastly, it looks like you are initialising your text values by reading the text value in the edit text post inflation and not reading the values you have entered on button click. In your onClickListener, read the values from your editText and then check for null.

Changing the App Language not working correctly (Kotlin)

I have a Splash screen in my kotlin app, where I do some stuff like loading data. Currently it is only in german and I wanted to add english as additional language.
I've create the strings resources and so on.
Now I have added a bit of code to the Splash-Screen, where it checks, if the user already set a preferenced language (saved in sharedPreferences).
When there is nothing saved, a simple alertdialog pops up asking the user for his preference and sets everything accordingly.
Then the splashscreen is reloaded. When I choose english on my device (system language is german), the splash screen gets the correct string resources (english).
But when it changes to the MainActivity, it is german again. Only after restarting the app, everything is in english.
Here is how I change the language on my splashscreen:
private lateinit var mainIntent : Intent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
var localeToSet: String? = prefs.getString("language", "")
if(localeToSet == null || localeToSet.equals("")){
val languages = arrayOf("Deutsch", "English")
val langSelectorBuilder = AlertDialog.Builder(this)
langSelectorBuilder.setTitle(R.string.selectLanguageText)
langSelectorBuilder.setCancelable(false)
langSelectorBuilder.setSingleChoiceItems(languages, -1) { dialog, selection ->
when(selection) {
0 -> {
localeToSet = "de"
setLocale("de")
}
1 -> {
localeToSet = "en"
setLocale("en")
}
}
recreate()
dialog.dismiss()
}.setOnDismissListener {
Handler().postDelayed({
startActivity(mainIntent)
finish()
}, SPLASH_TIME_OUT)
}
langSelectorBuilder.create().show()
}
else{
setLocale(localeToSet!!)
Handler().postDelayed({
startActivity(mainIntent)
finish()
}, SPLASH_TIME_OUT)
}
setContentView(R.layout.activity_splash_screen)
}
private fun createIntent(localeToSet: String){
mainIntent = Intent(this#SplashScreenActivity, MainActivity::class.java).apply {
//Do stuff and pass data to mainactivity
}
private fun setLocale(localeToSet: String) {
createIntent(localeToSet)
val config = resources.configuration
val locale = Locale(localeToSet)
Locale.setDefault(locale)
config.locale = locale
resources.updateConfiguration(config, resources.displayMetrics)
val prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
val editor: SharedPreferences.Editor = prefs.edit()
editor.putString("language", localeToSet)
editor.apply()
}
Nevermind, found the issue:
val langSelectorBuilder = AlertDialog.Builder(this)
langSelectorBuilder.setTitle(R.string.selectLanguageText)
langSelectorBuilder.setCancelable(false)
langSelectorBuilder.setSingleChoiceItems(languages, -1) { dialog, selection ->
when(selection) {
0 -> {
localeToSet = "de"
setLocale("de")
}
1 -> {
localeToSet = "en"
setLocale("en")
}
}
recreate()
dialog.dismiss()
}.setOnDismissListener {
finish()
}
langSelectorBuilder.create().show()
Without starting the MainActivity here, it works like it should

How to add new object in Kotlin SharedPreferences

I have this preference manager
class JournalManager {
lateinit var pref: SharedPreferences
lateinit var editor: SharedPreferences.Editor
lateinit var con: Context
var PRIVATE_MODE: Int = 0
constructor(con: Context?) {
if (con != null) {
this.con = con
}
if (con != null) {
pref = con.getSharedPreferences(PREF_NAME,PRIVATE_MODE)
}
editor = pref.edit()
}
companion object {
val PREF_NAME: String = "Journal"
val KEY_TEXT: String = "text"
}
fun createJournalSession(
text: EditText,
) {
editor.putString(KEY_TEXT, text.toString())
editor.commit()
}
fun getJournalDetails(): Map<String, String>
{
var journal: Map<String, String> = HashMap<String, String>()
pref.getString(KEY_TEXT,null)?.let { (journal as HashMap).put(KEY_TEXT, it) }
return journal
}
fun DeleteJournal() {
editor.clear()
editor.commit()
var i: Intent = Intent(con, JournalActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
con.startActivity(i)
}
}
And I'm looking for solution to add objects in it but my app is crashing, here is a sample of how I try to add object
lateinit var journalSession: JournalManager
override fun onCreate(savedInstanceState: Bundle?) {...}
fun openDialog() {
val dialog = MaterialDialog(this)
.noAutoDismiss()
.customView(R.layout.layout_new_journal)
//set initial preferences
dialog.findViewById<Button>(R.id.save_btn).setOnClickListener{
val note = dialog.findViewById<EditText>(R.id.new_journal_input)
//add to preference
journalSession.createJournalSession(
note
)
dialog.dismiss()
}
dialog.findViewById<Button>(R.id.cancel_btn).setOnClickListener {
dialog.dismiss()
}
dialog.show()
}
Any suggestion?
Update
Let me make it clear what I'm looking for:
At the beginning I do not have any data, nor shared preference in device (shared preference will create when user saves it's first journal).
2.When user adds new journal it suppose to be stored (as of sample) like this
journal [{
note="this was user first note"
}]
Then when next time user adds new journal, it suppose to be stored (as of sample) like this
journal [{
note="this was user first note"
},
{note="this was user second note"
}]
and so on...
PS So far all videos, articles anything else I've found on web was with same logic: (they've had a List and then store that list into preferences!), my case is not like that, I do not have any list to store my list will be created one by one, during the time just like any real world apps.
Now, any idea how to make it happen, and what should I change in my code?
SharedPreferences saves primitive type data and Srting. For saving object like List you can use GSON library (by Google) to convert the object into JSON String.
1. Import Dependency:
implementation 'com.google.code.gson:gson:2.8.6'
2. Basic Usage:
val studentJsonString = Gson().toJson(student) //object -> String
val student = Gson().fromJson(studentJsonString, Student.class) //String -> object
3. For List:
val typeToken = TypeToken<List<Student>>(){}
val students = Gson().fromJson(studentsJsonString, typeToken.getType())

How to add items from shared preferences to two spinners in one Kotlin Activity

I have an activity with two spinners. I have made arrays for each spinner containing data from popular foods, but I want the user to be able to add three of their own selections to the lists. The app compiles and installs and runs, BUT when I select the specific activity, the screen closes and either goes to the apps main screen or to the emulator's home screen. Logcat shows:-
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.kotlinsql/com.example.kotlinsql.CarbsInput}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.SharedPreferences android.content.Context.getSharedPreferences(java.lang.String, int)' on a null object reference
It's where I call the shared preferences.
I have tried different contexts but still get errors that vary slightly according to the context and I have included them in the code as remarks.
I have tried moving everything into onCreate, but this gives me an error in the class definition line, because the function "override fun onItemSelected" seems to have to be stand-alone, so must be outside onCreate.
Please help. I have only been learning this for less than a year, and I apologise for any stupid mistakes. No offence is intended.
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.*
import kotlinx.android.synthetic.main.input_carbs.*
import java.time.Clock
import java.time.LocalDateTime
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import android.content.SharedPreferences
import android.content.res.Configuration
import java.security.AccessController.getContext
import kotlin.math.*
class CarbsInput : AppCompatActivity(),AdapterView.OnItemSelectedListener {
var spinner:Spinner? = null
var spinner2:Spinner? = null
val sharedPrefFile = "greenbandbasicpreference"
val sharedPreferences: SharedPreferences by lazy { getSharedPreferences(sharedPrefFile, MODE_PRIVATE) }
val dataModel: CarbsInputModel by lazy { CarbsInputModel(sharedPreferences) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.input_carbs)
spinner = this.gi_spinner
spinner2 = this.carbs_per_spinner
// Create an ArrayAdapter using a simple spinner layout and gIndices array
val aa = ArrayAdapter(this, android.R.layout.simple_spinner_item, dataModel.gIndices)
// Set layout to use when the list of choices appear
aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// Set Adapter to Spinner
spinner!!.setAdapter(aa)
//spinner!!.setSelection(9)//optional, better to leave favourites at top
val aa2 = ArrayAdapter(this, android.R.layout.simple_spinner_item, dataModel.carbsPer)
aa2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner2!!.setAdapter(aa2)
input_carbs_btn.setOnClickListener {//set an onclick listener
enterCarbs() }
backbtn.setOnClickListener {
val fourth = Intent(this, MainActivity::class.java)//sets "fourth" to be MainActivity
// start your next activity
startActivity(fourth) }
btn_view_carbs.setOnClickListener { viewCarbs() }
btn_carb_calc.setOnClickListener {
var carbPer = et_carbsper.text.toString().toLong()
var weight = et_weight.text.toString().toLong()
var carbs = round((weight * carbPer) /100.0).toLong()
et_carbs.setText(carbs.toString())
}//end of button onClick listener
}//end of on create
fun enterCarbs(){//get inputs from keys and calculate carbLife using GI
var noow = ZonedDateTime.now(Clock.systemUTC())
var noowSecs: Long = noow.toEpochSecond()
var noowMins: Long = (noowSecs) / 60
//var carbLife:Long = 220// this has to be calculated from GI
var nowLocal = LocalDateTime.now()
var carbTime: Long = noowMins-1
var showCarbTime: String = nowLocal.format(DateTimeFormatter.ofPattern("E d MMM kk:mm "))+"local"
var sharedPrefFile = "greenbandbasicpreference"
val sharedPreferences: SharedPreferences = getSharedPreferences(sharedPrefFile, Context.MODE_PRIVATE)
val databaseHandler: DatabaseHandler = DatabaseHandler(this)
if (et_carbs.text.toString().trim() != "" && et_carbGI.text.toString().trim() != "") {
val carbs = et_carbs.text.toString().toLong()
val carbGi = (et_carbGI.text.toString().toLong())
//val carbLife = 12_000 /carbGi.toLong()// to be replaced with 1-(X/L)^n calculation in stage2
var carbDecayIndex:Double= sharedPreferences.getFloat("carbDecayIndex_key",0.8F).toDouble()//n
//public fun carbLifeCalc():Double//L = 10^((log120^n-logGI)/n)
var logLtoN = log10(120.00.pow(carbDecayIndex))//log120^n
var logGi = log10(carbGi / 100.00)//logGI
var carbLife = 10.00.pow((logLtoN - logGi) / carbDecayIndex).toLong()//gives L
//end of carbLifeCalculation
val status =
databaseHandler.saveCarbs(CarbsModelClass(carbTime, showCarbTime, carbs, carbGi, carbLife))
if (status > -1) {
Toast.makeText(applicationContext, "Carbohydrate saved", Toast.LENGTH_LONG).show()
//MainActivity.evaluateCarbs //want to call this function from here without writing it again
et_carbs.text.clear()
et_carbGI.text.clear()
}
} else {
Toast.makeText(
applicationContext,
"No field can be blank enter GI as 50 if unknown",
Toast.LENGTH_LONG
).show()
}
}//end of function entercarbs
fun viewCarbs() {
//creating the instance of DatabaseHandler class
val databaseHandler: DatabaseHandler = DatabaseHandler(this)
//calling the viewCarbs method of DatabaseHandler class to read the records
val carbohs: List<CarbsModelClass> = databaseHandler.viewCarbs()
//val carbohsArraycarbTime = Array<String>(carbohs.size) { "null" }//not needed
val carbohsArrayshowCarbTime = Array<String>(carbohs.size) { "null" }
val carbohsArraycarbs = Array<String>(carbohs.size) { "null" }
val carbohsArraycarbGI = Array<String>(carbohs.size) { "null" }
val carbohsArraycarbLife = Array<String>(carbohs.size) { "null" }
var index = 0
for (e in carbohs) {
//carbohsArraycarbTime[index] = e.carbTime.toString()//not needed
carbohsArrayshowCarbTime[index] = e.showCarbTime
carbohsArraycarbs[index] = e.carbs.toString()
carbohsArraycarbGI[index] = e.carbGi.toString()//note small i inGi
carbohsArraycarbLife[index] = e.carbLife.toString()
//index--
index++
}
//creating custom ArrayAdapter
val myCarbListAdapter = CarbListAdapter(
context = this,
//carbTime = carbohsArraycarbTime,//not needed
showCarbTime = carbohsArrayshowCarbTime,
carbs = carbohsArraycarbs,
carbGI = carbohsArraycarbGI,
carbLife = carbohsArraycarbLife
)
lv_carb_view.adapter = myCarbListAdapter
}//end of fun view carbs
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
// see https://stackoverflow.com/questions/9262871/android-two-spinner-onitemselected
if(parent?.getId() == R.id.gi_spinner) {
var giFullSelected = dataModel.gIndices[position]
var gIprelimString : String =
giFullSelected[0].toString() + giFullSelected[1]//selecting just digits
var GIprelim = gIprelimString.toLong()
et_carbGI.setText(gIprelimString)
}//end of first if
else{ if (parent?.getId() == R.id.carbs_per_spinner) {
var carbPerFullSelected = dataModel.carbsPer[position]
var carbPerString: String =
carbPerFullSelected[0].toString() + carbPerFullSelected[1]
var carbPer = carbPerString.toLong()
et_carbsper.setText(carbPerString)
var weight = et_weight.text.toString().toLong()
var carbs = round((weight * carbPer) /100.0).toLong()
et_carbs.setText(carbs.toString())}//end of second if
else { Toast.makeText(applicationContext, "parent id "+parent?.getId().toString(), Toast.LENGTH_LONG).show()
}//end of second else
}//end of elseif OR /first else
}//end of on item selected
override fun onNothingSelected(parent: AdapterView<*>?) { }
}//end of class carbs input
New Class CarbsInputModel Below
//start of CarbsInputModel
import android.content.SharedPreferences
class CarbsInputModel(private val sharedPreferences:SharedPreferences) {
// val sharedPrefFile = "greenbandbasicpreference"
//val sharedPreferences:SharedPreferences = getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
val sharedFav1Value: String? = sharedPreferences.getString("fav1_key", "50 50 defaultone")
val sharedFav2Value: String? = sharedPreferences.getString("fav2_key", "50 50 defaultwo")
val sharedFav3Value: String? = sharedPreferences.getString("fav3_key", "50 50 defaulthree")
val favDescr1:String = sharedFav1Value?.takeLastWhile { !it.isDigit() }.toString().trim()
val favDescr2:String = sharedFav2Value?.takeLastWhile { !it.isDigit() }.toString().trim()
val favDescr3:String = sharedFav3Value?.takeLastWhile { !it.isDigit() }.toString().trim()
val favData1:String = sharedFav1Value?.takeWhile { !it.isLetter() } .toString()
val favData2:String = sharedFav2Value?.takeWhile { !it.isLetter() } .toString()
val favData3:String = sharedFav3Value?.takeWhile { !it.isLetter() } .toString()
val favCarbPerString1 = favData1.take(3).trim()
val favCarbPerString2 = favData2.take(3).trim()
val favCarbPerString3 = favData3.take(3).trim()
val favGiString1 = favData1.takeLast(4).trim()
val favGiString2 = favData2.takeLast(4).trim()
val favGiString3 = favData3.takeLast(4).trim()
val favFullCarbPer1 = favCarbPerString1+" "+favDescr1+" "
val favFullCarbPer2 = favCarbPerString2+" "+favDescr2+" "
val favFullCarbPer3 = favCarbPerString3+" "+favDescr3+" "
val favFullGi1 = favGiString1+" "+favDescr1+" "
val favFullGi2 = favGiString2+" "+favDescr2+" "
val favFullGi3 = favGiString3+" "+favDescr3+" "
}//end of class Carbs Input Model
Attempted tidy code for override function. This still does absolutely Nothing
//trying to tidy up code
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
when {
parent.id == R.id.gi_spinner -> {
var giFullSelected = dataModel.gIndices[position]
var gIprelimString: String =
giFullSelected[0].toString() + giFullSelected[1]//selecting just leading digits
et_carbGI.setText(gIprelimString)
}
parent.id == R.id.carbs_per_spinner -> {
var carbPerFullSelected = dataModel.carbsPer[position]
var carbPerString: String =
carbPerFullSelected[0].toString() + carbPerFullSelected[1]
var carbPer = carbPerString.toLong()
et_carbsper.setText(carbPerString)
var weight = et_weight.text.toString().toLong()
var carbs = round((weight * carbPer) / 100.0).toLong()
et_carbs.setText(carbs.toString())
}
else -> {
Toast.makeText(applicationContext, "parent id " + parent?.getId().toString(),
Toast.LENGTH_LONG ).show()
}
}//end of when
}//end of override fun onitemselected
When you assign a value at the declaration site like this:
val sharedPreferences:SharedPreferences = getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
the function(s) you are calling to create the object that will be assigned to the property is getting called at the time the Activity is instantiated by Android. Unfortunately, this is too early to be calling anything that relies on the Activity being fully instantiated and set up, for example, anything that needs the Context as a constructor parameter.
The easy fix for this is to make these properties instantiate themselves lazily, so they are created after the Activity is already fully instantiated:
val sharedPreferences: SharedPreferences by lazy { getSharedPreferences(sharedPrefFile, MODE_PRIVATE) }
An alternate solution is the use a lateinit var and prepare the item in onCreate():
lateinit var sharedPreferences: SharedPreferences
// ...
override fun onCreate(bundle: SavedInstanceState) {
super.onCreate(bundle)
sharedPreferences = getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
}
I usually prefer the lazy method because it avoid splitting the declaration and the assignment so the code is easier to read. And it allows you to use val instead of var so the intent is clearer.
However, you also have many properties that are reliant on the SharedPreference instance, so they would all have to use one of the above solutions as well, which will lead to very verbose code. I recommend that you move all of these properties into a separate class that uses the SharedPreferences as a constructor paraamter. For example:
class CarbsInputModel(private val sharedPreferences: SharedPreferences) {
val sharedFav1Value: String? = sharedPreferences.getString("fav1_key", "50 50 defaultone")
val sharedFav2Value: String? = sharedPreferences.getString("fav2_key", "50 50 defaultwo")
val sharedFav3Value: String? = sharedPreferences.getString("fav3_key", "50 50 defaulthree")
// etc...
}
and then in your activity:
class CarbsInput : AppCompatActivity(),AdapterView.OnItemSelectedListener {
var spinner:Spinner? = null
var spinner2:Spinner? = null
val sharedPrefFile = "greenbandbasicpreference"
val sharedPreferences: SharedPreferences by lazy { getSharedPreferences(sharedPrefFile, MODE_PRIVATE) }
val dataModel: CarbsInputModel by lazy { CarbsInputModel(sharedPreferences) }
}
And then access your properties through the dataModel property. It is also better design practice to separate your UI and your functions that modify the data, so you could put those functions in your data model class.
You might also want to read up on how to use a ViewModel class. It would possibly be a more scalable solution than what I put above.

Categories

Resources