I am developing a location based chat application as a final assignment, but have one bug that I cannot figure out how to fix by myself. Currently, I intend to load all profiles within a certain radius into a recyclerview, and display only these profiles to the current user.
The recycler view is working fine and displays every user in my Firebase database, until I add the GeoFire query to limit the users that appear only to those within the 2km radius. All user latitudes and longitudes are being updated successfully to the database, so I don't think that this is where the problem originates.
When i run the app and it crashes, I get this exception in the LogCat:
"java.lang.NullPointerException: Attempt to invoke virtual method 'double android.location.Location.getLatitude()' on a null object reference"
This is confusing me, as none of the current users in my database have a null value stored for either their longitude or latitude.
So my main question is, how can I get only the users within 2km of the current user to populate my recyclerview, without the application crashing?
Database structure
The current source code:
public class FindChatters extends Fragment {
private RecyclerView mUsersList;
private View mMainView;
private DatabaseReference mUsersDatabase;
private FirebaseUser mCurrentUser;
private LatLng myCurrentLocation;
private String mUserFound;
private static final int RADIUS = 2;
Location mLastLocation;
public FindChatters() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
mMainView = inflater.inflate(R.layout.fragment_find_chatters, container, false);
mUsersDatabase = FirebaseDatabase.getInstance().getReference().child("Users");
mCurrentUser = FirebaseAuth.getInstance().getCurrentUser();
mUsersList = (RecyclerView) mMainView.findViewById(R.id.users_list);
mUsersList.setHasFixedSize(true);
mUsersList.setLayoutManager(new LinearLayoutManager(getContext()));
return mMainView;
}
//RETRIEVE DATA IN REALTIME
#Override
public void onStart() {
super.onStart();
startListening();
}
public void startListening() {
Query query = FirebaseDatabase.getInstance().getReference().child("Users").limitToLast(50);
FirebaseRecyclerOptions<Users> options = new FirebaseRecyclerOptions.Builder<Users>().setQuery(query, Users.class).build();
FirebaseRecyclerAdapter adapter = new FirebaseRecyclerAdapter<Users, UserViewHolder>(options) {
#Override
public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// CREATE NEW INSTANCE OF VIEWHOLDER, USING CUSTOM LAYOUT (R.LAYOUT.MESSAGE)
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.users_single_layout, parent, false);
return new UserViewHolder(view);
}
#Override
protected void onBindViewHolder(final UserViewHolder holder, final int position, final Users model) {
// // //
//RETRIEVE CURRENT USERS LAT/LONG TO FIND USERS NEARBY
String userID = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference myLoc = FirebaseDatabase.getInstance().getReference().child("Geo");
final GeoFire geoFire = new GeoFire(myLoc);
geoFire.setLocation(userID, new GeoLocation(mLastLocation.getLatitude(), mLastLocation.getLongitude()));
myCurrentLocation = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
//RETRIEVE USERS ONLY FROM WITHIN A SPECIFIED RADIUS TO THE CURRENT USER
DatabaseReference findNearby = FirebaseDatabase.getInstance().getReference().child("Geo");
GeoFire geoFire2 = new GeoFire(findNearby);
//QUERY ALL NEARBY USERS IN THE DATABASE WITHIN 2KM OF CURRENT USER LAT/LONG
final GeoQuery geoQuery = geoFire2.queryAtLocation(new GeoLocation(myCurrentLocation.latitude, myCurrentLocation.longitude), RADIUS);
//QUERY TO RETRIEVE CLOSET USERS
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
//IF ANY USERS FOUND WITHIN RADIUS - ON KEY ENTERED IS CALLED
#Override
public void onKeyEntered(String key, GeoLocation location) {
if (geoQuery != null) {
mUserFound = key;
//BIND CHAT OBJECT TO CHATHOLDER
holder.setName(model.name);
holder.setUserStatus(model.status);
holder.setUserOnline(model.online);
holder.setUserImage(getContext(), model.image);
//CLICK ON A USER PROFILE TO ACCESS THEIR INFORMATION OR INITIATE CHAT
final String user_id = getRef(position).getKey();
//RETRIEVE CURRENT USER ID
String current_uid = mCurrentUser.getUid();
//PREVENT USER FROM BEING ABLE TO INITIATE A CONVERSATION WITH THEMSELF
if (!user_id.equals(current_uid)) {
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent profileIntent = new Intent(getActivity(), ProfileActivity.class);
profileIntent.putExtra("user_id", user_id);
startActivity(profileIntent);
}
});
} else {
holder.setName("You");
holder.setUserStatus("Tap someone to say hello!");
}
} else {
//CREATE TEXTVIEW TO INFORM USER THAT NO NEARBY USERS ARE PRESENT
}
}
#Override
public void onKeyExited(String key) {
}
#Override
public void onKeyMoved(String key, GeoLocation location) {
}
#Override
public void onGeoQueryReady() {
}
#Override
public void onGeoQueryError(DatabaseError error) {
}
});
// // //
}
};
mUsersList.setAdapter(adapter);
adapter.startListening();
}
I am still pretty new to coding, so I really appreciate any replies or help. Thanks!
Android Studio v3.1.3
Make sure you initialise mLastLocation. I can see that you are retrieving lat and lng from it but was never initialised
Related
In my code, I am getting the user's location using FusedLocationProviderClient. In the callback, I am saving the latitude and longitude of the user on Firebase real time database. Everything works fine even when the user moves from one place to another, the only problem is, every time it saves the coordinates on Firebase, the activity refreshes, how do I stop this automatic refreshing?
I am posting only the onCreate method and the call back method, if more code is needed, I will provide it.
Everything is working fine in my code except the activity refreshes every time new coordinates are saved on Firebase?
Note: My code is in a Fragment
private static final String TAG = "DriverMapFragment";
int LOCATION_REQUEST_CODE = 10001;
FusedLocationProviderClient fusedLocationProviderClient;
LocationRequest locationRequest;
double latitude,longitude;
DatabaseReference databaseReference;
String schoolName, driverPhone, vehicleNumberPlate;
TextView newLat,newLng;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_driver_map, container, false);
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(getActivity());
locationRequest = LocationRequest.create();
locationRequest.setInterval(4000);
locationRequest.setFastestInterval(2000);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
databaseReference = FirebaseDatabase.getInstance().getReference();
Intent intent = getActivity().getIntent();
schoolName = intent.getStringExtra("SchoolName");
driverPhone = intent.getStringExtra("DriverPhone");
vehicleNumberPlate = intent.getStringExtra("VehicleNumberPlate");
newLat = view.findViewById(R.id.newLat);
newLng = view.findViewById(R.id.newLng);
return view;
}
LocationCallback locationCallback = new LocationCallback(){
#Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null){
return;
}
for (Location location : locationResult.getLocations()){
Log.d(TAG,"onLocationResult: " + location.toString());
latitude = location.getLatitude();
longitude = location.getLongitude();
Log.i("Lat",String.valueOf(latitude));
Log.i("Lng",String.valueOf(longitude));
LocationHelper helper = new LocationHelper(
location.getLongitude(),
location.getLatitude()
);
FirebaseDatabase.getInstance().getReference().child("Schools")
.child(schoolName)
.child("drivers")
.child(vehicleNumberPlate)
.child("driverLocation")
.setValue(helper).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if(task.isSuccessful()){
Log.i("Success","Location Saved");
}
else{
Log.i("Failure","Location Not Saved");
}
}
});
}
}
};
Make sure you use addListenerForSingleValueEvent instead of addValueEventListener, otherwise when you update data to Firebase, addValueEventListener will be called again and run the Activity you called in.
So, I have an activity in my app where I need to take in the number of tickets for different ticket classes that are retrieved from the backend. The number of ticket classes is also variable. How do I take the user input in this case? Each time the countdown_btn or countup_btn is pressed, I need to update an array that holds the number of tickets the user has chosen. How do I do this when the number of ticket classes itself is dynamic?
If the button 'pledge' is clicked, I want to take the respective inputs from each of the views here and somehow communicate it to the next activity using intent.
My app's code:
public class RewardsAndPledgeActivity extends AppCompatActivity {
DatabaseReference mRewardsRef;
RecyclerView rewards_list;
String Artcall_id;
String reward_id[];
Integer counter = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_rewards);
/* -------- Obtain the Event ID of the item that the user has selected on the RecyclerView ------*/
Intent intent = getIntent();
Artcall_id = intent.getStringExtra("Artcall_id");
/* ----------------------------------------------------------------------------------------------*/
mRewardsRef = FirebaseDatabase.getInstance().getReference().child("Rewards").child(Artcall_id);
rewards_list = (RecyclerView) findViewById(R.id.reward_list);
rewards_list.setHasFixedSize(true);
rewards_list.setLayoutManager(new LinearLayoutManager(this));
}
#Override
protected void onStart() {
super.onStart();
FirebaseRecyclerAdapter<Reward_List, RewardsViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Reward_List, RewardsViewHolder>(
Reward_List.class,
R.layout.single_reward_item_layout,
RewardsAndPledgeActivity.RewardsViewHolder.class,
mRewardsRef
) {
#Override
protected void populateViewHolder(final RewardsViewHolder viewHolder, Reward_List model, int position) {
final String reward_id = getRef(position).getKey();
viewHolder.setReward_ticket_amount_txt(model.getReward_ticket_amount_txt());
viewHolder.setReward_ticket_amount_class_name(model.getReward_ticket_amount_class_name());
viewHolder.setReward_ticket_class_desc(model.getReward_ticket_class_desc());
viewHolder.countdown_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer current_ticket_count = Integer.parseInt(viewHolder.ticket_counter.getText().toString());
if(current_ticket_count >0 ) {
viewHolder.ticket_counter.setText(String.valueOf(current_ticket_count - 1));
}
}
});
viewHolder.countup_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer current_ticket_count = Integer.parseInt(viewHolder.ticket_counter.getText().toString());
viewHolder.ticket_counter.setText(String.valueOf(current_ticket_count + 1));
}
});
}
};
rewards_list.setAdapter(firebaseRecyclerAdapter);
}
}
My database:
Image of the activity:
just a quick explanation of my project before I get to my question so you can get the gist of everything.
I'm currently working on this real-estate application with android and Firebase. I have been using Firebase to store my data of my app and to authenticate users.
I can add real estate objects to the database and retrieve the all the added real estates in a list view. When I click on one list item I'm getting a more detailed version of the real estate. The details of the real estates are shown in four different tabs. In one of the tabs I give the user the oppertunity to add attachments like pictures to the real estate.
I added the functionality to add the pictures just fine. They are stored in the database as shown in the following screenshot.
The eBncv5ke05ZxR32AiRoP9gSyPkO2 is the user_id and the
"-L38Qe8GEo33i5roKOCi" the Realestate id. the keys are the name of the image and the values the urls.
Here is the code that shows how I add the pictures to the database:
private void saveImage() {
// get the expose id
bundle = getArguments();
immoID = bundle.getString("exposeID");
Toast.makeText(getContext(), immoID, Toast.LENGTH_SHORT).show();
// get an reference to the current user and his id
user = FirebaseAuth.getInstance().getCurrentUser();
user_id = user.getUid();
imageName = imageNameInput.getText().toString();
if (!TextUtils.isEmpty(imageName)) {
//displaying progress dialog while image is uploading
final ProgressDialog progressDialog = new ProgressDialog(getContext());
progressDialog.setTitle("Bild wird hochgeladen");
progressDialog.show();
// uploading the Picture
pictureStorageRef = FirebaseStorage.getInstance().getReference(user_id).child(Constants.STORAGE_PATH_UPLOADS).child(immoID);
pictureDataRef = FirebaseDatabase.getInstance().getReference(Constants.DATABASE_PATH_UPLOADS).child(user.getUid()).child(immoID);
//checking if file is available
if (filePath != null) {
//getting the storage reference
StorageReference sRef = pictureStorageRef.child(Constants.STORAGE_PATH_UPLOADS + System.currentTimeMillis() + "." + getFileExtension(filePath));
//adding the file to reference
sRef.putFile(filePath)
.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
String imageName = imageNameInput.getText().toString().trim();
String imageDownloadURL = taskSnapshot.getDownloadUrl().toString();
//creating the upload object to store uploaded image details
PictureUpload upload = new PictureUpload(imageName, imageDownloadURL);
//adding an upload to firebase database
pictureDataRef.child(imageName).setValue(imageDownloadURL);
string_immo_image_url = imageDownloadURL;
//dismissing the progress dialog
progressDialog.dismiss();
changeFragment();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
progressDialog.dismiss();
Toast.makeText(getContext(), exception.getMessage(), Toast.LENGTH_LONG).show();
}
})
.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
#Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
//displaying the upload progress
double progress = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount();
progressDialog.setMessage(((int) progress) + "% wurden hochegeladen");
}
});
}
}else{
imageNameInput.setError("Geben Sie einen Namen ein");
imageNameInput.requestFocus();
}
}
Now I'm trying to show the pictures using Glide inside my Fragment in a ListView. Therefore I added an adapter for my ListView AttachmentList
public class AttachmentList extends ArrayAdapter <PictureUpload> {
List <PictureUpload> pictureUploads;
DatabaseReference pictureDatabase;
FirebaseUser user;
String userid;
private Activity context;
// Constructor
public AttachmentList (Activity context, List<PictureUpload> pictureUploads){
super (context, R.layout.layout_expose_list, pictureUploads);
this.context = context;
this.pictureUploads = pictureUploads;
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
// inflate the custom layout for the listitems
LayoutInflater inflater= context.getLayoutInflater();
final View listViewItem = inflater.inflate(R.layout.layout_expose_list, null, true);
// get the data item for this position
PictureUpload pictureUpload = pictureUploads.get(position);
user = FirebaseAuth.getInstance().getCurrentUser();
userid = user.getUid();
// get references to the view elements in the layout for populating the data
TextView textViewTitle = listViewItem.findViewById(R.id.imageNameDisplay);
ImageView attachmentImage = listViewItem.findViewById(R.id.attachmentImage);
// set the most relevant information of the immo object to the textviews
textViewTitle.setText(pictureUpload.getName());
Glide.with(getContext()).load(pictureUpload.getUrl()).into(attachmentImage);
// return the listview item to render
return listViewItem;
}
}
The PictureUpload class looks like the following:
public class PictureUpload {
public String name;
public String url;
// Default constructor required for calls to
// DataSnapshot.getValue(User.class)
public PictureUpload() {
}
public PictureUpload(String name, String url) {
this.name = name;
this.url = url;
}
public String getName() {
return name;
}
public String getUrl() {
return url;
}
}
And here is the code to the Fragment where I'm trying to display the list:
package com.webgalaxie.blischke.bachelortakesix.fragments.tabfragments;
public class AttachmentTabFragment extends Fragment {
private static final String TAG = "ATTACHMENT_TAB";
FirebaseUser user;
String user_id;
Bundle bundle, newBundle;
String immoID;
// Button to add Attachments to the Expose
Button addAtachments;
private DatabaseReference immoDataRef, pictureDataRef, contactDataRef;
private StorageReference pictureStorageRef;
ListView show_all_attachments_list;
List<PictureUpload> pictureUploads;
public AttachmentTabFragment() {
// Required empty public constructor
}
// Inflate the view for the fragment based on layout XML
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_attachment_tab, container, false);
// get reference to the view elements
addAtachments = view.findViewById(R.id.addAtachments);
show_all_attachments_list = view.findViewById(R.id.show_all_attachments_list);
// get the current user
user = FirebaseAuth.getInstance().getCurrentUser();
user_id = user.getUid();
// get the expose id
bundle = getArguments();
immoID = bundle.getString("exposeID");
// set the on ClickListener to the addAttachments Button
addAtachments.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// change the fragment
Fragment addAttachmentFragment = new AddAttachmentFragment();
FragmentManager manager = getFragmentManager();
newBundle = new Bundle();
newBundle.putString("exposeID", immoID);
addAttachmentFragment.setArguments(newBundle);
manager.beginTransaction().replace(R.id.content_frame, addAttachmentFragment).addToBackStack(null).commit();
}
});
// get reference to the database and storage
immoDataRef = FirebaseDatabase.getInstance().getReference(Constants.DATABASE_PATH_IMMOBILIEN).child(user_id).child(immoID);
pictureDataRef = FirebaseDatabase.getInstance().getReference(Constants.DATABASE_PATH_UPLOADS).child(user_id).child(immoID);
contactDataRef = FirebaseDatabase.getInstance().getReference(Constants.DATABASE_PATH_CONTACTS).child(user_id).child(immoID);
pictureStorageRef = FirebaseStorage.getInstance().getReference(user_id).child(Constants.STORAGE_PATH_UPLOADS);
//return the view
return view;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.showexposemenu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
final FragmentManager manager = getFragmentManager();
// get the expose id
bundle = getArguments();
immoID = bundle.getString("exposeID");
switch (item.getItemId()) {
case R.id.edit_expose:
Toast.makeText(getContext(), "Expose bearbeiten geklickt.", Toast.LENGTH_SHORT).show();
// put the immoID into new Bundle
newBundle = new Bundle();
newBundle.putString("exposeID", immoID);
// get a new instance of editExposeFragment
Fragment editExpose = new EditExposeFragment();
// set the newBundle as Arguments to the fragement
editExpose.setArguments(newBundle);
// switch the fragment
manager.beginTransaction().replace(R.id.content_frame, editExpose).commit();
break;
case R.id.delete_expose:
Toast.makeText(getContext(), "Expose wurde gelöscht.", Toast.LENGTH_SHORT).show();
immoDataRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
immoDataRef.removeValue();
pictureDataRef.removeValue();
contactDataRef.removeValue();
Fragment showAllExpose = new ShowAllExposeFragment();
manager.beginTransaction().replace(R.id.content_frame, showAllExpose).commit();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
break;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onStart() {
super.onStart();
// attaching the ValueEventListener
pictureDataRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// check if there are values in the database
if (dataSnapshot.getValue() != null) {
// clear the list of immos
pictureUploads.clear();
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
// getting the immo
PictureUpload pictureUpload = postSnapshot.getValue(PictureUpload.class);
// adding the immo to the list
pictureUploads.add(pictureUpload);
}
// creating the List Adapter and add him to the Listview
final AttachmentList attachmentAdapter = new AttachmentList((Activity) getContext(), pictureUploads);
show_all_attachments_list.setAdapter(attachmentAdapter);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
when I try to run my app on my device I'm always getting the following error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.webgalaxie.blischke.bachelortakesix, PID: 14901
java.lang.NullPointerException: Attempt to invoke interface method 'void java.util.List.clear()' on a null object reference
at com.webgalaxie.blischke.bachelortakesix.fragments.tabfragments.AttachmentTabFragment$3.onDataChange(AttachmentTabFragment.java:191)
at com.google.android.gms.internal.zzegf.zza(Unknown Source)
at com.google.android.gms.internal.zzeia.zzbyc(Unknown Source)
at com.google.android.gms.internal.zzeig.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6682)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
Does anyone have an idea why this happens. I'm a bit stuck on this issue.
Thank you very much for your help. If you need more information on the project do not hesitate to ask.
I also have the code to the project on GitHub if you need more information.
Link to GitHub: https://github.com/BexxBl/BachelorTakeSix
That's because in your onStart() method you're calling clear() on a list that hasn't been initialized yet. You should initialize it instead of clearing it:
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// check if there are values in the database
if (dataSnapshot.getValue() != null) {
// clear the list of immos
pictureUploads = new ArrayList();
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
// getting the immo
PictureUpload pictureUpload = postSnapshot.getValue(PictureUpload.class);
// adding the immo to the list
pictureUploads.add(pictureUpload);
}
// creating the List Adapter and add him to the Listview
final AttachmentList attachmentAdapter = new AttachmentList((Activity) getContext(), pictureUploads);
show_all_attachments_list.setAdapter(attachmentAdapter);
}
}
EDIT: The value of your snapshot is a String. You need to get the key as well and pass it to your PictureUpload class.
String val = postSnapshot.getValue(String.class);
PictureUpload pictureUpload = new PictureUpload(postSnapshot.getKey(), val);
pictureUploads.add(pictureUpload);
I am working on my first app and am just setting all the frame work up.
That is user sign up via Google, Email, Facebook and saving the data to Firebase.
I started using Realtime Database, which worked fine, but for the proceeding of my project, I think FireStore Cloud would be better suited.
I didnt have much data yet, so it was easy to get it set up.
User signs up or logs in, and if he doesnt exist already, a profile is set up based on the FirebaseAuth Name + Email and some variables I defined ("Nickname", "-"), and a few more.
All good so far. The information is fetched and displayed once the user clicks on his profile.
Then there is the OPTION TO EDIT some data, like the nickname, the age and the nationality.
If I update the data directly on firestore and click on profile again, it displays correctly.
BUT if the user enters the information and clicks the button that triggers the update to the firestore cloud, the app crashes. The database, however, also updates correctly...
I tried a lot things, but Im stuck! Thanks a lot for your help!
MY CODE
USER CLASS => where the information is stored to the cloud one time, when the user logs in
public class User extends AppCompatActivity {
public static final String AGE = "Age";
public static final String EMAIL = "Email";
public static final String FULLNAME = "Full name";
public static final String NATIONALITY = "Nationality";
public static final String NICKNAME = "Nickname";
public static final String STATUS = "Status";
private String userEmail = FirebaseAuth.getInstance().getCurrentUser().getEmail();
private String userFullName = FirebaseAuth.getInstance().getCurrentUser().getDisplayName();
public User() {
// Default constructor required for calls to DataSnapshot.getValue(User.class)
}
protected void checkFireStoreDatabase() {
// Create a new user with a first and last name
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference usersDocRef = db.collection("Users").document(userFullName);
if (usersDocRef != null) {
} else {
createNewEntry();
}
}
public void createNewEntry() {
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference usersDocRef = db.collection("Users").document(userFullName);
Map<String, Object> userEntry;
userEntry = new HashMap<>();
userEntry.put("Full name", userFullName);
userEntry.put(EMAIL, userEmail);
userEntry.put("Nickname", "-");
userEntry.put("Age", "-");
userEntry.put("Nationality", "-");
userEntry.put("Status", "Baby monkey");
db.document(userFullName).set(userEntry, SetOptions.merge()).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "Document has been saved");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "Document could not be saved");
}
});
}
USER PROFILE FRAGMENT => where the user can see his information that is stored in the cloud
public class UserProfileFragment extends Fragment implements View.OnClickListener {
private Button btnEditProfile;
//get firestore database data
private FirebaseFirestore db = FirebaseFirestore.getInstance();
private DocumentReference usersDocRef = db.collection("Users").document(FirebaseAuth.getInstance().getCurrentUser().getDisplayName());
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//DATA FROM FIRESTORE
displayFirestoreData();
btnEditProfile = (Button) view.findViewById(R.id.edit_user_info);
btnEditProfile.setOnClickListener(this);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_user_profile, container, false);
}
#Override
public void onClick(View v) {
Fragment fragment = null;
//if the button representing the "train now or create workout" fragment is clicked, create this fragment
if (v.getId() == R.id.edit_user_info) {
fragment = new EditUserProfileFragment();
}
if (fragment != null) {
getActivity().getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();
}
}
public void displayFirestoreData() {
if (usersDocRef != null) {
}
//this.getActivity makes sure the listener only works when in this FragmentActivity
usersDocRef.addSnapshotListener(this.getActivity(), new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) {
if (documentSnapshot.exists()) {
String name = documentSnapshot.getString(FULLNAME);
String email = documentSnapshot.getString(EMAIL);
String nickname = documentSnapshot.getString(NICKNAME);
String age = documentSnapshot.getString(AGE);
String nationality = documentSnapshot.getString(NATIONALITY);
String status = documentSnapshot.getString(STATUS);
//setting all the text views in the user profile
TextView txtProfileName = (TextView) getView().findViewById(R.id.profile_section_fullname);
txtProfileName.setText(name);
TextView txtProfileEmail = (TextView) getView().findViewById(R.id.profile_section_email);
txtProfileEmail.setText(email);
TextView txtProfileNickname = (TextView) getView().findViewById(R.id.profile_section_nickname);
txtProfileNickname.setText(nickname);
TextView txtProfileAge = (TextView) getView().findViewById(R.id.profile_section_age);
txtProfileAge.setText(age);
TextView txtProfileNationality = (TextView) getView().findViewById(R.id.profile_section_nationality);
txtProfileNationality.setText(nationality);
TextView txtProfileStatus = (TextView) getView().findViewById(R.id.profile_section_status);
txtProfileStatus.setText(status);
} else if (e != null) {
Log.w(TAG, "An exception occured", e);
}
}
});
}
EDIT USER PROFILE FRAGMENT => where the user can enter a new nickname, age or nationality
public class EditUserProfileFragment extends Fragment implements View.OnClickListener {
private Button btnSaveProfile;
private EditText editUsername;
private EditText editAge;
private EditText editNationality;
private String username_input;
private String age_input;
private String nationality_input;
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//Button to save the profile
btnSaveProfile = (Button) view.findViewById(R.id.save_user_info);
btnSaveProfile.setOnClickListener(this);
//field that allows changes on the nick name
editUsername = (EditText) view.findViewById(R.id.profile_section_edit_nickname);
//field that allows you to enter the correct age
editAge = (EditText) view.findViewById(R.id.profile_section_edit_age);
//field that allows you to enter your nationality
editNationality = (EditText) view.findViewById(R.id.profile_section_edit_nationality);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_user_profile_edit, container, false);
}
#Override
public void onClick(View v) {
username_input= editUsername.getText().toString().trim();
age_input = editAge.getText().toString().trim();
nationality_input = editNationality.getText().toString().trim();
//update Firestore data
updateFireStoreData(username_input, age_input, nationality_input);
}
//update the user entered information to the database, if the strings arent empty
public void updateFireStoreData(String nicknameUpdate, String ageUpdate, String nationalityUpdate) {
FirebaseFirestore db = FirebaseFirestore.getInstance();
FirebaseUser currUser = FirebaseAuth.getInstance().getCurrentUser();
DocumentReference userDocRef = db.collection("Users").document(currUser.getDisplayName());
if (!nicknameUpdate.matches("")) {
Map<String, Object> dataUpdate = new HashMap<String, Object>();
dataUpdate.put(NICKNAME, nicknameUpdate);
userDocRef
.set(dataUpdate, SetOptions.merge()).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "Document has been saved");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "Document could not be saved");
}
});
}
}
ERROR LOG:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: MYAPP, PID: 3992
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)'
on a null object reference
at MYAPP.UserProfileFragment$1.onEvent(UserProfileFragment.java:103)
at MYAPP.UserProfileFragment$1.onEvent(UserProfileFragment.java:91)
at com.google.firebase.firestore.DocumentReference.zza(Unknown Source:45)
at com.google.firebase.firestore.zzd.onEvent(Unknown Source:6)
at com.google.android.gms.internal.zzevc.zza(Unknown Source:6)
at com.google.android.gms.internal.zzevd.run(Unknown Source:6)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:251)
at android.app.ActivityThread.main(ActivityThread.java:6563)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
This is not a Firebase database Exception, either a Cloud Firestore Exception. Your Exception tells you clearly what is going on. So you are attempting to use findViewById() method on a null object reference. This means that getView() returns null. And this is happening because you are calling that method after you are returning the fragmnet view.
In order to solve this, call those methods before and use findViewById() method directly on the view.
I'm working on Activity to show all the others app users except my user and i'm using Java android with Firebase .
But i cant find anyway to exclude item from DatabaseReference or from FirebaseRecyclerAdapter .
My Activity look like this
My Database look like this :
My code inside this Activity looks like below :
#Override
public void onStart() {
super.onStart();
database = FirebaseDatabase.getInstance();
myRef = database.getReference().child("users");
FirebaseRecyclerAdapter<Users,RecyclerViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Users, RecyclerViewHolder>(Users.class , R.layout.users_single_layout ,RecyclerViewHolder.class,myRef ) {
#Override
protected void populateViewHolder(RecyclerViewHolder viewHolder, Users model, int position) {
viewHolder.setDisolayName(model.getDisplay_name());
viewHolder.setStatus(model.getStatus());
viewHolder.setImage(model.getImager_thumb(), getContext());
frinds_relative.setVisibility(View.INVISIBLE);
final String users_id = getRef(position).getKey();
viewHolder.mview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent target = new Intent(getActivity() , user_profile.class);
target.putExtra("profile_id" , users_id) ;
startActivity(target);
}
});
}
};
users_recycler.setAdapter(firebaseRecyclerAdapter);
}
I will be thankful if you have the solution .
I faced something similar once and how i did it was..
First thing is to give the parent layout and id in the users_single_layout xml file;
Reference the parent layout in the viewholder;
Then hide all the content of file by :
android:visibility="gone"
When That is done, You set and if statement in your FirebaseRecyclerAdapter:
Check the Code Below:
users_recycler = new FirebaseRecyclerAdapter<Users,RecyclerViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Users, RecyclerViewHolder>(Users.class , R.layout.users_single_layout ,RecyclerViewHolder.class,myRef ) {
#Override
protected void populateViewHolder(RecyclerViewHolder viewHolder, Users model, int position) {
if (adapter.getRef(position).getKey.equals(currentUid){
viewholder.thelayout.setVisibility(View.GONE);
} else {
viewHolder.setDisolayName(model.getDisplay_name());
viewHolder.setStatus(model.getStatus());
viewHolder.setImage(model.getImager_thumb(), getContext());
frinds_relative.setVisibility(View.INVISIBLE);
final String users_id = getRef(position).getKey();
viewHolder.mview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent target = new Intent(getActivity() , user_profile.class);
target.putExtra("profile_id" , users_id) ;
startActivity(target);
}
});
}
}
};
users_recycler.setAdapter(firebaseRecyclerAdapter);
The FirebaseUI FirebaseRecyclerAdapter is a direct representation of the data in the underlying Query or exists at a location in a Firebase database. To exclude an item from an adapter, you have to exclude it first from the Firebase location or ensure it doesn't match that particular query anymore.
To achieve this, you can use the following line of code:
yourAdapter.getRef(position).removeValue();