Problem with Firebase adding a fab button cant delete entry - java

Currently a have a floating action bottom in my detailView in this activity I bring the information of the Object with an intent. I want that when the user enters the detailView activity I have a filled heart if that Automoviles objectId already exist in the Favorites Node.if not add the information into the Database. If the favorite already exist and it has the filled heart if the user clicks again it will remove it from the database.
This is my favorites class:
public class Favorites {
private String id;
private String automovilesId;
private String userId;
private String fecha;
public Favorites() {
}
public Favorites(String automovilesId, String userId, String fecha) {
this.automovilesId = automovilesId;
this.userId = userId;
this.fecha = fecha;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAutomovilesId() {
return automovilesId;
}
public void setAutomovilesId(String automovilesId) {
this.automovilesId = automovilesId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getFecha() {
return fecha;
}
public void setFecha(String fecha) {
this.fecha = fecha;
}
}
At the moment i have this code:
databaseReference = FirebaseDatabase.getInstance().getReference(AppModel.Favorites);
favoritesButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
final DatabaseReference usersRef = rootRef.child("Favorites");
favorites = new Favorites(automoviles.getObjectId(), user.getUid(), AppModel.GetDate());
databaseReference.push().setValue(favorites);
}
});
Which Leads this entry in the database:
Firebase Data
This is all good but i need to verify also if favorites exist or not in the database or else everytime i run the android emulator it will create another favorites node.
Now i added more code . But when i run it and i add this AddFavorite method it will create infinite loops of Favorite Nodes in the database
databaseReference = FirebaseDatabase.getInstance().getReference(AppModel.Favorites);
favoritesButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
final DatabaseReference usersRef = rootRef.child("Favorites");
favorites = new Favorites(automoviles.getObjectId(), user.getUid(), AppModel.GetDate());
databaseReference.push().setValue(favorites);
usersRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String car = ds.child("automovilesId").getValue(String.class);
Favorites fav =ds.getValue(Favorites.class);
String carId =fav.getAutomovilesId();
Log.i("No","Este es el nombre------>:"+carId);
String id=ds.getKey();
AddFavoritesuser();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
});
This is the AddFavorite method and the delete method:
private void AddFavoritesuser() {
favoritesButton.setImageResource(R.drawable.ic_favorite);
favorites = new Favorites(automoviles.getObjectId(), user.getUid(), AppModel.GetDate());
databaseReference.push().setValue(favorites);
}
And the delete:
private void deleteFavoritesUser(String key) {
favoritesButton.setImageResource(R.drawable.ic_favorite_border);
databaseReference.child(key).getRef().removeValue();
}
In short i want it to verify if that object of Automoviles object exist . If it exist the user enters and sees the filled heart, if he wants to delete this favorite then he clicks again and it will run the deleteFavoritesUser method. But my question is what if Favorites is still no created in the database how can i also validate this?.
I runned the app again and I have the same vale created again:
As you can see I have the same automovilesId entry

basically you need to make your favorite button as a toggle button
favoritesButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// in place of YES/NO you can take any values to present true/false state
boolean status = v.getTag() != null ? v.getTag().equals("YES") : false;
v.setTag(status ? "NO" : "YES");
if (status) {
// Do favorite me
//AddFavoritesuser();
} else {
// Do un favorite me
//deleteFavoritesUser(key);
}
}
}

everytime i run the android emulator it will create another favorites node.
This is happening because everytime you are trying to add a new object of Favorites class to the database, you are using the push() method, which generates a new random key everytime is called. This is what this method does. So in order to check a property from the database, you need to use in your reference the same key, not to generate another one. Please see in my answer from this post, where I have explained how you can achieve this.

Related

How to constantly update query for Firebase Realtime Database with LiveData and ViewModel in Android

I use Firebase Realtime Database and LiveData with ViewModel in Android and I would like to constantly update the query.
Here is the ViewModel class:
public class ViewModel_MainActivity extends ViewModel {
/*
LiveData with Database query for the Firebase node "Ratings"
*/
private static long currentTimeMillisRating = System.currentTimeMillis();
private static double pastMinuteForDisplayingRatings = 1;
private static long pastTimeMillisRatings = System.currentTimeMillis() - (long) (pastMinuteForDisplayingRatings * 60 * 60 * 1000);
private static final Query QUERY_RATINGS =
FirebaseDatabase.getInstance(DB_SQLite_Firebase_Entries.FIREBASE_URL).getReference().child(DB_SQLite_Firebase_Entries.FIREBASE_NODE_RATINGS).orderByChild(DB_SQLite_Firebase_Entries.FIREBASE_RATINGDATEINMILLISECONDS).endAt(pastTimeMillisRatings);
private final LiveData_FirebaseRating liveData_firebaseRating = new LiveData_FirebaseRating(QUERY_RATINGS);
#NonNull
public LiveData_FirebaseRating getDataSnapshotLiveData_FirebaseRating() {
Log.e("LogTag_ViMo", "pastTimeMillisRatings: " + pastTimeMillisRatings);
return liveData_firebaseRating;
}
}
Here is the LiveData class:
public class LiveData_FirebaseRating extends LiveData<DataSnapshot> {
private static final String LOG_TAG = "LiveData_FirebaseRating";
private Query query;
private final LiveData_FirebaseRating.MyValueEventListener listener = new LiveData_FirebaseRating.MyValueEventListener();
DataSnapshot currentDataSnapShotFromFirebase;
public LiveData_FirebaseRating(Query query) {
this.query = query;
}
public LiveData_FirebaseRating(DatabaseReference ref) {
this.query = ref;
}
public void changeQuery(Query newQuery) {
this.query = newQuery;
}
public DataSnapshot getCurrentDataSnapShotFromFirebase() {
return currentDataSnapShotFromFirebase;
}
public void setCurrentDataSnapShotFromFirebase(DataSnapshot currentDataSnapShotFromFirebase) {
this.currentDataSnapShotFromFirebase = currentDataSnapShotFromFirebase;
}
#Override
protected void onActive() {
Log.d(LOG_TAG, "onActive");
query.addValueEventListener(listener);
}
#Override
protected void onInactive() {
Log.d(LOG_TAG, "onInactive");
query.removeEventListener(listener);
}
private class MyValueEventListener implements ValueEventListener {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
setValue(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(LOG_TAG, "Can't listen to query " + query, databaseError.toException());
}
}
}
And here is the part of the main activity, where the live data and view model are created and observed:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
/*
Initiate View Model with LiveData for Firebase
*/
rootRef_Firebase = FirebaseDatabase.getInstance(DB_SQLite_Firebase_Entries.FIREBASE_URL).getReference();
viewModel = new ViewModelProvider(this).get(ViewModel_MainActivity.class);
liveData_firebaseRating = viewModel.getDataSnapshotLiveData_FirebaseRating();
liveData_firebaseRating.observe(this, new Observer<DataSnapshot>() {
#Override
public void onChanged(#Nullable DataSnapshot dataSnapshot) {
liveData_firebaseRating.setCurrentDataSnapShotFromFirebase(dataSnapshot);
if(liveData_firebaseRating.getCurrentDataSnapShotFromFirebase()!=null) {
//Do something with the dataSnapshot of the query
}
}// end onChange
}); //end observe
}
So my problem lies in the non-updating of the QUERY_RATINGS in the class ViewModel_MainActivity . What I want is to return all the nodes from the FIREBASE_NODE_RATINGS whose at attribute FIREBASE_RATINGDATEINMILLISECONDS is older than 1 Minute (meaning that the entry was created at least 1 Minute before the current time slot). How can I do that such that I always get these nodes?
Firebase Query objects are immutable, and their parameters are fixed values. So there is no way to update the existing query, nor is there a "one minute ago" value that automatically updates.
The options I can quickly think off:
Create a new query frequently to capture the new full range of relevant nodes.
Create a new query frequently to capture only the new nodes (by combining startAt and endAt) and merge them with the results you already have.
Remove the condition from the query, and perform the filtering in your application code.
Based on what I know, I'd probably start with the third option as it seems the simplest.

How pass firebase id listed in listview to another activity with setOnItemClickListener?

Currently I have a ListView, which shows the data stored in Firebase.
I try to send the ID of each stored object, but my "putExtra" only sends the visible data, that is, the name (I have decided that this is the visible data by creating a toString method returning the name in the pojo class)
Pojo:
private String idParcela;
private String nombreParcela;
private int hectareas;
private Date ultimoTratamienetoQuimico;
private Date proximoTratamientoQuimico;
private String fruta;
private String variedad;
private String nombreTratamiento;
private int diasTratarQuimicamente;
//GETTERS SETTERS BUILDERS...
#Override
public String toString(){return nombreParcela; }
Now I will show the 2 functions
ListData collects the data from firebase, and stores it in the listview, with the arrayadapter.
private void listarDatos() {
databaseReference.child("parcelas").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
listaParcelas.clear();
for(DataSnapshot objSnaptshot : dataSnapshot.getChildren()){
Parcela p = objSnaptshot.getValue(Parcela.class);
listaParcelas.add(p);
}
arrayAdapterParcela = new ArrayAdapter<Parcela>(listaParcelas.this, android.R.layout.simple_list_item_1,listaParcelas);
listvListaParcela.setAdapter(arrayAdapterParcela);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
listvListaParcela.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int i, long l) {
Intent intentEditar = new Intent(listaParcelas.this,editarParcela.class);
intentEditar.putExtra("idParcelaModificable",listvListaParcela.getItemAtPosition(i).toString());
startActivity(intentEditar);
}
});
}
At the time of sending the intentEditar.putExtra only sends the data name and forgets all the data stored but not visible, which it contains (I know that the data is there, since in the debugger it can be observed)
I have finally succeeded, and I have extracted the object from the arrayadapter
After that I have extracted the id using get located in the pojo, and I have sent it to the intent.
In my case, I can send the whole object or just the id, whichever I prefer.
listvListaParcela.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int i, long l) {
Parcela DatosParcelaClick = arrayAdapterParcela.getItem(i);
String IdParcelaString = DatosParcelaClick.getIdParcela().toString();
Intent intentEditar = new Intent(listaParcelas.this,editarParcela.class);
//2 options
intentEditar.putExtra("IdParcelaClick",IdParcelaString);
intentEditar.putExtra("objetoParcelaClick",DatosParcelaClick);
startActivity(intentEditar);
}
});
}

Update Firebase database data

I am trying to update my databse:
This is my code, but it doesn't seem to update the database. (Write access is set to true in Firebase settings)
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
ref.child("items").child("1").child("itemAvailability").setValue("Not Available");
Java class
public class Item {
String itemName;
String itemAvailability;
public Item(String itemName, String itemAvailability) {
this.itemName = itemName;
this.itemAvailability = itemAvailability;
}
public Item() {
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getItemAvailability() {
return itemAvailability;
}
public void setItemAvailability(String itemAvailability) {
this.itemAvailability = itemAvailability;
}
}
You can do something like this with a Map Data structure.
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
DatabaseReference itemRef = ref.child("items/1/itemAvailability");
Map<String, Object> itemUpdates = new HashMap<>();
itemUpdates.put("itemAvailability", "Not Available");
itemRef.updateChildrenAsync(itemUpdates);
This will take the item 1's availability and update it's availability to Not Available
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
ref.child("items").child("1").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
HashMap<String, Object> updateMap = new HashMap<>();
updateMap.put("itemAvailability", "Not Available");
ref.updateChildren(updateMap);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
Might work for you.
Here is the code for Firebase real-time database Insert, Update & Delete operation(CRUD).
//****UPDATE
var database = firebase.database();
let userRef = database.ref('myroot/items/');
userRef.child("1").child("itemAvailability").update("Not Available").then().catch();
//****Insert
var database = firebase.database();
let userRef = database.ref('myroot/items/');
userRef.child("1").child("itemAvailability").set("Not Available").then().catch();
//****Delete
let itemId=1;
let userRef = database.ref('myroot/items/' + itemId);
userRef.remove().then().catch();

Hide the snippet in the markers so users can't see the postId

The issue is that for my markers on the map I have the title which is fine, and then for the snippet() I inserted the postId so that later I can use that postId for the OnInfoWindowClickListener to search for it in the database and if it matches it takes the user to the PostDetailFragment which has all the post's data...
Obviously I don't want the user to see the postId when they click on the marker the info Window comes up, but at that moment the snippet should hide, so I need to replace it, or make it invisible, or GONE...
Also, to identify the markers I tried using setTag() and getTag(), but it didn't work.
Suggestions? I need to hide or make invisible the snippet in each tag so the users don't see the postid, BUT I need the postId to run the query in the database.
Maybe there's a better way to do this?
MapFragment
LatLng latLng = new LatLng(latitude, longitude);
// MarkerOptions markerOptions = new MarkerOptions().position(latLng).title(post.getText_event()).snippet(post.getText_location() + " \u25CF " + post.getTime());
MarkerOptions markerOptions = new MarkerOptions().position(latLng).title(post.getText_event()).snippet(post.getPostid());
markerOptions.icon(BitmapDescriptorFactory.defaultMarker());
mGoogleMap.addMarker(markerOptions);
// Info Window
mGoogleMap.setOnInfoWindowClickListener(marker -> {
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference("Posts");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot1) {
for (DataSnapshot snap : snapshot1.getChildren()) {
Post pos = snap.getValue(Post.class);
if (pos != null) {
if (marker.getSnippet().equals(pos.getPostid())) {
SharedPreferences.Editor editor = getActivity().getSharedPreferences("PREFS", Context.MODE_PRIVATE).edit();
editor.putString("postid", marker.getSnippet());
editor.apply();
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new PostDetailFragment(), null).addToBackStack(null).commit();
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
});
}
Using the default marker has some of those limitations.
What you should do is create a class called CustomMarker that implements ClusterItem then you can use that as the Marker.
ClusterItem is simply a Marker.
public class Marker implements ClusterItem {
private final LatLng positiond;
private final String markerId;
private final String title;
private final String tag;
private final Post post;
private final String snippet;
public Marker(LatLng positiond, String markerId, String title, String snippet, Post post,String tag) {
this.positiond = positiond;
this.markerId = markerId;
this.title = title;
this.snippet = snippet;
this.post=post;
this.tag=tag;
}
#Override
public LatLng getPosition() {
return positiond;
}
#Override
public String getTitle() {
return title;
}
#Override
public String getSnippet() {
return snippet;
}
public String getMarkerId() {
return markerId;
}
public Post getPost() {
return post;
}
public String getTag() {
return tag;
}
}
The above is a Marker. Then you can just pass the Post into the Marker like so above.
When you create the marker/clusterItem you can pass the MarkerID as the PostId then when you click it. you can get the markerID then perform any of the logic you want.

How to generate a random question from firebase to a quiz app?

I'm trying to create my first android app. I have created quiz, connected with Firebase. It works fine, but I need to show only 10 questions - from the total 30, randomly. Also, I don't want to repeat the question. How can I generate random question? Here is the code I made. Thanks
private void updateQuestion() {
mQuestionRef = new Firebase("https://ab-quiz.firebaseio.com/"+ mQuestionNumber +"/biq");
mQuestionRef.addValueEventListener(new com.firebase.client.ValueEventListener() {
#Override
public void onDataChange(com.firebase.client.DataSnapshot dataSnapshot) {
String question = dataSnapshot.getValue(String.class);
mQuestion.setText(question);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
if(mQuestionNumber > 10){
quitFunction();
}
else {
mQuestionNumber++;
}
}
Try these steps:
Add all your questions in a List.
Maintain a count of how many questions have you displayed starting with 0.
Use the code below to get a random number within your list size.
Random rand = new Random();
int n = rand.nextInt(list.size);
Get the question from your list using list.get(n).
After your question is displayed remove the item from the list using list.remove(n).
Increment your count of how many questions have you displayed declared in step 2.
Start another process after you have reached your limit of displaying questions.
Hope this helps.
Thank you.
Here is a possible answer -
Firstly, you should create a QuizQuestion.java where you can set a questionId and the questionText, like this -
public class QuizQuestion {
private int questionId;
private String questionText;
public ChatMessage(int questionId, String questionText, ) {
this.messageText = messageText;
this.messageUser = messageUser;
}
public QuizQuestion(){
}
public int getQuestionId() {
return questionId;
}
public void setQuestionId(int questionId) {
this.questionId = questionId;
}
public String getQuestionText() {
return questionText;
}
public void setQuestionText(String questionText) {
this.questionText = questionText;
}
}
Then, call it in MainActivity.java -
public class MainActivity extends AppCompatActivity {
private TextView questionView;
private Button nextButton;
private int questionCount = 0;
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
questionView = findViewById(R.id.question_view);
nextButton = findViewById(R.id.next_button);
displayQuestion();
nextButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
displayQuestion();
}
});
}
public void displayQuestion() {
// This methods queries your database for a question with the Id of an random integer from 1 to 30 and sets the text of the TextView to the question text
final Query questionToDisplay = FirebaseDatabase.getInstance()
.getReference().child("questions")
.orderByChild("questionId")
.equalTo(new Random().nextInt(30)); // Generate a random integer from 1 to 30
questionToDisplay.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot question : dataSnapshot.getChildren()) {
if (questionCount is < 10) {
questionView.setText(question.getValue(QuizQuestion.class).getQuestionText);
questionCount += 1;
} else if (questionCount >= 10) {
finish(); // Close Activity
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.e("ItemDetail", "onCancelled", databaseError.toException());
}
});
}
If you want add a question, just add these lines wherever you want -
FirebaseDatabase.getInstance().getReference().child("questions").push
.setvalue(new QuizQuestion(1, /* Question Id */"What sound does a fox make?" /* Question Text */));
Hope this helps!!
EDIT:
Here is the asssumed database structure -
--quiz-app // Root node
--questions
--Lxjcksduso12m42i4m
--questionId: "1"
--questionText: "What sound does a fox make?"
--Lxjcksduso12m42i4m
--questionId: "2"
--questionText: "What is the capital of Italy?"
--Lxjcksduso12m42i4m
--questionId: "3"
--questionText: "When will the polar ice caps melt completely?"
Suppose if you have a questions like below image in firebase database.
database = FirebaseFirestore.getInstance();
ArrayList<Question> questions;
String categoryId = getIntent().getStringExtra("categoryId");
database.collection("categories")
.document(categoryId)
.collection("Questions")
.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for(DocumentSnapshot snapshot : queryDocumentSnapshots) {
Question question = snapshot.toObject(Question.class);
questions.add(question);
}
Collections.shuffle(questions);
}
});
In my case, I have a category (for example computer category, software category) and in each category we have a collection of questions Therefore we get a specific categoryId to display questions of specific category.
first you get all the question from database and add in a list like questions.add(question); if all the added in a list then you shuffle the questions in the list, Collections.shuffle(questions);
so each time you get different questions.

Categories