I am trying to get the id of the logged in user and use it in two fragments. In the fragment I am using first, id gets retrieved and stored in my global variable properly but when I access the other fragment after that, my app crashes saying that getUid() is generating a null pointer exception in the other activity.
Here is my code (Both fragments have identical codes except for different variable names. LDQApp is my class for global variables)
public class RecruitmentFragment extends Fragment {
private DatabaseReference mDatabase;
EditText name_text,reg_text,year_txt, mob_txt,interest_txt;
Button button;
public RecruitmentFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_recruitment, container, false);
name_text = (EditText)rootView.findViewById(R.id.editText2);
reg_text = (EditText)rootView.findViewById(R.id.editText3);
year_txt = (EditText)rootView.findViewById(R.id.editText4);
mob_txt = (EditText)rootView.findViewById(R.id.editText5);
interest_txt = (EditText)rootView.findViewById(R.id.editText6);
button = (Button)rootView.findViewById(R.id.button2);
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
mDatabase=FirebaseDatabase.getInstance().getReference("recruitment");
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
LDQApp.uid = firebaseUser.getUid();
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final String name,regNo,mobNo,interest;
int year;
name = name_text.getText().toString();
regNo = reg_text.getText().toString();
year = Integer.parseInt(year_txt.getText().toString());
mobNo = mob_txt.getText().toString();
interest = interest_txt.getText().toString();
RecruitUser recruitUser = new RecruitUser(name,regNo,year,mobNo,interest);
mDatabase.child(LDQApp.uid).setValue(recruitUser);
mDatabase.child(LDQApp.uid).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
RecruitUser sUser = dataSnapshot.getValue(RecruitUser.class);
Toast.makeText(getContext(),"Submitted successfully",Toast.LENGTH_SHORT).show();
Log.d(TAG, "User name: " + name + ", Registration No: " + regNo);
}
#Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.w(TAG, "Failed to read value.", error.toException());
}
});
}
});
return rootView;
}
}
This is the log as requested
Get your FirebaseDatabase like this:
you don't need to call that and assign new value again an again use a static one;
public class Util {
private static FirebaseDatabase database;
public static FirebaseDatabase getDatabase() {
if (database == null) {
database = FirebaseDatabase.getInstance();
database.setPersistenceEnabled(true);
}
return database;
}
}
then you can access this as Util.getDatabase() or you can do it keeping a static boolean too as i said in that comment, but this avoids more code redundancy too.
read documentation
This is quite easy just create a new class that extends Application
public class FireApp extends Application {
#Override
public void onCreate() {
super.onCreate();
/* Enable disk persistence */
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
}
connect it to your project in the manifest file by adding the name attribute
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:name=".FireApp"
android:supportsRtl="true"
android:theme="#style/AppTheme">
Don't assume, when using Firebase Authentication, that once a user is logged in, that the user will always be logged in. Don't store the UID in a global like that. Wherever you need to know the login status of the user, you typically should use an AuthStateListener to keep track of that. The callback you pass it will always be invoked with the correct login status of the user, and you can react to that information accordingly.
Also, if you are going to use getCurrentUser() to try to get the currently logged in user, note that it can return null when the user is not logged in. That's probably what's happening to you, and why calling getUid() is throwing an NPE for you.
Related
I am trying to retrieve some data from Firebase, but it is not working for one of my nodes
Note that the UnverifiedEmployees node works fine but the Companies node is not
I am able to see the Log RIGHT BEFORE the Event Listener, but nothing inside of it prints
I am referring to this area:
public void addDataToFirebase() {
Log.i("BEFORE", "BEFORE COMPANIES REF");
companiesRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.i("COMPANIESREF", "INSIDE COMPANIES REF ONDATACHANGE");
I have tried this on a different phone. I have tried it on wifi and 4g. I also tried logging in with a different user
I have also tried uninstalling the app from my phone and reinstalling. I have tried writing different implementations of the addListener.
I have also tried looking this up but did not find a lot (e.g. Android Firebase addListenerForSingleValueEvent not called) ( Firebase Android addListenerForSingleValueEvent sometimes not returning data)
Database: (If UserID exists, grab the CompanyID its related to)
Entirety of Code: AddEmployeeActivity.java:
public class AddEmployeeActivity extends Activity {
private EditText firstNameET, lastNameET, phoneNumberET, emailET, ssnET;
private ImageView checkmarkImage;
private static FirebaseUser currentUser;
private static final String TAG = "RealtimeDB";
private FirebaseDatabase database;
private DatabaseReference unverifiedRef, companiesRef;
String emailKey, ssnKey, phoneKey, companyId;
int num;
private FirebaseMethods firebaseMethods;
private Context mContext;
ArrayList<EmployeeUser> myListItems;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register_addemployees);
mContext = AddEmployeeActivity.this;
firstNameET = (EditText) findViewById(R.id.firstNameET);
lastNameET = (EditText) findViewById(R.id.lastNameET);
phoneNumberET = (EditText) findViewById(R.id.phoneNumberET);
emailET = (EditText) findViewById(R.id.emailET);
ssnET = (EditText) findViewById(R.id.ssnET);
checkmarkImage = (ImageView) findViewById(R.id.checkmarkImage);
database = FirebaseDatabase.getInstance();
unverifiedRef = database.getReference("/Unverified Employees");
companiesRef = database.getReference("/Companies");
currentUser =
FirebaseAuth.getInstance().getCurrentUser();
checkmarkImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final String email = emailET.getText().toString().trim();
final String ssn = ssnET.getText().toString().trim();
final String phone = phoneNumberET.getText().toString().trim();
//Add EditText info to Firebase UnverifiedEmployees Node
addDataToFirebase();
}
});
} //End of ONCREATE
public void addDataToFirebase() {
Log.i("BEFORE", "BEFORE COMPANIES REF");
//companiesRef.addValueEventListener(new ValueEventListener(){
companiesRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.i("COMPANIESREF", "INSIDE COMPANIES REF ONDATACHANGE");
EmployeeUser user = new EmployeeUser();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
for (DataSnapshot ds2 : ds.getChildren()) {
for (DataSnapshot ds3 : ds2.getChildren()) {
for (DataSnapshot ds4 : ds3.getChildren()) {
for (DataSnapshot ds5 : ds4.getChildren()) {
Log.i(TAG, "checkIfUsernameExists: datasnapshot: " + ds5);
//user.setCompanyId(ds5.getValue(EmployeeUser.class).getCompanyId());
user.setCompanyId(ds5.getValue(String.class));
Log.i(TAG, "checkIfUsernameExists: ID: " + user.getCompanyId());
//callback.gotDataSnapshot(dataSnapshot);
if (user.getCompanyId() != null) {
companyId = user.getCompanyId();
}
}
}
}
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.i("Cancelled", "In onCancelled: " + databaseError);
}
//};
}); //END OF ADDLISTENER
// companiesRef.addListenerForSingleValueEvent(eventListener);
emailKey = unverifiedRef.child(currentUser.getUid()).push().getKey();
unverifiedRef.child(currentUser.getUid()).child(emailKey).child("emailAddress").setValue(emailET.getText().toString(), completionListener);
Log.i("getKey EMAIL", emailKey);
ssnKey = unverifiedRef.child(currentUser.getUid()).push().getKey();
unverifiedRef.child(currentUser.getUid()).child(ssnKey).child("socialSecurityNumber").setValue(ssnET.getText().toString(), completionListener);
unverifiedRef.child(currentUser.getUid()).child(ssnKey).child("CompanyID").setValue(companyId, completionListener);
Log.i("getKey SSN", ssnKey);
Log.i("get COMPANY ID", companyId);
phoneKey = unverifiedRef.child(currentUser.getUid()).push().getKey();
unverifiedRef.child(currentUser.getUid()).child(phoneKey).child("phoneNumber").setValue(phoneNumberET.getText().toString(), completionListener);
Log.i("getKey Phone", phoneKey);
}
You cannot achieve this in the way you do. There is no way in which you can get a parent based on the value of one of its childs. With other words, you cannot get the value of L4bs8bH5... if that id RC9zI1... exists beneath it. To solve this, you need to consider using denormalization, which is a common practice when it comes to Firebase. This means that you need to duplicate data in order have those results. For that, I recomend you see this tutorial, Denormalization is normal with the Firebase Database, for a better understanding.
So, in this case you typically should consider augmenting your data structure to allow a reverse lookup. For example, in your scenario, I'd add a list of companies under a userId. Doing this, you'll be able to query all the companies that belong to a single user, if obviously it exists.
I think the problem lies in the references, you're creating.
If you want to get reference to "Unverified Employees" and "Companies" nodes you should create
unverifiedRef = database.getReference("Unverified Employees");
companiesRef = database.getReference("Companies");
instead of
unverifiedRef = database.getReference("/Unverified Employees");
companiesRef = database.getReference("/Companies");
(notice missing slash / sign in the reference string name)
Hope this will help
I have users on my Application and I store additional information about them in Firebase Database. I need to retrieve additional information in more than one Activity. I do not want to use ValueEventListeners because they are not called unless there is any change in the database. How can I get information about users from Database without using ValueEventListeners?
In my ProfileFragment I need to get name and departmant values.
I get current user from Firebase and I tried to take other information with a function.
talker = new DatabaseTalk();
FirebaseUser currentUser = mAuth.getCurrentUser();
// Get info of logged in user with talker.
loggedInUser = talker.getUserFromID(currentUser.getUid());
This is my DatabaseTalker class to handle read and write operations to database
public class DatabaseTalk {
private FirebaseDatabase mDatabase;
private DatabaseReference UserRef;
private DatabaseReference SurveyRef;
private List<User> userList;
public DatabaseTalk(){
mDatabase = FirebaseDatabase.getInstance();
UserRef = mDatabase.getReference("users");
UserRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot child: dataSnapshot.getChildren()){
userList.add(child.getValue(User.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w("Error", "Failed to read value.", databaseError.toException());
}
});
SurveyRef = mDatabase.getReference("surveys");
}
public void WriteUser(User usr){
UserRef.child(usr.getUserID()).setValue(usr);
}
public void WriteSurvey(Survey survey){SurveyRef.push().setValue(survey);}
public User getUserFromID(String id){
for(User usr: userList){
if(usr.getUserID().equals(id))
return usr;
}
return null;
}
}
I think, I can take additional information about users from userList in DatabaseTalk but userList is null always.
EDIT
I changed getUserFromID method. OnDataChange() does not work when I called getUserFromID method.
public User getUserFromID(String id){
DatabaseReference newRef = mDatabase.getReference("users");
DatabaseReference ds = newRef.child(id);
ds.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
userList.add(dataSnapshot.getValue(User.class));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return userList.get(0);
}
I solved the problem. It turned out I do not know how Firebase works actually. Since onDataChange() make async calls, writing Listener definitions on a function is useless because onDataChange mostly does not trigger before function terminates and this cause function to return null value.
I make the definition of ValueEventListeners in onCreate methods. It triggers now after few seconds my ProfileFragment created. I think it is better to use Progress Dialogs to wait.
Thanks to everyone who interested in the question.
So I am currently developing an API which gets data from Firebase and depending on that data the button changes its color. However, when I tried to make a new structure for my buttons. It won't let me make one.
I tried the answers here: How to insert data of two tables in Firebase? and here: How can I create an empty table from android app in Firebase? but neither did work for me.
Here's my sample code:
public class Normal extends AppCompatActivity {
Button suite, normal, room1;
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle mToggle;
private DatabaseReference mFirebaseDatabase1;
private FirebaseDatabase mFirebaseInstance;
//FIREBASE AUTH FIELDS
DatabaseReference mSearchedLocationReference;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_normal);
mFirebaseInstance = FirebaseDatabase.getInstance();
//FIREBASE
mFirebaseDatabase1 = mFirebaseInstance.getReference("Rooms");
mSearchedLocationReference = mFirebaseDatabase1.child("Room1").child("RoomStatus");
//ASSIGN ID's
room1 = (Button) findViewById(R.id.room2);
room1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mFirebaseDatabase1.child("Rooms").child("Room1").child("RoomStatus").setValue("Green");
startActivity(new Intent(Normal.this, room2.class));
}
});
mSearchedLocationReference.addValueEventListener(new ValueEventListener() { //attach listener
#Override
public void onDataChange(DataSnapshot dataSnapshot) { //something changed!
for (DataSnapshot locationSnapshot : dataSnapshot.getChildren()) {
String location = locationSnapshot.getValue().toString();
Log.d("Locations updated", "location: " + location); //log
if ( location.equals("Green")){
room1.setBackgroundColor(Color.GREEN);
}else if ( location.equals("Red")){
room1.setBackgroundColor(Color.RED);
}
else{
room1.setBackgroundColor(Color.YELLOW);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) { //update UI here if error occurred.
}
});
}
}
PS: I created the User structure (which is for my login) on another class.
I think you should make a new structure at firebase you can do it by clicking the "+" beside your DB Name, here's an example:
Implement your methods and it should work. Then Connect again your firebase database.
I think the problem is when you are changing the value when (room 1) is clicked. You already declared this reference like this:
mFirebaseDatabase1 = mFirebaseInstance.getReference("Rooms");
when you clicked the button (room 1) you triggered this reference to change value :
mFirebaseDatabase1.child("Rooms").child("Room1").child("RoomStatus").setValue("Green");
In other words
you are saying that you want to access
Rooms/Rooms/Room1/RoomStatus/Green
but you should be looking for this
Rooms/Room1/RoomStatus/Green
you have an extra child called Rooms that is repeated and causing the problem
Possible Solution
do this in the click listener of room 1
room1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mFirebaseDatabase1.child("Room1").child("RoomStatus").setValue("Green");
startActivity(new Intent(Normal.this, room2.class));
}
});
I just removed the extra(.child("Rooms")).
What i think is going wrong is that an array is returning and its last item is green
for (DataSnapshot locationSnapshot : dataSnapshot.getChildren())
that is not letting you change the color of the button .
we will be able to help you if you show the model of your database .
I'm trying to get a user's profile from a Firebase DB. Then using the user's information I want to set TextViews in my Fragment's layout to reflect the user's individual stats.
The problem is that the rootViw is being returned prior to having recieved the user's profile. And so I get a null object reference error.
My understanding of the fragment's life cycle is that onCreate() is created first and so I tried placing the DB code there but I get the same problem. I then figured that if accessing the DB is slower than my onCreateView() I'll place a Thread.sleep() timer to wait for the DB call to complete and then perform the rest of my code. Which I know is a stupid solution but just wanted to test my theory; that also failed so obviously my understanding is wrong.
Where should I place my DB call so that it completes prior to returning my rootView? Why does placing the DB listener in OnCreate() not work and why does the Thread.sleep() delay not work?
Leaderboard Fragment
public class Leaderboard extends Fragment{
private FirebaseAuth mFirebaseAuth;
private DatabaseReference mUserDatabaseReference;
private User user;
private TextView scoreView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_leaderboard,
container, false);
scoreView = rootView.findViewById(R.id.leaderboard_score);
mFirebaseAuth = FirebaseAuth.getInstance();
final String userUID = mFirebaseAuth.getCurrentUser().getUid();
mUserDatabaseReference =
FirebaseDatabase.getInstance().getReference("users");
mUserDatabaseReference.addListenerForSingleValueEvent(new
ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.hasChildren()) {
for (DataSnapshot messageSnap: snapshot.getChildren()) {
if(messageSnapshot.getKey().equals(userUID)) {
user = messageSnapshot.getValue(User.class);
}}}}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
//Causes error because user==null
scoreView.setText("Score: " + user.getScore());
return rootView;
}
}
All Firebase APIs are asynchronous. You should expect that listeners may be called after any amount of time, based on the quality of the hardware and its network connection. Don't ever use Thread.sleep() to try to control the timing of things - that is an anti-pattern.
My suggestion to you is to inflate a "loading" screen in onCreateView() to display immediately, so the user doesn't have to look at a blank screen when your fragment starts. Then, when your listener is called with the data you want to display, add or update other views as needed.
Why not set the score after you get the DB result ?
mUserDatabaseReference.addListenerForSingleValueEvent(new
ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.hasChildren()) {
for (DataSnapshot messageSnap: snapshot.getChildren()) {
if(messageSnapshot.getKey().equals(userUID)) {
user = messageSnapshot.getValue(User.class);
}
}
scoreView.setText("Score: " + user.getScore());
}}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
I have recently been working with Android development. I have been developing a social networking app. For the app, I decided to create a separate helper class for all database methods. In my database, all users have a user id and their information is stored under this id. I have a (non-static) method in this class that would get certain User information when given a DatabaseReference to the user's information location. The method would simply take the reference, add a listener for single value event (addListenerForSingleValueEvent(ValueEventListener)). I was encountering problems with this so I tried putting a Log statement in the onDataChange() method of the ValueEventListener. Oddly enough, this Log method was never reached. Even more strange is the fact that, if I copy and paste the code from this method into one of the locations where I need it, the Log statement is reached. Does anyone have any idea as to why this happens? This is a method that I am using in multiple activities and copying and pasting the code everywhere would make the code very sloppy. Any help would be much appreciated. Thank you!
Update: It turns out the code works if placed in the Database class, but the Log statement will only run after the method is over. Below is the an outline of the class I am using to observe this.
Fragment Class
public class FragmentClass extends Fragment {
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDatabase = DatabaseManager.getInstance();
String userId = "userId";
mDatabase.getUserFromUserId(userId);
}
}
Database Class
public class DatabaseManager {
private static FirebaseDatabaseManager mInstance;
private static FirebaseDatabase mDatabase;
public static FirebaseDatabaseManager getInstance() {
if(mInstance == null) {
mInstance = new FirebaseDatabaseManager();
}
return mInstance;
}
private FirebaseDatabaseManager() {
mDatabase = FirebaseDatabase.getInstance();
}
public void getUserFromUserId(final String userId) {
DatabaseReference userReference = mDatabase.getReference(userId);
userReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.i("databaseTag", "reached");
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.i("databaseTag", "reached");
}
});
while(true) { // if this part is commented out, the log statement will be executed; otherwise, it won't
}
}
}