How to use sharedpref.edit() only once - android

I have a code where I called sharedPref.edit() and sharedPref.apply() multiple times. How to make convert it to call only once.
if (success) {
val data = response.getJSONObject("data")
sharedPreferences.edit().putBoolean("isLoggedIn", true).apply()
sharedPreferences.edit()
.putString("user_id", data.getString("user_id")).apply()
sharedPreferences.edit().putString("name", data.getString("name"))
.apply()
sharedPreferences.edit().putString("email", data.getString("email"))
.apply()
sharedPreferences.edit()
.putString("mobile_number", data.getString("mobile_number"))
.apply()
sharedPreferences.edit()
.putString("address", data.getString("address")).apply()
StyleableToast.Builder(this)
.text("Welcome " + data.getString("name"))
.backgroundColor(Color.RED)
.textColor(Color.WHITE).show()
userSuccessfullyLoggedIn()
}
I want to use the method call only once.
This can be called once, the returned editor instance can be stored in
a variable and re-used.
How to do this ??

These little steps will organize your code.
You can put it like this:
val editor = sharedPreferences.edit()
Then use it :
editor.putBoolean("isLoggedIn", true)
And Add others values without ".apply()"
Then Put at the End:
editor.apply()

you can create your custom Shared Preferences
class CustomSharedPreferences {
companion object {
private val PREFERENCES_USER_NAME = "preferences_user_name"
private var sharedPreferences: SharedPreferences? = null
#Volatile private var instance: CustomSharedPreferences? = null
private val lock = Any()
operator fun invoke(context: Context) : CustomSharedPreferences = instance ?: synchronized(lock){
instance ?: makeCustomSharedPreferences(context).also {
instance = it
}
}
private fun makeCustomSharedPreferences(context: Context) : CustomSharedPreferences{
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
return CustomSharedPreferences()
}
}
fun saveUser(name: String, email: String){
sharedPreferences?.edit(commit = true){
putString(PREFERENCES_USER_NAME, name)
}
}
fun getUser() = sharedPreferences?.getString(PREFERENCES_USER_NAME, "")
}
You can save all information to SP in saveUser().

Related

SharedPreferences not working when Activity is started from somewhere else?

I have a ThreadActivity with two functions, saveContacts and loadContacts. They both use sharedpreferences and Gson to save an ArrayList consisting of Objects called SimpleContacts. Somehow it cannot retrieve data from sharedpreferences once I start the Activity from somewhere else. (I tried loading instantly after saving and that works, but not if I close the Activity and re-open it)
The save function:
private fun saveContact() {
val gson = Gson()
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
try {
val editor = sharedPreferences.edit()
val json = gson.toJson(participants)
editor.putString(threadId.toString()+"_Contact", json)
editor.apply()
} catch(e: Exception) {
e.printStackTrace()
}
}
The load function:
private fun loadContact() {
val gson = Gson()
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
val type = object : TypeToken<ArrayList<SimpleContact?>?>() {}.type
try {
val json = sharedPreferences.getString(threadId.toString()+"_Contact", "")
participants = gson.fromJson(json, type)
} catch(e: Exception) {
e.printStackTrace()
}
}
I have 2 Activities that can open this ThreadActivity, if I start it from the same one, it all works perfectly fine. But when I use the other Activity to start it, the sharedPrefs are empty.
Launch Activity that works (I don't know if its because its the way the Intent is build so I will write them both here):
private fun launchThreadActivity(phoneNumber: String, name: String) {
hideKeyboard()
val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: ""
val numbers = phoneNumber.split(";").toSet()
val number = if (numbers.size == 1) phoneNumber else Gson().toJson(numbers)
Intent(this, ThreadActivity::class.java).apply {
putExtra(THREAD_ID, getThreadId(numbers))
putExtra(THREAD_TITLE, name)
putExtra(THREAD_TEXT, text)
putExtra(THREAD_NUMBER, number)
if (intent.action == Intent.ACTION_SEND && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) {
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
putExtra(THREAD_ATTACHMENT_URI, uri?.toString())
} else if (intent.action == Intent.ACTION_SEND_MULTIPLE && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) {
val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
putExtra(THREAD_ATTACHMENT_URIS, uris)
}
startActivity(this)
}
}
Start Activity that does not work:
Intent(this, ThreadActivity::class.java).apply {
putExtra(THREAD_ID, (it as Conversation).threadId)
putExtra(THREAD_TITLE, it.title)
putExtra("fromMain", true)
startActivity(this)
}
Nevermind, it was my mistake.
When saveContact was called the threadId was not initialized yet. So basically the keys were always different.

Optimizing Android shared preference value access

I am using Androids shared preferences as a simple Storage like this:
class Storage(context: Context) {
private val storage = context.getSharedPreferences("my_storage", Context.MODE_PRIVATE)
private val myKey = "my_string_key"
fun getMyString(): String {
return storage.getString(myKey, "default String") ?: "default String"
}
fun setMyString(str: String) {
storage.edit().apply {
putString(myKey, str)
apply()
}
}
}
However, I don't like how this introduces boilerplate code each time I add another stored value.
This is my current workaround:
class Storage(context: Context) {
private val storage = context.getSharedPreferences("my_storage", Context.MODE_PRIVATE)
private inline fun put(block: (SharedPreferences.Editor) -> Unit) {
storage().edit().apply {
block(this)
apply()
}
}
var myString: String = "default String"
set(str) = put { it.putString(::myString.name, str) }
get() = storage.getString(::myString.name, field) ?: field
}
Now my Questions are:
Is this even a good idea? (Im new to programming on the Android platform and I haven't seen anyone do this, so there might be a good reason to stick to the function approach)
Can this be optimised further? Ideally I would like to only declare the variable and have the getter and setter generated somehow.
Thank you in advance.
I would choose different approach. You just need keys for this. For example something like this:
class Storage(context: Context) {
private val storage = context.getSharedPreferences("my_storage", Context.MODE_PRIVATE)
public fun getKey(key: String): String = storage.getString(key, "defaultString") ?: "defaultString"
public fun set(key: String, value: String) = storage.edit().putString(key, value)
object Keys {
const val key1 = "Key1"
const val key2 = "Key2"
}
}
Or, if you want more safety for keys to be used as constants, not strings
enum class Keys {
key1, key2
}
class Storage(context: Context) {
private fun getDefaultValue(key:Keys): String = when(key) {
Keys.key1 -> "string"
Keys.key2 -> "String2"
}
private val storage = context.getSharedPreferences("my_storage", Context.MODE_PRIVATE)
public fun getKey(key: Keys): String =
storage.getString(key.name, null) ?: getDefaultValue(key)
public fun set(key: Keys, value: String) = storage.edit().putString(key.name, value)
}

setting language before setContentView results in unending loop

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)

SharedPreferences is saving only the last value

i try to use SharedPreferences but it's only saving the last value.
MainActivity:
myPreferences.setPrice(txtPrice.text.toString().toFloat())
myPreferences.setSABV(txtABV.text.toString().toFloat())
SharedPreferences handler:
class myPreferences(context: Context){
val PREFERENCENAME = "BeerNote"
val PRICE = 0.0f
val ALCOHOLBYVOLUME = 0.0f
val preference = context.getSharedPreferences(PREFERENCENAME,Context.MODE_PRIVATE)
fun setPrice(price:Float){
preference.edit().putFloat(PRICE.toString(),price).apply()
}
fun getPrice():Float{
return preference.getFloat(PRICE.toString(),0.0f)
}
fun setSABV(abv:Float){
preference.edit().putFloat(ALCOHOLBYVOLUME.toString(),abv).apply()
}
fun getABV():Float{
return preference.getFloat(ALCOHOLBYVOLUME.toString(),0.0f )
}
}
When i try to recover the data:
Toast.makeText(this, "Price:"+mypreference.getPrice(), Toast.LENGTH_LONG).show()
Toast.makeText(this, "ABV:"+mypreference.getABV(), Toast.LENGTH_LONG).show()
It only saves the ABV value in Price and ABV.
You probably want to use constants as keys instead of floats converted to strings as you do now. This would look like:
class myPreferences(context: Context){
val PREFERENCENAME = "BeerNote"
val PRICE = 0.0f
val ALCOHOLBYVOLUME = 0.0f
val priceKey = "price"
val SABVKey = "sabv"
val preference = context.getSharedPreferences(PREFERENCENAME,Context.MODE_PRIVATE)
fun setPrice(price:Float){
preference.edit().putFloat(priceKey,price).apply()
}
fun getPrice():Float{
return preference.getFloat(priceKey,0.0f)
}
fun setSABV(abv:Float){
preference.edit().putFloat(SABVKey,abv).apply()
}
fun getABV():Float{
return preference.getFloat(SABVKey,0.0f )
}
}

App crashes on reading shared preferences after obfuscation

In my application, I am using SharedPreferences to store some user preferences. The application was not obfuscated (-dontobfuscate in the proguard file).
Now in the next version of the application, I want to enable obfuscation. When I try this, the application returns NullPointerException while reading the SharedPreferences data from the previous version of the application. The error log is not helpful because the code is already obfuscated and it does not provide meaningful information. However, while trying in the debug mode I found the crash may be due to null context which is a static variable in the code! That should not be the case because the application works file if SharedPreferences were not there already.
Is there any way the app can still read the SharedPreferences data from unobfuscated version?
Writing / reading the SharedPreferences is pretty standard:
Writing:
SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor prefsEditor = mPrefs.edit();
prefsEditor.putString("userUnitsOption", "C");
//apply the storage
prefsEditor.apply();
Reading:
final SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
return mPrefs.getString("userUnitsOption", "A");
If you want to use shared preferences in Android then use it as given below :
First, you have to store your preferences like this :
SharedPreferences.Editor editor = getSharedPreferences("mySharedPref", MODE_PRIVATE).edit();
editor.putString("myName", "abc");
editor.apply();
Now, To read or get those stored shared preferences write code as given below :
SharedPreferences prefs = MainActivity.this.getSharedPreferences("mySharedPref", MODE_PRIVATE); // Here MainActivity.this represent the context. So you can use your context in place of MainActivity.this
String strName = prefs.getString("myName","defaultName");
In Kotlin,
Usage:
private val sharedPref = defaultPrefs(this)
To Save Data--> sharedPref[KEY] = *String data to save*
To Get Data --> val userDetails = sharedPref[KEY, ""]
Create a shared preference helper class like below.
object PreferenceHelper {
fun defaultPrefs(context: Context): SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context)
fun customPrefs(context: Context, name: String): SharedPreferences =
context.getSharedPreferences(name, Context.MODE_PRIVATE)
inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) {
val editor = this.edit()
operation(editor)
editor.apply()
}
/**
* puts a key value pair in shared prefs if doesn't exists, otherwise updates value on given [key]
*/
operator fun SharedPreferences.set(key: String, value: Any?) {
when (value) {
is String? -> edit { it.putString(key, value) }
is Int -> edit { it.putInt(key, value) }
is Boolean -> edit { it.putBoolean(key, value) }
is Float -> edit { it.putFloat(key, value) }
is Long -> edit { it.putLong(key, value) }
else -> throw UnsupportedOperationException("Not yet implemented")
}
}
/**
* finds value on given key.
* [T] is the type of value
* #param defaultValue optional default value - will take null for strings, false for bool and -1 for numeric values if [defaultValue] is not specified
*/
inline operator fun <reified T : Any> SharedPreferences.get(
key: String,
defaultValue: T? = null
): T? {
return when (T::class) {
String::class -> getString(key, defaultValue as? String) as T?
Int::class -> getInt(key, defaultValue as? Int ?: -1) as T?
Boolean::class -> getBoolean(key, defaultValue as? Boolean ?: false) as T?
Float::class -> getFloat(key, defaultValue as? Float ?: -1f) as T?
Long::class -> getLong(key, defaultValue as? Long ?: -1) as T?
else -> throw UnsupportedOperationException("Not yet implemented")
}
}
}

Categories

Resources