I have an authenticateID method which searches in the database to find a match and does something. I guess it will take long to explain so here is my code:
public boolean authenticateStudentID() {
boolean success = true;
final String studentID = etStudentID.getText().toString().trim();
final String module = etModule.getText().toString().trim();
final String degree = etDegree.getText().toString().trim();
final String room = etRoom.getText().toString().trim();
final String email = etEmail.getText().toString().trim();
final String fullname = etfullname.getText().toString().trim();
final String loginID = etLoginID.getText().toString().trim();
if (success) {
databaseRef.addListenerForSingleValueEvent(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) { // wtf is this advanecd for loop
//map string string because our key is a string and value is a string, map has a key and value object
Map<String, String> map = (Map) snapshot.getValue();
if (map != null) { //if the values and keys are not null
String studentIDMatch = map.get("studentID");
// Log.v("E_VALUE", "students ID entered : " + studentIDMatch);
// Log.v("E_VALUE", "students ID from db: " + studentID);
if (studentID.equals(studentIDMatch)) {
String uniqueKey = databaseRef.push().getKey();
NewStudentAccounts sam = new NewStudentAccounts
(studentID, loginID, email, fullname, module, degree, room);
databaseRef.child(uniqueKey).setValue(sam);
Toast.makeText(getApplicationContext(), "Your account registration has been successful!", Toast.LENGTH_SHORT).show();
startActivity(new Intent(getApplicationContext(), LoginActivity.class));
} else {
Toast.makeText(getApplicationContext(), "Invalid Student Credentials Entered!!", Toast.LENGTH_SHORT).show();
}
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
return success;
I want to know how I can reuse this method for another class instead of copy and pasting code. Please guide me, I really appreciate it.
private void addNewStudent() {
findViewById(R.id.buttonAddStudent).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
View addStudentActivityDialog = LayoutInflater.from(LecturerAccount.this).inflate(R.layout.activity_add_student,null);
etStudentName = addStudentActivityDialog.findViewById(R.id.editTextStudentName);
etStudentUserID = addStudentActivityDialog.findViewById(R.id.editTextStudentUserID);
AlertDialog.Builder addStudentBuilder = new AlertDialog.Builder(LecturerAccount.this);
addStudentBuilder.setMessage("STAR").setView(addStudentActivityDialog).setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
String studentName = etStudentName.getText().toString();
String studentID = etStudentUserID.getText().toString();
registerActivity = new RegisterActivity(); //calling the instance of the class here
if (registerActivity.authenticateStudentID() == true){
studentarray.add(studentName);
}
}
}).setNegativeButton("cancel", null).setCancelable(false);
AlertDialog newStudentDialog = addStudentBuilder.create();
newStudentDialog.show();
}
});
}
My if statement here calling the function, I am totally clueless here.
Since onDataChange(DataSnapshot dataSnapshot) is an asynchronous callback event from firebase you must implement your own callback method to get notified of the result.
One approach would be to use interfaces.
create a separate class Auth
public class Auth {
public static void authenticateStudentID(final String studentID, final AuthListener listener) {
DatabaseReference databaseRef = FirebaseDatabase.getInstance().getReference("your reference");
databaseRef.addListenerForSingleValueEvent(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) { // wtf is this advanecd for loop
//map string string because our key is a string and value is a string, map has a key and value object
Map<String, String> map = (Map) snapshot.getValue();
if (map != null) { //if the values and keys are not null
String studentIDMatch = map.get("studentID");
if (studentID.equals(studentIDMatch)) {
if (listener != null)
listener.onAuthSuccess();
} else {
if (listener != null)
listener.onAuthFailure();
}
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
if (listener != null)
listener.onAuthFailure();
}
});
}
public interface AuthListener {
void onAuthSuccess();
void onAuthFailure();
}
}
And then call it by
Auth.authenticateStudentID(studentId, new Auth.AuthListener() {
#Override
public void onAuthSuccess() {
}
#Override
public void onAuthFailure() {
}
});
wherever required
As the method you want to reuse should be "public" first of all. It simply means that it can be publically accessed among other classes of that project. And after making it public you can simply refer it using the class name.
Here is an example of this :
Class2 instance = new Class2();
instance.publicMehtodToBeAcessedInThisClass(any parameters);
But in your case, you will have to copy and paste the code to another class file only.
Reason: Because you are fetching data from the layout file of your Java file and this will crash the app. Either you should further modularize your code and handle this by making a separate function for fetching all this data. Otherwise, copy pasting only a method from one class to another will not make your application run into any performance issue or lags.
Access modifier is incorrect. Good old java doc will explain better than me:
access modifiers
In order to access it, you have to create an instance like so:
YourClass yourClass = new YourClass();
yourCLass.authenticateStudentID();
The YourClass is usually the name of the file where this code you pasted located in.
From what you've shown, there are two issues you need to deal with:
As noted, having it private doesn't do you much good when it comes to reuse.
It looks like the databaseRef object is a class property. So you'll need to pass this in, rather than rely on the class property for that class, since you want to use it from another class. (Or you can put this method, and the databaseRef property, in a superclass and have your two classes inherit from it.)
In general - think about what your method needs to do, and then what it needs to do that. Those should shape how you make the method more usable from other parts of your code.
Related
Before adding a new data into the firestore, i want to check already a data of the same kind exists in the database or not.if already a data was present means i want to prevent the user from entering duplicate data in the database.
In my case it is like a appointment booking if already a booking for the same time exists,i want to prevent to users to book on the same time.i tried using query function but it is not preventing duplicate data entering.someone plz help me
private boolean alreadyBooked(final String boname, final String bodept, final String botime) {
final int[] flag = {0};
CollectionReference cref=db.collection("bookingdetails");
Query q1=cref.whereEqualTo("time",botime).whereEqualTo("dept",bodept);
q1.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for (DocumentSnapshot ds : queryDocumentSnapshots) {
String rname, rdept, rtime;
rname = ds.getString("name");
rdept = ds.getString("dept");
rtime = ds.getString("time");
if (rdept.equals(botime)) {
if (rtime.equals(botime)) {
flag[0] = 1;
return;
}
}
}
}
});
if(flag[0]==1){
return true;
}
else {
return false;
}
}
Loading data from Cloud Firestore happens asynchronously. By the time you return from alreadyBooked, the data hasn't loaded yet, onSuccess hasn't run yet, and flag still has its default value.
The easiest way to see this is with a few log statements:
private boolean alreadyBooked(final String boname, final String bodept, final String botime) {
CollectionReference cref=db.collection("bookingdetails");
Query q1=cref.whereEqualTo("time",botime).whereEqualTo("dept",bodept);
System.out.println("Starting listener");
q1.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
System.out.println("Got data from Firestore");
}
});
System.out.println("Returning");
}
If you run this code it will print:
Starting listener
Returning
Got data from Firestore
That's probably not the order you expected. But it perfectly explains why you always get false when calling alreadyBooked: the data simply didn't come back from Firestore in time.
The solution for this is to change the way you think about the problem. Your current code has logic: "First check if it is already booked, then add a new item". We need to reframe this as: "Start checking if it is already booked. Once we know that is isn't, add a new item." In code this means that all code that needs data from Firestore must be inside the onSuccess or must be called from there.
The simplest version is to move the code into onSuccess:
private void alreadyBooked(final String boname, final String bodept, final String botime) {
CollectionReference cref=db.collection("bookingdetails");
Query q1=cref.whereEqualTo("time",botime).whereEqualTo("dept",bodept);
q1.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
boolean isExisting = false
for (DocumentSnapshot ds : queryDocumentSnapshots) {
String rname, rdept, rtime;
rname = ds.getString("name");
rdept = ds.getString("dept");
rtime = ds.getString("time");
if (rdept.equals(botime)) {
if (rtime.equals(botime)) {
isExisting = true;
}
}
}
if (!isExisting) {
// TODO: add item to Firestore
}
}
});
}
While this is simple, it makes alreadyBooked less reusable since now it contains the code to insert the new item too. You can solve this by defining your own callback interface:
public interface AlreadyBookedCallback {
void onCallback(boolean isAlreadyBooked);
}
private void alreadyBooked(final String boname, final String bodept, final String botime, AlreadyBookedCallback callback) {
CollectionReference cref=db.collection("bookingdetails");
Query q1=cref.whereEqualTo("time",botime).whereEqualTo("dept",bodept);
q1.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for (DocumentSnapshot ds : queryDocumentSnapshots) {
String rname, rdept, rtime;
rname = ds.getString("name");
rdept = ds.getString("dept");
rtime = ds.getString("time");
if (rdept.equals(botime)) {
if (rtime.equals(botime)) {
isExisting = true;
}
}
}
callback.onCallback(isExisting)
}
});
}
And then you call it as:
alreadyBooked(boname, bodept, botime, new AlreadyBookedCallback() {
#Override
public void onCallback(boolean isAlreadyBooked) {
// TODO: insert item
}
});
Also see (many of these are for the Firebase Realtime Database, where the same logic applies):
getContactsFromFirebase() method return an empty list
Doug's blog post on asynchronous callbacks
Setting Singleton property value in Firebase Listener
Android + Firebase: synchronous for into an asynchronous function
Is it possible to synchronously load data from Firebase?
Querying data from firebase
I have a problem with my Firebase database data retrieve sync. I am able to get data but return works before it. So my data never put in the intended list.
Here is my code:
DatabaseManager databaseManager = new DatabaseManager();
MedicineData medicineData = new MedicineData();
boolean validated = false;
private static final String TAG = "BarcodeDecoderDataMatrix";
public Map getDataMatrix(String dataMatrixText) {
Map<String, Object> dataMatrix = new HashMap<>();
String barcodeNum = getBarcodeNumber(dataMatrixText);
String expireDate = getExpireDate(dataMatrixText);
String serialNum = getSerialNumber(dataMatrixText);
String partyNum = getPartyNumber(dataMatrixText);
dataMatrix.put("barcodeNumber",barcodeNum);
dataMatrix.put("expireDate", expireDate);
dataMatrix.put("serialNumber", serialNum);
dataMatrix.put("partyNumber", partyNum);
getMedicineName(barcodeNum, (success) -> {
if(success){
//find the data on database
dataMatrix.put("medicineName", medicineData.getProductName());
dataMatrix.put("companyName", medicineData.getCompanyName());
dataMatrix.put("price", medicineData.getPrice());
}
else {
//can't find on database
}
});
return dataMatrix;
}
This method called from another class to get dataMatrix list.
private void getMedicineName(String barcodeNum, SimpleCallback<Boolean> finishedCallback) {
DatabaseReference rootRef = databaseManager.getReference();
DatabaseReference medicinesRef = rootRef.child("MedicineList");
Query queryMedicineNameFinder = medicinesRef.orderByKey().equalTo(barcodeNum);
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
medicineData = ds.getValue(MedicineData.class);
}
if (medicineData != null){
validated = true;
}
finishedCallback.run(validated);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
finishedCallback.run(false);
}
};
queryMedicineNameFinder.addListenerForSingleValueEvent(valueEventListener);
}
These two methods are in the same class called BarcodeDecoderDataMatrix. I create an instance of this class from another class and call getDataMatrix method. I am expecting to get a list include my values. I can get barcodeNum, expireDate, serialNum, partyNum values without a problem. But the list doesn't include medicineName, companyName and price informations. I made a debug so I know I can get data from database. I can see it in medicineData variable. I am pretty sure it is a sync issue. Because my data in medicineData but it can't put it in the list before return called.
How can I make this happen?
addListenerForSingleValueEvent() method that you are using in your code, is asynchronous. This means that it returns immediately, while onDataChange() method fires only when the results are available.
What you are doing in your code is incorrect because you assuming that the results are immediately available and are not.
When you call getMedicineName() method, you should directly use dataMatrix in the callback, after the data is known that is available.
For more information, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
The data is saved correctly, as you can see in the following image:
I use this code:
btnDatos.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String name = nameTextView.getText().toString();
String email = emailTextView.getText().toString();
String id = idTextView.getText().toString();
String idJuego = idffTextView.getText().toString();
Map hopperUdates = new HashMap();
hopperUdates.put("name", name);
hopperUdates.put("email", email);
hopperUdates.put("id", id);
hopperUdates.put("idFreeFire", idJuego);
mDatabase.child("Usuario").child(mAuth.getCurrentUser().getUid()).updateChildren(hopperUdates);
}
});
And to obtain the data I use the following code:
mDatabase.child("Usuario").child(mAuth.getCurrentUser().getUid()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
int idJuego = dataSnapshot.getValue(Integer.class);
idffTextView.setText(idJuego);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
But it does not show the data and when I test it on the device the app is closed. What would be the error?
If you want all data then create model class and then stored datasnapshot into that model class like this
Model model = dataSnapshot.getValue(Model.class);
Or you want single data then just get the child like this
String idJuego = dataSnapshot.child("childname").getValue().toString;
And then set that string into textView.
try to convert from Integer to string...
int idJuego = dataSnapshot.getValue(Integer.class);
idffTextView.setText(String.valueOf(idJuego ));
Because you are saving the uid as the key of your user object and in the same time as a property within the user object, to set that id to your idffTextView, please use either the following lines of code:
String idJuego = dataSnapshot.getkey();
idffTextView.setText(idJuego);
Or:
String idJuego = dataSnapshot.child("id").getValue(String.class)
idffTextView.setText(idJuego);
Please note, that in both cases the id is of type String and not int.
I'm trying create a "read" method which will get a value from Firebase (Google server) using android studio (Java)
I wrote the code below but the problem that the value[0] always returning "" as a value and not the value from the server.
When Im inside the method "onDataChange",the value[0] is equal to the value from the server but outside, its back to the original value.
What is wrong with my code?
please help
/**
* Return value
*
* #param parent
* #param child
* #param key
* #return
*/
public static String read(String parent, String child, String key) {
final String[] value = {""};
// Get a reference to our posts
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference(parent).child(child);
// Attach a listener to read the data at our posts reference
ref.child(key).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String str = dataSnapshot.getValue(String.class);
System.out.println(str);
value[0] = str;
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getCode());
}
});
return value[0];
}
}
its an asynchronous method,try something like this
public static String read(String parent, String child, String key) {
final String[] value = {""};
// Get a reference to our posts
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference(parent).child(child);
// Attach a listener to read the data at our posts reference
ref.child(key).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String str = dataSnapshot.getValue(String.class);
System.out.println(str);
value[0] = str;
passingValue(value[0]);
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getCode());
}
});
return value[0];
}
private void passingValue(String temp){
//do something with your string
}
outside the scope of OndataChange,the value won't be retained
This is happening due the asynchronous behaviour of onDataChange() method which is called even before you are getting that data from your database. A quick fix would be to declare and use your array inside onDataChange() method like this:
ref.child(key).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String[] value = {""}; //declaration of the array
String str = dataSnapshot.getValue(String.class);
System.out.println(str);
value[0] = str;
passingValue(value[0]);
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getCode());
}
});
Please see the declaration of the array inside onDataChange() method. For a more complex answer, please see my answer from this post, which also contains a reference on how to use that value outside onDataChange() method
I have an activity on my app where a user can update their registered information stored in a remote database. When the update button is pressed the information in the database is being updated but the static variable is not changing. Here is my code thanks in advance for any help!
btUpdate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final String first_name = First_name.getText().toString();
final String last_name = Last_name.getText().toString();
final String email = Email.getText().toString();
Response.Listener<String> responseListener = new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonResponse = new JSONObject(response);
boolean success = jsonResponse.getBoolean("success");
if (success) {
LoginActivity.first_name = jsonResponse.getString("first_name");
LoginActivity.last_name = jsonResponse.getString("last_name");
LoginActivity.email_address = jsonResponse.getString("email");
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(UpdateInfoActivity.this);
builder.setMessage("Submission Failed")
.setNegativeButton("Retry", null)
.create()
.show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
};
UpdateInfoRequest updateInfoRequest = new UpdateInfoRequest(first_name, last_name, email, userID, responseListener);
RequestQueue queue = Volley.newRequestQueue(UpdateInfoActivity.this);
queue.add(updateInfoRequest);
Intent intent = new Intent(UpdateInfoActivity.this, MainActivity.class);
UpdateInfoActivity.this.startActivity(intent);
}
});
Change your code to this
if (success) {
LoginActivity.first_name = first_name;
LoginActivity.last_name = last_name;
LoginActivity.email_address = email;
}
and I wouldn't be using static variables like that if you want to have a global user profile you could do this
class User {
private static User user = null;
public String firstName = "";
private User() {}
public static synchronized User getInstance() {
if (user == null) user = new User();
return user;
}
}
to retrieve data from anywhere in project call this
String name = User.getInstance().firstName;
And to modify the data do this
User.getInstance().firstName = UserName;
First Understand that static variables are shared by all objects and methods of the class.
So we only have one instance of the static variable.
The ways to Update static variable from other class -
1.Through object.
2.Through Class name.
Enclosing the code sample.
class A{
static int val;
public A(){val=0; }
//....
}
class B{
A obj= new A();
public void updateStatic(){
obj.val=10; // updates values through object to 10
A.val=100; //updates values through class name to 100
}
//..
}
Hope it Helps
Transfer of data between activities using the static variable is not a better way in my opinion. It is bad practice. Transferring data using intents or save data in storage media and accessing from there will be the better solution.
but the static variable is not changing.
Should be... You told the code to do that
if (success) {
LoginActivity.first_name = jsonResponse.getString("first_name");
LoginActivity.last_name = jsonResponse.getString("last_name");
LoginActivity.email_address = jsonResponse.getString("email");
}
Just want to mention...
1) You update a String, not any TextView or EditText in your question, so if you expected to see a "visual change" in your app, then no, nothing will happen unless you call setText.
2) That code is wrapped in a try-catch, and could error, so check the logs for a JSONException. If those keys aren't sent back from the server, then sure, they won't update. For example, the JSON is only {"success": true }
Still, SharedPrefences should largely be preferred over static variables here.