I am making and application that show a first screen with a bunch of randomly picked data from a node in Firebase database.
To present user with different data every time is kind of important for my application
Is there anyway to achieve this in native Android, all snapshots are of same model
There is no direct way provided by firebase database but you can do this using Collections.shuffle()
What i did was,Take the snapshot and store it in an arraylist.
private ArrayList<Integer> array=new ArrayList<>();
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot imageSnapshot : dataSnapshot.getChildren()) {
MyClass myclass = imageSnapshot.getValue(MyClass.class);
array.add(myclass.someFunction());
}
}
Then call the shuffle method on this array list.
Collections.shuffle(array); // randomize the arraylist
Now you can do whatever you want with this randomized arraylist.
Don't think there is a way to randomly grab data from the Firebase database as all queries that you can construct end up being deterministic in some way, either based on the generated push ids (which in turn are based on time) or some other key ordered lexicographically. I think the best way would be to grab a list of data from a node and randomly choose the data client side.
There actually is a possibility to do that without Loading the whole list client side. First you have to generate a numeric id either as child id or as an extra node.
That how your database would look like:
notes:
-KXe8LJJzczEJs3YYwRe
numericid : 001
-KXeePWrWBXvpw4g9n0p
numericid : 002
or
notes:
001
002
to create the numbers as String you can use DecimalFormat
String newint = new DecimalFormat("000").format(oldint);
Now you can get the children count in your valueeventlistener an use Math.random() to get a random child, e.g. for the second Database Design
FirebaseDatabase().getInstance().getReference().child("notes").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Long childrencount = dataSnapshot.getChildrenCount();
if(childrencount.equals(0))return;
int random = getRandomInteger(safeLongToInt(childrencount), 1);
String selectedchild = new DecimalFormat("000").format(random);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
You also need to add these safeLongtoInt and getRandomInteger
public static int getRandomInteger(int maximum, int minimum){
return ((int) (Math.random()*(maximum - minimum))) + minimum;
}
public static int safeLongToInt(long l) {
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
throw new IllegalArgumentException
(l + " cannot be cast to int without changing its value.");
}
return (int) l;
}
selectedchild is your random child id.
Related
I am new to android studio and programming and am currently trying to make my first app. In firebase RTDB, I have multiple push ids under a single child that keep on increasing in number as the user presses certain buttons, and they all store only an integer. I want to retrieve the data from all those push ids(or keys, I don't really know what they are actually called) under the child and then sum the integers up and display the result in a textView and do it every time a new field is added. How can I do that? So far I only know how to normally send and receive data to childs sub-childs like this but i have never ever used push ids or keys before:
String recieve = datasnapshot.child("Child").child("subchild").getText().toString();
String send = "send";
databaseReferenc.child("Child").child("sunchile").setValue(send);
The data tree in firebase is as follows:
Assuming that Australia is a direct child of your Firebase Realtime Database root, to sum all those values, please use the following lines of code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference australiaRef = rootRef.child("Australia");
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
int total = 0;
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String value = Integer.parseInt(ds.getValue(String.class));
total += value;
}
Log.d("TAG", "total: " + total);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d("TAG", databaseError.getMessage()); //Don't ignore potential errors!
}
};
australiaRef.addListenerForSingleValueEvent(valueEventListener);
One more thing. Because you are storing numbers, it best to store them as long values and not as String values.
I'm having trouble getting from my Firebase database data to an android app and the Firebase listener and data snapshot documentations aren't really helping.
Say I have a database with the nodes structured as below:
Contacts-->
John : 1334255
May : 3345777
James : 5799862
Ford : 4574878
How can I directly retrieve the contacts from the nodes with the key and value both as strings , without having to cast them into some object(that's not the string object).
I want to be able to display the names(keys) as contact names and the numbers(their values) as Contact number.
Please try this code:
DatabaseReference contactsRef = FirebaseDatabase.getInstance().getReference().child("Contacts");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String name = ds.getKey();
String contactNumber = ds.getValue(String.class);
Log.d("TAG", name + ": " + contactNumber);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
contactsRef.addListenerForSingleValueEvent(eventListener);
Hope it helps.
tl;dr write them as something other than numbers
The values in your example are Long (integers). Assuming they're phone numbers, you'll probably want to store them as strings anyway. When you write a value to Firebase, it will infer the type and store it as one of the following:
String (will appear in quotes)
Long (whole number)
Double (decimal number)
Boolean (true or false without quotes)
Map<String, Object> (key with children)
List<Object> (key with children)
In your case, since you want to treat it as a String type when it's being read, just write it as a string:
If you're setting the values from client code, use the String type when you create the value.
If you're manually entering them from the dashboard, you can either wrap them in quotes or use non-numeric characters like the familiar format (123) 456-7890. Any value you type in that doesn't evaluate to a Long/Double/Boolean will be interpreted as a String.
I want to create a nickname to each user who login to my app for the first time. The nickname is the user's firstname and I add a number to it from 1 to 9 000 000. For example kevin Bond could get the nickname kevin123456.
I have all the nicknames stored in a node of my firebase database. Two users shouldn't have the same nickname, so I have to check wether the nickname already exists or not in my database. If it already exists, I want to create another nickname until I get a new nickname.
I currently:
- add a listener to the usernickname node
- in onDataChange I have a for loop to create a nickname, then I check if it already exists with dataSnapshot.hasChild(newNickname). If the nickname doesn't exist I do what I want with the nickname and break the for loop, if it exists I loop the for loop to try another nickname. Here is my code:
mFirebaseDatabase.getReference().child("usernickname").orderByKey().addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (int i = 0; i < 9000000; i++) {
final Random random = new Random();
randomNumberId = random.nextInt(9000000);
String newNicknameId = mFirstnameId + String.valueOf(randomNumberId);
//if the nickname already exists we restart the for loop to get a new random number
if (dataSnapshot.hasChild(newNicknameId)) {
i++;
//else if the nickname doesn't exists we create it and stop the loop
} else if (!dataSnapshot.hasChild(newNicknameId)) {
//do what I want...
break;
}
}
}
});
My issue is that I currently have to download all the nicknames node or at least all the nicknames beginning with the user's firstname. It is an issue for me because it could cost me a lot in GB downloaded if I have a lot of users :D
I guess I could use datasnapshot.exists() to check if the nickname exists without having to download all the nicknames, but I don't see how to create another nickname if it already exists. I cannot have a for loop outside of the listener and break it inside of the listener when needed.
Do you see what is the best way to create a new nickname for my users?
Thank you,
Alex
A better approach for unique nicknames would be to append a time stamp to the name:
username + new Date().getTime();
This will add a unique long like 1493808526335 and you can be certain that two users are unlikely to register in the same millisecond with the same name.
You need to use another aproch to check if a user exists. You need to change this line:
dataSnapshot.hasChild(newNicknameId);
with
dataSnapshot.child("userName").getValue().equals(newNicknameId);
In which userName is the name of field in which you store the newNicknameId, assuming your key -> value pair looks like this: userName: "newNicknameId"
Hope it helps.
final Query query = mFirebaseDatabase.getReference().orderByChild("usernickname").equalTo("yourNewNickName");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
//here means the value exist
//do whatever you want to do
} else {
//here means the value not exist
//do whatever you want to do
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
use this will solve your problem. :)
I want to get first the number of children (if any). Then I want to add 1 to number of children, so the added children will be +1 from the existing.
So when I add new question, II want to add number 3. But I get it added as number 1 and it updates the existing number.
int count = 0;
public void addQuestion(View view) {
mDatabase = FirebaseDatabase.getInstance();
mRef = FirebaseDatabase.getInstance().getReference("Questions");
count = count + 1;
String nr = String.valueOf(count);
GetData getdata = new GetData(qs, A, B, C, correct);
mRef.child(nr).setValue(getdata);
Toast.makeText(AdminPanel.this, "Adding question...", Toast.LENGTH_SHORT).show();
}
You can use the push function and it will generate a unique key, so you don't have to worry about giving then unique numbers.
mRef.push();
you can also get the key that was generated if you need it,
https://firebase.google.com/docs/database/admin/save-data#getting-the-unique-key-generated-by-push
or just retrieve all the children (your questions) when needed.
I suggest you read the documentation, I found there all the information I needed.
https://firebase.google.com/docs/database/
I have three ArrayLists, two are Strings and the last one is an Integer.
The first ArrayList contains the specific Variant (variantArray) for a certain Product like the flavor variant of a cola, the second one contains the Unit (unitArray) that contains the unit like the size (80oz, 500mL, 1L) of the product, and the last one is the quantity *(quantityArray).
This is the class I use.
public class CurrentOrderClass {
//ArrayLists
private ArrayList<String> variantArray = new ArrayList<String>();
private ArrayList<String> unitArray = new ArrayList<String>();
private ArrayList<Integer> quantityArray = new ArrayList<Integer>();
//TODO ArrayList functions
public ArrayList<String> getUnitArray() {
return unitArray;
}
public void setUnitArray(ArrayList<String> unitArray) {
this.unitArray = unitArray;
}
public void addToUnitArray(String unit){
this.unitArray.add(unit);
}
public ArrayList<Integer> getQuantityArray() {
return quantityArray;
}
public void setQuantityArray(ArrayList<Integer> quantityArray) {
this.quantityArray = quantityArray;
}
public void addToQuantityArray(int quantity){
this.quantityArray.add(quantity);
}
public ArrayList<String> getVariantArray() {
return variantArray;
}
public void setVariantArray(ArrayList<String> variantArray) {
this.variantArray = variantArray;
}
public void addToVariantArray(String variantArray){
this.variantArray.add(variantArray);
}
#Override
public String toString() {
return "[ product=" + productName + ", variants=" +
variants + " , unit=" + unit + " , quantity=" + quantity + "]";
}
}
I take in user input so the user chooses the Variant, Unit, and Quantity and the input is then stored in their respective ArrayLists.
However, I'm having a problem updating the ArrayLists when the user inputs a Variant and a Unit that already exists in the ArrayLists but only with a different Quantity. What I want to do is not to add the new entry, but update the current ArrayLists in such a way that when the user inputs a variant and a unit but different quantity, I'd only update the quantity.
The first thing I tried was to see if the input already exists in the ArrayList by using indexOf(userInputVariant) and then check if it matches with a indexOf(userInputUnit), this would mean that the user input repeated already. However, I don't think indexOf runs on that logic and the value it returns is the value where it found the first instance of the userInputVariant string.
The second attempt I tried was using a for each loop, however, I'm once again having a hard time returning the index I want properly.
I instantiate an object of the CurrentOrderClass named currentOrder and prepopulated the first 10 elements of its variantArray with "Grape"
After that, I tried this if-else statement:
if( (currentOrder.getVariantArray().indexOf(product.getVariant()[variantPosition]) ==
currentOrder.getUnitArray().indexOf(product.getUnit()[position])
However, as mentioned above, indexOf returns an int where it first found the String that I told it to find, I don't think it checks if it exists after that certain position.
The second code I tried was to use a for-each and return the position from there, but again, it only returned position = 0.
for( String Grape : currentOrder.getVariantArray() ){
Log.d("Angelo", "Retrived Element: " + Grape
+ " at position = " + currentOrder.getVariantArray().indexOf(Grape));
}
I'm currently thinking of running a for-each loop inside a for-each loop but I don't know how to make it return the proper position that I want.
Does anyone have an idea on how to make it work properly? I need to return the position of an item that appears multiple times in my variantArray by "cross-referencing" it with my unitArray. Thanks.
One for loop is enough in this case.
This will some thing like:
isExist = false;
for(index = 0; index<variantArray.size(); index++){
if(variantArray.get(index) == userChoiceVariant &&
unitArray.get(index) == userChoiceUnit){
//update quantity
isExist = true;
}
}
if(!isExist){
//insert order
}
The first solution I reach is: "Search all the indexes matching with the user input in the variant array, store this in another ArrayList<Integer>, name it, matchingVariants for now. Then, for each of the ints in matchingVariants get that position of the units list. If one of this items matches, then you should update, otherwise insert.
However, I think a better solution is wrap Variants and Units in one class, for example Product with attributes String variant and String unit. Implementing equals method you can forget about your variants and units lists and have only one ArrayList<Product>
As a next step, I would use a Map with Product as key and Integer as values. So I can just update the Map rather than having all the lists you have.