How to use PreferenceDataStore in async mode? - android

I would like to store user preferences in Firebase real time database instead of typical sharedPreferences.
As an example, my user should specify the itemId in order to access to this part of the database:
items
itemId
members
uid: true
So when the user fill in the itemId, I need to check if this itemId exists in the database and display error asking the user to fill in another itemId.
I envisage to use standard Preference screens and associate a custom PreferenceDataStore at least to handle the itemId.
When reading the documentation, I need to override getString/putString methods:
public class DataStore extends PreferenceDataStore {
#Override
public void putString(String key, #Nullable String value) {
// Save the value somewhere
}
#Override
#Nullable
public String getString(String key, #Nullable String defValue) {
// Retrieve the value
}
}
At the end, I need to manage it asynchronously in order to query my database and get some results.
So I'm wondering if using PreferenceDataStore is the good approach ... My initial idea was to avoid developping settings screens that already exist in Androidx Preference lib.
Thx for your help!

Related

Save linked objects Primary key value already exists Relationships

I have just started to use RealmDB and cannot figure out how to save linked object correctly, to implement a sort of foreign key
Here is my main User model.
public class UserModel extends RealmObject {
#PrimaryKey
public Long id;
public String firstName;
public String lastName;
public UserSettings userSettingsModel;
}
UserSettings Model is defined as follows.
public class UserSettingsModel extends RealmObject {
#PrimaryKey
private Long id;
public String email;
public RealmList<Car> cars;
}
And Car is a model itself.
public class Car extends RealmObject {
#PrimaryKey
private Long id;
public String model;
}
The problem is that when I am trying to save UserModel it tries to recreate all objects assigned to it. So before I saving user model I have already creates some Car objects.
I don't need to create them, but to reference like the foreign key in SQL databases. And when I am retrieving a user from the database it should automatically load all related data by primary keys.
Is it possible to achieve such behavior using Realm ?
Thanks for help. Solved this problem by using the copyToRealmOrUpdate method instead of copyToRealm.
You should create a managed object using realm.createObject(clazz, pkValue); if it doesn't exist yet, then set it as value or add it to the RealmList that you get another managed object.
You can also create managed objects from unmanaged objects with copyToRealmOrUpdate() (if the object has a primary key).
And when I am retrieving a user from the database it should automatically load all related data by primary keys.
The RealmList allows access to the related objects, and in fact, is also queryable by calling .where() on it. However, this is not based on primary keys. That feature is tracked under "computed fields".

How to store groups of booleans in Realm for Android?

Let's say I have a dozen booleans to store for each entity and expect the database to store about up to, at most, a few thousand records on any given Android device. As a fictional example, perhaps a Restaurant object has booleans like wifi, valetParking, sitDown, tippingEncouraged, listedOnYelp, etc. Let's also say I want to be able to easily search by them, so it's simple for a user to find restaurants that offer wifi and do not offer valet parking, for example.
How should these be stored on Realm?
I know this question in general for databases is something people like to argue over--whether to simply use a separate int field for each boolean, whether to have a separate table to implement a many-to-many relationship between the booleans and records, or whether to use each boolean as bitshifted flags and store the combination of flags as a single value?
For Realm specifically, my hunch is that it's easiest/best to just store each boolean separately, as it supports booleans and it seems the other approaches would be more complex than they would otherwise. But I'd love for someone more knowledgeable to say the correct, best supported approach in Realm.
Option 1: One field per boolean
public class Restaurant extends RealmObject {
private boolean wifi;
private boolean listedOnYelp;
/* getter, setter, etc.*/
}
Easy to implement
Easy to understand
RealmQueries based on the booleans are possible
Option 2: Flag field
64 booleans per long, the masks go from 21, 22, 23 to 264
public class Restaurant extends RealmObject {
private long booleanFlags;
private boolean getFlag(long mask) {
return (booleanFlags & mask) == mask;
}
private void setFlag(long mask, boolean value) {
if (value) {
booleanFlags |= mask;
} else {
booleanFlags &= ~mask;
}
}
public boolean hasWifi() {
return getFlag(1);
}
public void setWifi(boolean hasWifi) {
setFlag(1, hasWifi);
}
public boolean isListedOnYelp() {
return getFlag(2);
}
public boolean isTippingEncouraged() {
return getFlag(4);
}
}
Super memory efficient (unless Realm does not do the same in its core)
Not so easy to read or understand
No RealmQueries based on the booleans
Option 3: Extra class
public class Restaurant extends RealmObject {
/*...*/
}
public class RestaurantPropertyStore extends RealmObject {
private RealmList<Restaurant> restaurantsWithWifi;
private RealmList<Restaurant> restaurantsListedOnYelp;
}
Not object-oriented
Queries on one boolean at a time are possible
Option 1 is the object-oriented, Java-like and Realm-like way
Option 2 is the C, C++ way
Option 3 is the SQL, RDBMS way

Saving data upon closing app and retrieving that data

I know, there are plenty of questions in regards to saving/retrieving data on here. I was doing find looking things up on my own and really thought I could manage to find my answers without having to "ask a question", but I began to wonder something that I haven't seen an answer for on here.
MY SITUATION:
Naturally, I'm making an app. Upon closing the app, I want to save a simple array of numbers (0 or 1) or boolean values as it were. Upon starting the app, I want to search for that array, if it exists, and retrieve it for use within the app.
I began placing my code into the activity in which the array would be used. But, I started wondering if I would have to copy/paste the overridden onStop() function into all of my activities? Or do I do it in the main activity and somehow link the other activities.
Basically, no matter what state/activity the app is currently on when the app is closed, I want to be able to save the array of int/bool and open it back up when the app is started.
Maybe I didn't know how to search for what I wanted, so explaining it felt like the right thing to do.
I don't mind doing more searching, but if someone would point me in the right direction at the very least, I'd be extremely grateful.
EDIT: If there's a better way to do what I want than what I described (i.e. using a different state instead of onStop(), for instance), please feel free to throw out ideas. This is my first time actually having to deal with the activities' lifecycles and I'm a bit confused even after looking through the android development tutorials. I really think they're poorly done in most cases.
When you application needs to save some persistent data you should always do it in onPause() method and rather than onStop(). Because if android OS kills your process then onStop() and onDestroy() methods are never called. Similarly retrieve data in onResume() method.
Looking at the purpose you want to fulfill, SharedPreferences is all you want.
The documentation states:
"SharePreferences provides a general framework that allows you to save
and retrieve persistent key-value pairs of primitive data types. You
can use SharedPreferences to save any primitive data: booleans,
floats, ints, longs, and strings. This data will persist across user
sessions (even if your application is killed)."
Use SharedPreference to store small amount of data or use SQLite to store large amount of data.
See this link
http://developer.android.com/guide/topics/data/data-storage.html
Serialize an object and pass it around which is more dependable than shared preferences (had lots of trouble with consistency with shared preferences):
public class SharedVariables {
public static <S extends Serializable> void writeObject(
final Context context, String key, S serializableObject) {
ObjectOutputStream objectOut = null;
try {
FileOutputStream fileOut = context.getApplicationContext().openFileOutput(key, Activity.MODE_PRIVATE);
objectOut = new ObjectOutputStream(fileOut);
objectOut.writeObject(serializableObject);
fileOut.getFD().sync();
} catch (IOException e) {
Log.e("SharedVariable", e.getMessage(), e);
} finally {
if (objectOut != null) {
try {
objectOut.close();
} catch (IOException e) {
Log.e("SharedVariable", e.getMessage(), e);
}
}
}
}
Then use a class to use:
public class Timestamps implements Serializable {
private float timestampServer;
public float getTimestampServer() {
return timestampServer;
}
public void setTimestampServer(float timestampServer) {
this.timestampServer = timestampServer;
}
}
Then wherever you want to write to the variable use:
SharedVariables.writeObject(getApplicationContext(), "Timestamps", timestampsData);
Best way to achieve that is:
create a class. Call it MySettings, or whatever suits you
in this class, define the array of ints / booleans you are going to share, as static. Create getter & setter method (property) to access that (also as static methods)
add a static load() method to MySettings that reads from SharedPreferences. When you launch the app (in your first activity or better in a subclass of Application) call MySettings.load(). This load method sets the array
add a static save() method. Public also. Now you can save from anywhere in you app. This save() method reads the array and writes in SharedPreferences
Code sample:
public class MySettings {
private static List<Integer> data;
public static void load() {
data = new ArrayList<Integer>();
// use SharedPreferences to retrieve all your data
}
public static void save() {
// save all contents from data
}
public static List<Integer> getData() {
return data;
}
public static void setData(List<Integer> data) {
MySettings.data = data;
}
}

Creating object vs setting individual values for passing values between activities in android

I have to pass some data related to user between to activities, data is
name, user id, user name, email
now to do this I have to put these values in Intent and pass to other activity.
Now there a two ways
1)
Create a DTO for User say UserDto and make it Serializable and pass it using intent
intent.putExtra("user",userDto);
2)
put all four different values in intent as
intent.putExtra("name",name);
intent.putExtra("userid",id);
intent.putExtra("username",username);
intent.putExtra("email",email);
which one of above is most optimized way,
Please recommend some other way of maintaining user throughout the application scope.
You can have one Application class in your project like:
public class MyApplication extends Application {
private User user;
#Override
public void onCreate() {
super.onCreate();
}
public void setUser(User user){
this.user=user;
}
public User getUser(){
return user;
}
}
Or you can have some static class or some interface to hold application state (I.E. your user object)
In app manifest
<application android:name="<-pkgname->.MyApplication" ......>
and at any activity in your application you can get it as
MyApplication myApp=(MyApplication)getApplicationContext();
User user=myApp.getUser();
You can store the data as a static variable. So the data will be maintain throughout the application scope.
The best practice is to store data in the Shared Preference.

Validating a phone number in Android PreferenceScreen

I have a PreferenceScreen in which the user is capable, if system can't autodetect it, to enter the device's phone number. I'm still learning this part of Android but I managed to understand a bit of PreferenceScreens by examples provided by Android SDK itself and a few tutorials.
What I want is that the user can save the phone number only if null or valid, where by "valid" I mean running a generic validation logic (ie. an anonymous method that returns true or false, that can be reused in any possible situation*) or better, just to simplify things, ^(\+39)?3[0-9]{9}$
For now I have the following XML snip
<EditTextPreference
android:inputType="phone"
android:key="#string/preference_phoneNo"
android:selectAllOnFocus="true"
android:singleLine="true"
android:summary="#string/pref_phoneNumber_description"
android:title="#string/pref_phoneNumber" />
and following code by courtesy of Eclipse New Activity wizard:
private void setupSimplePreferencesScreen() {
if (!isSimplePreferences(this)) {
return;
}
addPreferencesFromResource(R.xml.pref_general);
bindPreferenceSummaryToValue(findPreference(getString(R.string.preference_phoneNo)));
}
addPreferenceFromResource is supposed to load the XML node and add the preference to the screen, while binPreferenceSummaryToValue is supposed to make description text change when preference is updated. Just for sake of completeness for those who don't like code courtesy of the IDE, the second method is provided by Eclipse who also provides a private class in the code file that is
/**
* A preference value change listener that updates the preference's summary
* to reflect its new value.
*/
In the general case, what should I do to perform validation logic before the preference gets saved when I click OK on the preference editor? Where is the validation logic to be put in a PreferenceScreen?
*Aren't we all here to learn?
Android has a built in helper method for this.
String phoneNumber = ...;
boolean valid = PhoneNumberUtils.isGlobalPhoneNumber(phoneNumber);
For a generic, re-usable method, here's the implementation of that method in PhoneNumberUtils, courtesy of AOSP (Apache licensed)
private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN =
Pattern.compile("[\\+]?[0-9.-]+");
...
public static boolean isGlobalPhoneNumber(String phoneNumber) {
if (TextUtils.isEmpty(phoneNumber)) {
return false;
}
Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
return match.matches();
}
Validation should occur within a Preference.OnPreferenceChangeListener , in the onPreferenceChange method. Simply return false if you don't want the value to be saved.
Example snippet:
private static Preference.OnPreferenceChangeListener myListener =
new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object value) {
String stringValue = value.toString();
if (preference instanceof PhoneNumberPreference) {
return isGlobalPhoneNumber(value);
}
}
...
As a note, since you're starting off with the Settings activity generated by the Eclipse wizard, this listener has already been built for you. You just need to edit it to include validation of the phone number (assuming that's what's being edited), and to return false if the number is invalid, so it won't be saved to preferences.

Categories

Resources