Cannot read country code selected in a spinner Android - android

I am working on an app in which users have to select a country code, i was successful in creating a spinner for the said purpose as shown in this link:
Creating a spinner for choosing country code
But i am getting problem in reading the value selected in the spinner.
{
String abc = onCountryPickerClick();//abc is always null
}
public String onCountryPickerClick (){
ccp.setOnCountryChangeListener(new CountryCodePicker.OnCountryChangeListener() {
#Override
public void onCountrySelected() {
selected_country_code = ccp.getSelectedCountryCodeWithPlus();
}
});
return selected_country_code;
}

When String abc = onCountryPickerClick(); is being invoked, the selected_country_code value will be assigned to abc.
When your CountryCodePicker.OnCountryChangeListener's onCountrySelected() method is being invoked, the ccp.getSelectedCountryCodeWithPlus();'s value gets assigned to selected_country_code. Since String is immutable, changing selected_country_code's value won't change the value of abc, nor the return selected_country_code; will be invoked.
One of possible solutions would be to change your CountryCodePicker.OnCountryChangeListener anonymous implementation to assign the selected country value to abc e.g.
#Override
public void onCountrySelected() {
selected_country_code = ccp.getSelectedCountryCodeWithPlus();
abc = selected_country_code
}

Callbacks are not synchronous. Unfortunately, you cannot simply do String abc = onCountryPickerClick(); because what you are returning is something that is not yet set. Let's go through your code:
ccp.setOnCountryChangeListener(
new CountryCodePicker.OnCountryChangeListener() {
#Override
public void onCountrySelected() {
selected_country_code = ccp.getSelectedCountryCodeWithPlus();
}
});
The code seems to say that when the country is selected in the spinner, you assign the value of selected_country_code. Assuming this is an action triggered by the user, when you call String abc = onCountryPickerClick();, how can you be sure the user has selected anything? This is the issue. You cannot be sure that the user has already selected the option and returning the value is not enough.
You can solve this in many ways. You can for example keep propagating the callback:
public void onCountryPickerClick(OnCountryChangeListener listener){
ccp.setOnCountryChangeListener(listener);
}
// Anywhere you call this
onCountryPickerClick(new CountryCodePicker.OnCountryChangeListener() {
#Override
public void onCountrySelected() {
// Here do whatever you want with the selected country
}
});
The above approach is not very different than what you have now. There are other options. You could use java observables i.e.:
class CountryCodeObservable extends Observable {
private String value;
public CountryCodeObservable(String value) {
this.value = value;
}
public void setCountryCode(String countryCode) {
value = countryCode;
setChanged();
notifyObservers(value);
}
}
public CountryCodeObservable onCountryPickerClick(){
CountryCodeObservable retValue = new CountryCodeObservable("");
ccp.setOnCountryChangeListener(
new CountryCodePicker.OnCountryChangeListener() {
#Override
public void onCountrySelected() {
retValue.setCountryCode(ccp.getSelectedCountryCodeWithPlus());
}
});
return retValue;
}
// Then when calling this method you can do something like:
CountryCodeObservable observable = onCountryPickerClick();
observable.addObserver((obj, arg) -> {
// arg is the value that changed. You'll probably need to cast it to
// a string
});
The above example lets you add more than one observable. It might be too much for your use case, I just thought it illustrates another approach and also the asynchronicity of this situation.
Again, there are even more ways to solve this, the key is that you can't simply return a string and hope it changes when the user selects anything.

Related

How to make a dynamic filter? Android SDK Java

Help to realize the idea with the application. The application receives JSON from the site, and displays information on the screen (for example, games (image, name of the game, creators, year of release and description)). This is all parted, got the data through the retrofit and output them through recycleview. There is no problem with this, but I can’t think of a filter implementation. The filter must be dynamic, for example the creators and year of release. Activation opens where the CREATORS list goes down and the checkboxes with the studios name go down, and the YEAR of the ISSUE and the checkboxes with the year of release also go after it (only the creators and the year must take information from the data they received from the server via Jason). The idea is to have the first standard check box of the type all, which allows you to immediately output everything that is right at the start of the application, and then click the filter button and choose exactly what interests you. And there should be a button that all this update and returns with specific parameters. And I saw there is a button like a cross on the upper right (above on the actionbar), which possibly cancels everything and sets it back to its original position (all checkboxes only). I really hope for your advice and tips on how to implement this application. Thanks to all
Here is a good example of a filter (I need this one) https://pp.userapi.com/c851332/v851332451/e7308/hhiO3IOHPsg.jpg
in fact, a separate activity, which is dynamically filled with checkboxes on the results obtained from JSON (tobish the name, year, etc.)
POJO class value:
public class Value {
#SerializedName("title")
#Expose
private String title;
#SerializedName("year")
#Expose
private String year;
#SerializedName("genre")
#Expose
private List<String> genre;
#SerializedName("director")
#Expose
private String director;
#SerializedName("desription")
#Expose
private String desription;
#SerializedName("image")
#Expose
private String image;
public String getTitle() {
return title;
}
public String getYear() {
return year;
}
public List<String> getGenre() {
return genre;
}
public String getDirector() {
return director;
}
public String getDesription() {
return desription;
}
public String getImage() {
return image;
}
}
POJO class for list value:
public class Example {
#SerializedName("values")
#Expose
private List<Value> values = null;
public List<Value> getValues() {
return values;
}
}
MainActivity:
public class MainActivity extends Activity {
private static final String TAG = "MoviesApp";
RecyclerView recyclerView;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.filters:
Intent intent = new Intent(this, FiltersActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.rc_movies);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setHasFixedSize(true);
JSONPlaceHolderApi service = NetworkService.getJSONApi();
Call <Example> call = service.getValues();
call.enqueue(new Callback<Example>() {
#Override
public void onResponse(Call<Example> call, Response<Example> response) {
Example data = response.body();
if(response.isSuccessful()) {
if(data != null) {
MoviesAdapter moviesAdapter = new MoviesAdapter(MainActivity.this, data);
recyclerView.setAdapter(moviesAdapter);
Log.i(TAG, "Response call");
}else{
Log.i(TAG, "Data is null");
}
}else{
Log.i(TAG, "Response does not successful");
}
}
#Override
public void onFailure(Call<Example> call, Throwable t) {
Log.i(TAG, "Failure call");
}
});
}
}
And i cant make FiltersActivity so that it works according to the condition
Sorry for bad english :C
...
Hi Daniil,
using a second activity for filter is not a bad idea in my opinion.
The problem is to update the list every time you apply a new filter because you have to store your filters values somewhere and then update the list after apply.
I suggest you to try using a shared class between activities (maybe a ViewModel) and if you wanna allows the generation of filters you can build the interface of the second activity in a dynamic way during your network call using the values you have received.
All filters value can be stored inside the viewmodel into an appropriate object during the network call (using a background operation to not freeze the UI).
When you call the second activity the shared class will be interrogated to obtain the filters and in the onCreate method you can generate the corresponding Views programmatically from the data you have received after the interrogation.
Also,to make sure the "apply filter" event will be intercepted by the first activity you can use an Event Bus Library, a LiveData Object or an Observer Pattern (Listener) according to the ones that fit your architecture best. When the first activity receives the event you can force and offline filter operation in your data and update the RecyclerView using a notifyDataSetChange().
Another architectural approach is to use a single activity and two fragments, but it require more code despite it's flexibility.This approach fits best with Viewmodels and Livedata and allows you to manage only one activity lifecycle (no worries about the kill of the non focused activities by the system).
Hope this will give you some hints/help.
Cheers

manage null boolean with RxJava2 PublishSubject

I'm implementing the MVP design pattern. My presenter receives the new values from the view. I want to manage the state of a next button by automatically check if everything is valid when values are updated on the view.
In my form I have an optional part which is displayed only if the user select the correct option.
In this optional part I have a binary question. If the part is not displayed I need to set the value of the question to null on the Presenter side.
For example, the user select the option and the optional part is displayed. The user select the answer. Then the user change the option and the optional part is hidden. In that case I need to set the answer to the optional question to null, for the answer to not be already selected if the user display the optional part again.
To do so, I call a method on the Presenter with a null value instead of true/false.
Here is the code:
private final PublishSubject<Boolean> mObsOptionalAnswer = PublishSubject.create();
public MyPresenter(){
// Combine all the values together to enable/disable the next button
Observable.combineLatest(
// ... other fields
// I need this to return false if the optional part is
// displayed but nothing is selected
mObsOptionalAnswer.map(this::isValid),
(...) -> ...
).subscrible(enable ->{
mView.enableBtn(enable);
});
}
public void myFunction(Boolean isSomething){
// ... some code
mObsOptionalAnswer.onNext(isSomething);
}
private boolean isValid(Boolean value){
return value != null;
}
The problem is, since RxJava 2, null values are not allowed in the onNext() method.
So, how am I supposed to manage that?
If you want to be able to send a null value, you can use a wrapper. In this configuration, you send the wrapper, which isn't null even if the value itself is.
public class BooleanWrapper {
public final Boolean value;
public BooleanWrapper(Boolean value) {
this.value = value;
}
}
Your PublishSubject<Boolean> becomes a PublishSubject<BooleanWrapper> and you just have to create the wrapper and de-reference your Boolean when needed :
mObsOptionalAnswer.onNext(new BooleanWrapper(isSomething));
and
mObsOptionalAnswer.map(wrapper -> this.isValid(wrapper.value))
If you need to do that more than once in your code, you can create a generic wrapper (as described by this tutorial) :
public class Optional<M> {
private final M optional;
public Optional(#Nullable M optional) {
this.optional = optional;
}
public boolean isEmpty() {
return this.optional == null;
}
public M get() {
return optional;
}
}
you could use a constante Boolean object
public static final Boolean RESET_VALUE = new Boolean(false);
and you can emit this instead of emitting null. The receiver would have to check against this instance and behaving accordingly. Eg.
.subscrible(enable ->{
if (enable != RESET_VALUE) {
mView.enableBtn(enable);
}
});

return an object Android

I want to return an object with some things in them.
Here is the declaration;
Object user_det = get_user_det();
Here is the function code:
private Object get_user_det() {
Firebase f_user = new Firebase("https://myapp.firebaseio.com/User/");
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot snap_user) {
// TODO Auto-generated method stub
Iterable<DataSnapshot> rs = snap_user.getChildren();
Iterator<DataSnapshot> irs = rs.iterator();
long allNum2 = snap_user.getChildrenCount();
int maxNum2 = (int)allNum2;
int count_user = 1;
while(irs.hasNext())
{
if(count_user <= maxNum2)
{
Firebase user_data = new Firebase("https://myapp.firebaseio.com/");
AuthData authData = user_data.getAuth();
Map<String, Object> nPost = (Map<String, Object>) irs.next().getValue();
String db_email = nPost.get("email_addr").toString();
if (authData != null) {
String usr_email = authData.getProviderData().get("email").toString();
if(usr_email.equals(db_email))
{
//NB: I WANT TO ADD THE FOLLOWING INTO THE OBJECT
String disp_name = nPost.get("disp_name").toString();
String real_name = nPost.get("real_name").toString();
}
} else {
System.out.println("Failed");
}
}
count_user++;
}
}
#Override
public void onCancelled(FirebaseError arg0) {
// TODO Auto-generated method stub
}
});
return null; //NB: I NEED TO RETURN THE OBJECT HERE.
}
I want to return the string disp_name and real_name but they are inside the addListenerForSingleValueEvent, so how do I get them out and return it to the function.
I have wrote "NB" in the code where I need help with.
Thanks for your time.
If you want to return an object from your method in java, do it like this:
The Object class:
This contains the structure of your Object, and defines what data will be in it. Also includes methods to easily get the data.
private class myObject {
private String name;
private String realName;
//The constructor, so you can set the data when creating the Object.
public myObject (String disp_name, String real_name) {
name = disp_name;
realName = real_name;
}
//Getter methods, to get the data.
public String getRealName() {return realName;}
public String getDisplayName() {return name;}
}
Your code:
private Object get_user_det() {
myObject o; //Declare it, so it can be returned.
...
String disp_name = nPost.get("disp_name").toString();
String real_name = nPost.get("real_name").toString();
o = new myObject(disp_name, real_name); //create it and set the data.
...
return myobject; //return the new Object with the data.
}
To get the data from the Object:
myObject o = get_user_det(); //Call the metod which return our Object.
String realName = o.getRealName(); //Get the data from the Object.
String displayName = o.getDisplayName;
In your case, it would be much easier to use a String array.
Hope this helps.
It's probably easiest to see what's going on, if you add some printlns to your code:
private Object get_user_det() {
Firebase f_user = new Firebase("https://myapp.firebaseio.com/User/");
System.out.println("Adding listener");
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot snap_user) {
System.out.println("Data received");
}
#Override
public void onCancelled(FirebaseError arg0) {
// TODO Auto-generated method stub
}
});
System.out.println("Returning");
return null; //NB: I NEED TO RETURN THE OBJECT HERE.
}
If you execute this code, you will see that it logs:
Adding listener
Returning
Data received
Most likely, this is not what you expected. But hopefully, it makes sense if you read my explanation below.
Asynchronous loading
When you register your listener:
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
You tell Firebase to start listening for events. It goes off and starts retrieving the data from the server.
Since retrieving the data may take some time, it does this retrieval asynchronously so that your thread isn't blocked. Once the data is completely retrieved, Firebase calls the onDataChange method in your listener.
Between the time you start listening and the time onDataChange is called, your code continues executing. So there is no way to return data that is loaded asynchronously, because by the time your function returns, the data isn't loaded yet.
Solutions
Disclaimer: I am not an expert at solving this problem in Java, so there may be problems with my solutions. If^H^HWhen you find any, please report them in the comments.
I know of three possible solutions to the problem:
force the code to wait for the data to be returned
return a Future that at some point will contain the data
pass a callback into get_user_det and call that function once the data is available
You will probably be tempted to selected option 1, since it matches most closely with your mental modal of loading data. While this is not necessarily wrong, keep in mind that there is a good reason that the loading is done asynchronously. It might be worth taking the "learning how to deal with asynchronicity" penalty now.
Instead of writing up examples for all solutions, I'll instead refer to some relevant questions:
Retrieving data from firebase returning NULL (an answer that uses approach 3)
Is waiting for return, ok?
Java wait() & notify() vs Android wait() & notify() (a question from a user taking approach 1)
How it works:
Firebase uses reflection to build a JSON tree object to save to the database. When you retrieve this JSON tree, you can cast it back to your original object. Just like serializing and deserializing. This means you do not need to handle the keys and values when trying to "rebuild" your object like you are. It can all be done like so:
YourObject object = (YourObject) dataSnapshot.getValue(YourObject.class);
Notice the YourObject.class in the getValue(). This tells firebase to reflect through this class and find the appropriate accessors with the dataSnapshot.
How to do it
Be sure that your object has:
Accessors Appropriate getters and setters for ALL fields - (or annotated with #JsonIgnore if you wish to not save a particular field)
Empty constructor. Your object must provide a constructor that does not modify itself at all.
What your object should look like:
public class YourObject {
private String displayName;
private String realName;
public YourObject() { /*Empty constructor needed for Firebase */ }
// Accessors
public void setRealName(String realName){
this.realName = realName;
}
public String getRealName(){
return this.realName;
}
public String getDisplayName(){
return this.displayName;
}
public void setDisplayName(String displayName){
this.displayName = displayName;
}
}
Then, in any of the firebase callbacks, you can just cast your DataSnapshot in to your object:
public void onDataChange(DataSnapshot snap_user) {
YourObject object = new Object;
if(snap_user.getValue() != null) {
try {
object = (YourObject) snap_user.getValue(YourObject.class); <-- Improtant!!!
} catch(ClassCastException ex) {
ex.printStackTrace();
}
}
return object;
}
Also
It seems you are retrieving many objects. When doing this, I find it best to use the onChildEventListener then for each of the YourObjects in that node, onChildAdded(DataSnapshot ds, String previousChild); will be called.

Firebase + Android - Notifications ChildAdded - How to get only New Childs?

I'm trying to setup an Android Service in my app that listens for new Childs in a Firebase Ref, and throws a Notification when that happens.
I'm having issues because addChildEventListener onChildAdded apparently is called one time for every existent record and only then actually listens for new childs..
In this answer #kato states that if addChildEventListener is called like ref.endAt().limit(1).addChildEventListener(...) it would get only the newly added records.
It actually only gets one record at a time (I suppose with limit(1)) but it still gets an existant record before listening for added records.
Here's some code:
Initializing the Listener in onCreate():
#Override
public void onCreate() {
super.onCreate();
this.handler = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
AllowedGroup ag = dataSnapshot.getValue(AllowedGroup.class);
postNotif("Group Added!", ag.getName());
}
...rest of needed overrides, not used...
I'm using the AllowedGroup.class to store the records, and postNotif to build and post the notification. This part is working as intended.
Then, onStartCommand():
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.f = new Firebase(FIREBASE_URL).child("users").child(this.currentUserUid).child("allowedGroups");
f.endAt().limit(1).addChildEventListener(handler);
return START_STICKY;
}
It still returns one existant record before actually listening for newly added childs.
I've also tried querying by timestamp, like so:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.f = new Firebase(FIREBASE_URL).child("users").child(this.currentUserUid).child("allowedGroups");
f.startAt(System.currentTimeMillis()).addChildEventListener(handler);
return START_STICKY;
}
Hoping that it would only get records set after the service was started. It doesn't get existant records, but doesn't even get newly added childs.
EDIT:
I've also thought of something like getting into memory first all of the existant records, and conditionally post a notification if the record brought by onChildAdded does not exist on the previously gathered list, but that seems a bit like overkill, and thought that could be an easier (more API-friendly) way of doing this, am I right ?
Can anyone provide me with some insight on this ? I can't really find anything on the official docs or in any StackOverflow question or tutorial.
Thanks.
If you have a field modifiedOn, you can index that and setup a query like:
Query byModified = firebaseEndpoint.orderByChild("modifiedOn").startAt(lastInAppTime);
For me the logic was is to have value -"status" for example- which needs to be validated before deciding whether it is really new or was an old record then I set the "status" to a different value so I don't get it next time:
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildKey) {
if(dataSnapshot.hasChildren()) {
String Id = (String) dataSnapshot.child("user_id").getValue();
String status = (String) dataSnapshot.child("status").getValue();
if (Id != null && Id.equals(storedId) && status != null && status.equals("created")) {
Log.d("INCOMING_REQUEST", "This is for you!");
sessionsRef.child(dataSnapshot.getKey()).child("status").setValue("received");
}
}
}
I overcome this problem by keeping items of firebase database reference node in a List; and whenever onChildAdded(...) method is triggered, check if the incoming datasnapshot is in your list or not; if not, then it's new data
In order to achieve this you must meet below conditions:
Have a unique value that must not repeated from item to item. (this can be easily achieved when you add items into Firebase using .push() method.
Override .equals() method of your model class in order to fulfill the comparison based on this unique value.
Here code snippets based on your inputs
Model class
public class AllowedGroup {
private String id;
public static List<AllowedGroup> sGroups;
// reset of fields ...
public AllowedGroup() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Override
public boolean equals(#Nullable Object o) {
// If the object is compared with itself then return true
if (o == this) {
return true;
}
// Check if o is an instance of AllowedGroup or not
if (!(o instanceof AllowedGroup)) {
return false;
}
// typecast o to AllowedGroup so that we can compare data members
AllowedGroup group = (AllowedGroup) o;
// Compare data based on the unique id
return (group.id).equals(id);
}
}
Listening to firebase added nodes
#Override
public void onCreate() {
super.onCreate();
this.handler = new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
AllowedGroup group = dataSnapshot.getValue(AllowedGroup.class);
if (!AllowedGroup.sGroups.contains(group)) {
// Here you'll receive only the new added values
}
}
// ...rest of needed overrides, not used...
}
}

Getting objects out of an inner class/listener

I finally managed to get an object out of an AsyncTask (DogAsyncTask) with an interface/listener (DogListener):
public String fout(String url) {
Dog d_in = new Dog("DogName");
DogAsyncTask task = new DogAsyncTask(d_in);
final String out = ""; <---Have tried but error for out becomes "The final local variable out cannot be assigned, since it is defined in an enclosing type"
task.setDogListener(new DogListener()
{
#SuppressWarnings("unchecked")
#Override
public void DogSuccessfully(String data) { <---The string I want to get
Log.e("DogListened", data);
out = data;
}
#Override
public void DogFailed() {}
});
task.execute(url);
return out;
}
My main activity calls this function (fout) and is supposed to get a String out of it. String data is already there and Log.e("DogListened", data); records it too. How can I return that outwards to my main activity? I have tried setting out = data and made out a final String on the outside but the "final local variable out cannot be assigned, since it is defined in an enclosing type" error comes up. How can I get around this?
Thanks
I guess you cannot access to out because it is out of the listener's scope. You can maybe pass your out as a reference parameter to the constructor of your DogListener.
final String out = "";
task.setDogListener(new DogListener( **out** )
{
#SuppressWarnings("unchecked")
#Override
public void DogSuccessfully(String data) {
Log.e("DogListened", data);
out = data;
}
#Override
public void DogFailed() {}
});
BUT honestly I donT know how to pass parameters as a reference in Java like in C#..
EDIT:
This can help you too: Can I pass parameters by reference in Java?
Use a value holder, which basically is an object which stores some value in a field. The value holder can be assigned as final, but you can change the value. Follow the link for more info on this pattern: http://martinfowler.com/eaaCatalog/lazyLoad.html
However you can use your string as value holder: Just append to the empty string assigned to out. Or better use a StringBuffer object.

Categories

Resources