There are two classes MainActivity and PickTimeForNotif in my project. In MainActivity getSharedPreferences works just fine, i can save my data and get it back. In PickTimeForNotif, however, the same method seems to do nothing.
Here's my simplified MainActivity class:
class MainActivity : AppCompatActivity(), ChangeCupDialogFragment.StringListener {
#SuppressLint("SetTextI18n")
//this is variable i'm saving
private var drankToday = 0
//function in which i save my value to SharedPreferences
private fun saveWaterCountToInternalStorage(clickCounter: Int) {
val sharedPref = this.getSharedPreferences("something", Context.MODE_PRIVATE)
with (sharedPref.edit()){
putInt(getString(R.string.clickCount), clickCounter)
apply()
}
}
//and here i get it from there
private fun loadWaterCountToInternalStorage(): Int {
val sharedPref = this.getSharedPreferences("something", Context.MODE_PRIVATE)
return sharedPref.getInt(getString(R.string.clickCount), drankToday)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
val setupNotifButton = findViewById<Button>(R.id.setupNotifButton)
setupNotifButton.setOnClickListener{
val notifIntent = Intent(applicationContext, PickTimeForNotif::class.java)
startActivity(notifIntent)
}
}
}
In setOnClickListener i intend my second activity PickTimeForNotif, here it is.
class PickTimeForNotif: AppCompatActivity(), TimePickerFragment.OnCompleteListener {
val APP_PREFERENCES = "settings"
private val SAVED_FROM_HOUR = "SetFromHour"
private var FROM_HOUR = 99
private fun saveTimeToInternalStorage(prefName1: String, Hour:Int) {
val sharedPref = this.getSharedPreferences(APP_PREFERENCES, MODE_PRIVATE)
with (sharedPref.edit()){
putInt(prefName1, Hour)
apply()
}
}
private fun loadTimeFromInternalStorage() {
val sharedPref = this.getSharedPreferences(APP_PREFERENCES, MODE_PRIVATE)
if (sharedPref.contains(APP_PREFERENCES)) {
sharedPref.getInt(SAVED_FROM_HOUR, FROM_HOUR)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.pick_time_activity)
saveTimeToInternalStorage(SAVED_FROM_HOUR, 1)
loadTimeFromInternalStorage()
Toast.makeText(applicationContext,"$FROM_HOUR", Toast.LENGTH_SHORT).show()
}
}
In the code above i'm trying to set value (1 for example ) to a SAVED_FROM_HOUR key and then get it back and assign to FROM_HOUR variable. However, the Toast shows 99, which means that new data wasn't loaded properly. I tried putting all code from loadTimeFromInternalStorage and saveTimeToInternalStorage to onCreate, but the result is same.
I also tried checking if the Preferences file exists after i call getSharedPreferences with
if (sharedPref.contains(APP_PREFERENCES))
but it does not.
So i'm asking to explain what am i doing wrong and why i can save the data in my MainActivity, but not in the second one. Thanks alot to anyone in advance!!
In loadTimeFromInternalStorage(), you are fetching the value but not assigning to variable like this:
private fun loadTimeFromInternalStorage() {
val sharedPref = this.getSharedPreferences(APP_PREFERENCES, MODE_PRIVATE)
if (sharedPref.contains(APP_PREFERENCES)) {
FROM_HOUR = sharedPref.getInt(SAVED_FROM_HOUR, FROM_HOUR)
}
}
Also, in this line FROM_HOUR = sharedPref.getInt(SAVED_FROM_HOUR, FROM_HOUR), the last parameter in getInt() method is the default value so you should make another constant for it or supply it 0.
Related
So I have a child activity that I press two buttons, one to increment and one to decrement. I want to save this number when I go back to the previous activity. However, I am stuck here. I tried using shared preference, however that seems to work for main to secondary activity. I tried using Activity Result and that seems way above me right now. I want my value in the textView to stay until I press a button to reset the whole thing.
This is the parent activity.
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
class TallBoys : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tall_boys)
val btn1: Button = findViewById(R.id.button1)
val btn2: Button = findViewById(R.id.button2)
val btn3: Button = findViewById(R.id.button3)
val btn4: Button = findViewById(R.id.button4)
val btn5: Button = findViewById(R.id.button5)
val btn6: Button = findViewById(R.id.button6)
val btn7: Button = findViewById(R.id.button7)
btn1.setOnClickListener {
val intent = Intent(this, TallBoysNumbers::class.java)
}
btn2.setOnClickListener {
val intent = Intent(this, TallBoysNumbers::class.java)
startActivity(intent)
}
btn3.setOnClickListener {
val intent = Intent(this, TallBoysNumbers::class.java)
startActivity(intent)
}
btn4.setOnClickListener {
val intent = Intent(this, TallBoysNumbers::class.java)
startActivity(intent)
}
btn5.setOnClickListener {
val intent = Intent(this, TallBoysNumbers::class.java)
startActivity(intent)
}
btn6.setOnClickListener {
val intent = Intent(this, TallBoysNumbers::class.java)
startActivity(intent)
}
btn7.setOnClickListener {
val intent = Intent(this, TallBoysNumbers::class.java)
startActivity(intent)
}
}
}
This is the child activity
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class TallBoysNumbers : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tall_boys_numbers)
val confirmBtn: Button = findViewById(R.id.confirm_button)
val plusBtn: Button = findViewById(R.id.plus)
val textView = findViewById<TextView>(R.id.numbers)
val negBtn: Button = findViewById(R.id.negative)
var count = 0
plusBtn.setOnClickListener {
count++
textView.text = count.toString()
if (count >= 8) {
plusBtn.isEnabled = false
negBtn.isEnabled = true
}
}
negBtn.setOnClickListener {
count--
textView.text = count.toString()
if (count <= 0) {
negBtn.isEnabled = false
plusBtn.isEnabled = true
}
}
confirmBtn.setOnClickListener {
finish()
}
}
}
There are a lot of ways to do this, so I'm just going to show how to do it using SharedPreferences. The steps you need are:
In the parent activity, load the saved numbers from SharedPreferences in onResume and set the views accordingly. This needs to be in onResume so that it is also called when returning from the child activity after the numbers are modified.
Pass the key that is being edited to the child activity, so that it can save it in the correct spot in the SharedPreferences. The child activity can also use this key to load the current value so it is initialized correctly.
For example, in the parent activity
class ShowNumbersActivity : AppCompatActivity() {
private lateinit var prefs: SharedPreferences
private lateinit var txt1: TextView
private lateinit var txt2: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_show_numbers)
prefs = getSharedPreferences("MY_PREFS", Context.MODE_PRIVATE)
val btn1 = findViewById<Button>(R.id.btn1)
val btn2 = findViewById<Button>(R.id.btn2)
txt1 = findViewById(R.id.txt1)
txt2 = findViewById(R.id.txt2)
// When you click on "Button 1" you have to signal
// to the second activity which key you are editing
btn1.setOnClickListener {
val i = Intent(this, PickNumber::class.java)
i.putExtra("KEY","VAL1")
startActivity(i)
}
btn2.setOnClickListener {
val i = Intent(this, PickNumber::class.java)
i.putExtra("KEY","VAL2")
startActivity(i)
}
}
// Update the displayed state in onResume
// so that it is updated after returning from the
// child activity editing the number
override fun onResume() {
super.onResume()
val num1 = prefs.getInt("VAL1", 0)
val num2 = prefs.getInt("VAL2", 0)
txt1.text = num1.toString()
txt2.text = num2.toString()
}
}
Then in the child activity, get the "KEY" and the same SharedPreferences instance and edit it when you click "Submit"
class PickNumber : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pick_number)
val key = intent.getStringExtra("KEY")
if( key == null ) {
finish()
}
val prefs = getSharedPreferences("MY_PREFS", Context.MODE_PRIVATE)
val txt = findViewById<EditText>(R.id.num)
val submit = findViewById<Button>(R.id.submit)
// load the current value and display it
val current = prefs.getInt(key, 0)
txt.setText(current.toString())
submit.setOnClickListener {
// Get the updated number, save it to shared preferences
// (remember to call "apply") and finish this activity.
// When onResume is called in the parent activity it will
// load this updated number.
val num = txt.text.toString()
val numI = num.toIntOrNull() ?: 0
prefs.edit().putInt(key, numI).apply()
finish()
}
}
}
If you want to clear the numbers, you could make a button that calls prefs.edit().clear().apply() to erase all the saved values, or prefs.edit().remove(someKey).apply() to erase just one specific value.
There are definitely better ways to do this - if you switch to using a Fragment like you indicated these could be stored in a shared ViewModel that handles persistence and would be a cleaner design overall.
You are calling "finish" method. This will finish TallBoysNumbers activity without saving any data. Do not use SharedPreferences for that. What you'll need is "startActivityForResult".
This will allow you to save the data before calling "finish" AND getting it from the caller activity. Take a look here:
https://developer.android.com/training/basics/intents/result
with using shared pref I hope it will come to you
class dataShared() for saving count
class DataShared(
context: Context,
userSharedPrefName:String="USER_SHARED_PREF_NAME"
) {
private val sharedPreferences: SharedPreferences =
context.getSharedPreferences(
userSharedPrefName, Context.MODE_PRIVATE
)
fun getCount(): Int {
return (sharedPreferences.getInt("count", 0))
}
fun setCount(value: Int) {
val editor = sharedPreferences.edit()
editor.putInt("count", value)
editor.apply()
}
}
plusBtn and negBtn
val data=DataShared(this)
plusBtn.setOnClickListener {
if (data.getCount()<7) {
data.setCount(data.getCount() + 1)
textView.text = data.getCount().toString()
}
}
negBtn.setOnClickListener {
if (data.getCount()>=1){
data.setCount(data.getCount()-1)
textView.text=data.getCount().toString()
}
}
This is the function I made in one fragment to store my value,
private fun nickdata(){
val sharedPreferences = getSharedPreferences("sharedPrefs", Context.MODE_PRIVATE)
val nicdat = binding.nickname.text.toString()
val editor = sharedPreferences.edit()
editor.putString("Nickname_key","trial")
editor.apply()
editor.commit()}
below is the code I am using to fetch data in another fragment
val pref = activity!!.getPreferences(Context.MODE_PRIVATE)
val id = pref.getString("Nickname_Key", "trial")
binding.nickdata.text = id
In output I am getting "trial" which is def value.
Make sure you are using the same shared preference in both fragments.
Also what you did is you stored the value trial not the value that you want to send the the other fragment.
val sharedPreferences = activity!!.getSharedPreferences("sharedPrefs", Context.MODE_PRIVATE)
val nicdat = binding.nickname.text.toString()
val editor = sharedPreferences.edit()
editor.putString("Nickname_key",nicdat)
editor.apply()
editor.commit()
For the other fragment
val pref = activity!!.getSharedPreferences("sharedPrefs", Context.MODE_PRIVATE)
val id = pref.getString("Nickname_Key", "trial")
binding.nickdata.text = id
You can use Fragment Result Listeners. More documentation: Communicating between fragments. Here is some working code that controls navigation in my app:
In first fragment:
open fun navigate(idOfDestination: Int, bundle: Bundle?) {
val tempBundle = bundle ?: Bundle()
tempBundle.putInt(NAVIGATION_DESTINATION, idOfDestination)
activity?.supportFragmentManager?.setFragmentResult(NAVIGATION_RESULT, tempBundle)
}
In target fragment, register the listener:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.supportFragmentManager?.setFragmentResultListener(NAVIGATION_RESULT, this, FragmentResultListener { _, bundle ->
val destination = bundle.getInt(NAVIGATION_DESTINATION)
bundle.remove(NAVIGATION_DESTINATION)
navigationHandler(bundle,destination)
})
}
private fun navigationHandler(bundle: Bundle?, idOfDestination: Int) {
navController = Navigation.findNavController(binding.root)
if (!FeatureControlManager.isDestinationAllowedToGo(idOfDestination)) {
navController = childFragmentManager.fragments.first().findNavController()
}
navController.validateAndNavigate(navController, idOfDestination, childFragmentManager, bundle)
}
I got it done
private fun nickdata(){
val sharedPreferences = getSharedPreferences("sharedPrefs", Context.MODE_PRIVATE)
val nicdat = binding.nickname.text.toString()
val editor = sharedPreferences.edit()
editor.putString("trial",nicdat)
editor.apply()
editor.commit()
this code in one fragment
below mentioned code where I want to fetch it
val pref = activity!!.getSharedPreferences("sharedPrefs", Context.MODE_PRIVATE)
val nicdat = binding.nickdata.text.toString()
val id = pref.getString("trial", nicdat)
binding.nickdata.text = id
I want to move from one Activity which displays a RecyclerView to another Activity (detail). But when I added data transmission via Intent, the data always failed to be taken in the Activity detail.
This is the error:
My MainDetail:
private lateinit var viewModel: MainDetailModel
var idAnime: String = "34134"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_detail)
idAnime = intent.getStringExtra("idAnime")
println("idanime $idAnime")
setupFragment()
setupViewModel()
}
}
ViewModel:
class MainViewModel(context: Application, private val appRepository: AppRepository, private val contexts: Context) : AndroidViewModel(context), MainItemClickAction {
override fun onItemClicked(detailModel: DetailModel) {
var intent = Intent(contexts, MainDetailActivity::class.java)
intent.putExtra("idAnime",detailModel.mal_id )
contexts.startActivity(intent)
}
}
Check if your field "detailModel.mal_id" mal_id in that case is string, because you're requesting string in details activity. If it's string check also if this "mal_id" is null. Other issues from code you provided can't be seen.
Check your value idAnime, it exists or not, I think is better check all value is empty or not before putting into listView or another view.
I tried to follow this tutorial, but an error occured when I try to assign a value to my Sh.Preference (prefs.token = "sometoken"):
kotlin.UninitializedPropertyAccessException: lateinit property prefs has not been initialized
I don't understand where's the bug, I also checked this thread.
Here are my code snippets
Prefs.kt :
class Prefs(context: Context) {
private val PREFS_FILENAME = "com.example.myapp.prefs"
private val PREFS_TOKEN = "token"
private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_FILENAME, 0)
var token: String?
get() = prefs.getString(PREFS_TOKEN, "")
set(value) = prefs.edit().putString(PREFS_TOKEN, value).apply()
}
App.kt :
val prefs: Prefs by lazy {
App.prefs
}
class App : Application() {
companion object {
lateinit var prefs: Prefs
}
override fun onCreate() {
prefs = Prefs(applicationContext)
super.onCreate()
}
}
prefs.token has a default value of "", so why the logs said that has not been initialized?
Ok, problem found... The code was alright, I just missed to add this line
android:name=".App"
in the tag
<application in my Android Manifest.
In my case, i just initialize SharedPreference in onCreate(), and all works
For example: MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AppPreferences.init(this) // added this
}
SharedPreferences object:
object AppPreferences {
private const val NAME = "SpinKotlin"
private const val MODE = Context.MODE_PRIVATE
private lateinit var preferences: SharedPreferences
// list of app specific preferences
private val IS_FIRST_RUN_PREF = Pair("is_first_run", false)
fun init(context: Context) {
preferences = context.getSharedPreferences(NAME, MODE)
}
/**
* SharedPreferences extension function, so we won't need to call edit() and apply()
* ourselves on every SharedPreferences operation.
*/
private inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) {
val editor = edit()
operation(editor)
editor.apply()
}
// getter and setter with Shared Preference
var temperature: Float
// custom getter to get a preference of a desired type, with a predefined default value
get() = preferences.getFloat("temp",1f )
// custom setter to save a preference back to preferences file
set(value) = preferences.edit {
it.putFloat("temp", value)
}
}
So, basically I have a class:
class App : Application() {
lateinit var prefs: SharedPreferences
}
Now, I want to add a delegated property:
var isInitialized: Boolean by prefs.boolean()
The problem is that this, isInitialized property must be initialized lazily since I'm using Android Dagger2 framework, which performs injection after App creation (during calling onCreate() method):
class App : Application() {
lateinit var prefs: SharedPreferences
var isInitialized: Boolean = false
override fun onCreate() {
super.onCreate()
// how can I assign a delegate to isInitialized?
}
}
I would like it to be done either via:
lazy initialization during declaration (which is delegate in delegate - wondering whether this possible?)
lazy initialization during assignment
Is there any way of doing this?
Thanks!
You could do it with an indirection:
class DoubleDelegate<R, T>(var realDelegate: ReadWriteProperty<R, T> = /* some default */) : ReadWriteProperty<R, T> by realDelegate
then
val isInitializedDelegate = DoubleDelegate<App, Boolean>()
var isInitialized: Boolean by isInitializedDelegate
override fun onCreate() {
super.onCreate()
isInitializedDelegate.realDelegate = prefs.boolean()
}
Somehow I don't think this is actually a good idea.
Use Lazy
From the document Lazy Gets the lazily initialized value of the current Lazy instance. Once the value was initialized it must not change during the rest of lifetime of this Lazy instance.
Application class
val prefs: Prefs by lazy {
App.prefs!!
}
class App : Application() {
companion object {
var prefs: Prefs? = null
}
override fun onCreate() {
prefs = Prefs(applicationContext)
super.onCreate()
}
}
your data model class should be like this
class Prefs (context: Context) {
val PREFS_FILENAME = "com.teamtreehouse.colorsarefun.prefs"
val IsInitialized = "isInitialized"
val prefs: SharedPreferences = context.getSharedPreferences(PREFS_FILENAME, 0);
var initialized: Boolean
get() = prefs. getBoolean(IsInitialized, false)
set(value) = prefs.edit(). putBoolean(IsInitialized, value).apply()
}
then use Activity or fragment
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val initialized = prefs.initialized //getvalue
selectvalue(false)// set value
}
private fun selectvalue(value: Boolean) {
prefs.initialized = value
}
}
more details refer this example SharedPreferences Easy with Kotlin