1

Aim

Allow Users to send reports to the authorities. Reports will be stored and will not be overwritten when new Reports are entered by the same user

Picture of Current Database

enter image description here

The current user signed in is able to store "Incident Report" into the Database but apparently when the user submits another "Incident Report", the newly submitted "Incident Report" will overwrite the data of the previous "Incident Report"

Picture of Desired Database

enter image description here

The Yellow Highlighted data is supposed to be data retrieved from the "Users" child "name" which is the Red Circled data

For example, if the data within the Red Circled data is "Abe Long", the data of the Yellow Highlighted will be "Abe Long" as well.

In addition to that, when the current signed in User submits an "Incident Report", each new one created will create a new Unique Key. This means it will not overwrite the previously submitted data.

Report Fragment Class

import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;

import java.util.Date;
import java.util.HashMap;


public class ReportFragment extends Fragment {

    private EditText jReportDatePick;
    private EditText jReportTimeEnt;
    private EditText jReportLocationEnt;
    private EditText jReportDescriptionEnt;

    private Button jReportSendBtn;

    private FirebaseUser jReportCurrentUserID;

    private DatabaseReference jReportByUserDatabase;

    private ProgressDialog jReportLoad;

    public ReportFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View viewRoot = inflater.inflate(R.layout.fragment_report, container, false);

        jReportDatePick = viewRoot.findViewById(R.id.reportDatePick);
        jReportTimeEnt = viewRoot.findViewById(R.id.reportTimeEnt);
        jReportLocationEnt = viewRoot.findViewById(R.id.reportLocationEnt);
        jReportDescriptionEnt = viewRoot.findViewById(R.id.reportDescriptionEnt);

        jReportSendBtn = viewRoot.findViewById(R.id.reportSendBtn);

        jReportLoad = new ProgressDialog(getActivity());

        jReportSendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String userReportDate = jReportDatePick.getText().toString();
                String userReportTime = jReportTimeEnt.getText().toString();
                String userReportLocation = jReportLocationEnt.getText().toString();
                String userReportDescription = jReportDescriptionEnt.getText().toString();

                if(!TextUtils.isEmpty(userReportDate)&&
                        !TextUtils.isEmpty(userReportTime)&&
                        !TextUtils.isEmpty(userReportLocation)&&
                        !TextUtils.isEmpty(userReportDescription)){
                    submitReport(userReportDate, userReportTime, userReportLocation, userReportDescription);
                    jReportLoad.setTitle("Sending Report");
                    jReportLoad.setMessage("Please wait while the report is being sent");
                    jReportLoad.setCanceledOnTouchOutside(false);
                    jReportLoad.show();
                }else{
                    jReportLoad.dismiss();
                    Toast.makeText(getActivity(), "Report failed to be sent due to empty inputs", Toast.LENGTH_SHORT).show();
                }

            }
        });

        return  viewRoot;
    }

    private void submitReport(final String userReportDate,final String userReportTime,
                              final String userReportLocation,final String userReportDescription) {

        jReportCurrentUserID = FirebaseAuth.getInstance().getCurrentUser();
        final String reportUserID = jReportCurrentUserID.getUid();
        jReportByUserDatabase = FirebaseDatabase.getInstance().getReference().child("Incident Reports").child(reportUserID);

        HashMap<String, String> incidentReportUser = new HashMap<>();
        incidentReportUser.put("date", userReportDate);
        incidentReportUser.put("time", userReportTime);
        incidentReportUser.put("location", userReportLocation);
        incidentReportUser.put("description", userReportDescription);

        jReportByUserDatabase.setValue(incidentReportUser).addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                if(task.isSuccessful()){
                    jReportLoad.dismiss();
                    Toast.makeText(getActivity(), "Report was Sent", Toast.LENGTH_SHORT).show();
                    jReportDatePick.setText("");
                    jReportTimeEnt.setText("");
                    jReportLocationEnt.setText("");
                    jReportDescriptionEnt.setText("");
                }else{
                    jReportLoad.dismiss();
                    Toast.makeText(getActivity(), "Report failed to be sent", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}

Additional Comments

  • How the Report Fragment works

I do not intend for the currently signed in User to be able to submit their name, that will create a possibility of the User entering someone else's name into the "Incident Report". I intend for the "CurrentUser signed in" name to be retrieved and entered into the "Incident Report" automatically.

  • How the Report Fragment is coded

Instead of putting jReportByUserDatabase.setValue(incidentReportUser) I have tried putting .updateChildren while using Map<String, Object>. The problem was that each data was not entered under a "Unique ID" which will be stored under "Incident Report" within the Data Tree


Solution to the Problem

Helped by both Cadet and JavaBanana

Implementing a "For Loop DataSnapshot" to loop through the data and retrieving the desired one by also using the "Users class" which contains the name, address, status, etc.

private void submitReport(final String userReportDate,final String userReportTime,
                          final String userReportLocation,final String userReportDescription) {

DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference();
dbRef.child("Users").addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
            Users user = snapshot.getValue(Users.class);
            HashMap<String, String> incidentReportUser = new HashMap<>();
            incidentReportUser.put("name", user.name);
            incidentReportUser.put("date", userReportDate);
            incidentReportUser.put("time", userReportTime);
            incidentReportUser.put("location", userReportLocation);
            incidentReportUser.put("description", userReportDescription);

            jReportCurrentUserID = FirebaseAuth.getInstance().getCurrentUser();
            final String reportUserID = jReportCurrentUserID.getUid();
            jReportByUserDatabase = FirebaseDatabase.getInstance().getReference().child("Incident Reports").child(reportUserID);
            jReportByUserDatabase.push().setValue(incidentReportUser).addOnCompleteListener(new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    if(task.isSuccessful()){
                        jReportLoad.dismiss();
                        Toast.makeText(getActivity(), "Report was Sent", Toast.LENGTH_SHORT).show();
                        jReportDatePick.setText("");
                        jReportTimeEnt.setText("");
                        jReportLocationEnt.setText("");
                        jReportDescriptionEnt.setText("");
                    }else{
                        jReportLoad.dismiss();
                        Toast.makeText(getActivity(), "Report failed to be sent", Toast.LENGTH_SHORT).show();
                    }
                }
            });
        }
    }
    @Override
    public void onCancelled(DatabaseError databaseError) {
    }
});
}
The Employee 123
  • 494
  • 1
  • 14
  • 27

2 Answers2

2

Write it by this way (Push adds values, instead of overriding).

jReportByUserDatabase.push()setValue(incidentReportUser)

Also, I recommend to make the Report node containing the user fields also. Like combine them to flat node.

enter image description here

Cadet
  • 376
  • 1
  • 9
  • OMG THANK YOU SO MUCH!!! A `.push()` helped solve 90% of the Problem!! I never thought of it!! but is there a way for me get the "name" of the current user? Please help me with that =D – The Employee 123 Sep 25 '17 at 12:45
  • What is definition of "current" user? One made the latest report? – Cadet Sep 25 '17 at 12:51
  • Yes!!! For example, "John Smith" is the currentUser signed into the app. "John Smith" will be able to send as many reports as he wishes. But all I would like is to retrieve his "name" from the **Red Circled** data as shown in the **Picture of Desired Database** – The Employee 123 Sep 25 '17 at 12:57
  • Hmmm... I understand. This question involves also logic changes, not only Firebase usage. What I can recommend, is to make the Report node containing the user fields also. Like combine them to flat node(I'll try to add snapshot to my previous answer -I can't do it in the comments). Probably you have 2 separated classes in your code (User and report), so add parameter "User" to constructor of report, read the fields and make then flat in report (call them like userAddess, userName etc.) It will save you a lot of issues in the future, and reduce the complexity of working with Firebase. – Cadet Sep 25 '17 at 13:06
  • Thank you for your suggestion Cadet, but if I were to add them into together as one within the Database, as shown in the screenshot, that would mean that as soon as a user registers into the app, they would already be sending an Incident Report. What if the user decides not to send a report because everything in their area is fine? I have tried acquiring selected data from a single node and it works. But I wouldn't want the users to send a report as soon as they register. – The Employee 123 Sep 25 '17 at 13:40
  • I am currently trying to use dataSnapshot to acquire the "name" of the currentUser and prompting it into the `HashMap`. – The Employee 123 Sep 25 '17 at 13:45
  • Can I create another question so as to mark your answer about preventing old data from overwritten as the answer? Currently, both you and Java Banana helped me answer my question. My problem about preventing the old data from being overwritten. Thank you for your help – The Employee 123 Sep 25 '17 at 14:22
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/155253/discussion-between-cadet-and-thestudent123). – Cadet Sep 25 '17 at 14:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/155255/discussion-between-cadet-and-thestudent123). – Cadet Sep 25 '17 at 14:52
1

Okay so the main problem with your designs that you are storing the reports under the user's Id which firebase will overwrite obviously.

You can generate a random key for storing your reports and set your report at the generated key in the database.

public class Report {
  String reportId; // This is important
  String reporterId;  //This is important
  String reporterName;
  String userType;
  String status;
  String address;
  String neibourhood;
  String image;
}

DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference("Incident Reports");
String reportId = dbRef.push().getKey(); //This generates a new key and returns it
Report report = new Report("report id", "reporter id", "other stuff"... "etc");
dbRef.child(reportId).setValue(report);

To Retrieve the names of the Users

DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference();
dbRef.child("Users").addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        //Loop through the retrieved user data
        for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
            User user = snapshot.getValue(User.class);
            System.out.println(user.name);
        }
    }
    @Override
    public void onCancelled(DatabaseError databaseError) {}
});
rsanath
  • 1,154
  • 2
  • 15
  • 24
  • Thank you for your answer!! Thank you for showing me the way of generating a new key `String reportId = dbRef.push().getKey(); ` but what if I would like to retrieve the "name" from the **Red Circled ** data? – The Employee 123 Sep 25 '17 at 12:54
  • The users name from the 'Users' node or from the 'Incident Report' node ? – rsanath Sep 25 '17 at 12:58
  • The user's name from the "Users" node. The **Picture of Current Database** is the one that I currently have, the **Picture of Desired Database** is the one that I would like to generate. The user's "name" is not entered by the current User but it is retrieved from current user when he/she submits an "Incident Report" – The Employee 123 Sep 25 '17 at 13:01
  • JavaBanana!!! Thank you for your answer. I have placed the `HashMap` into the DataSnapshot to save both the data and retrieved data. Have a Nice Day. – The Employee 123 Sep 25 '17 at 14:21
  • Hello JavaBanana, can you please help me answer this question at https://stackoverflow.com/questions/46471541/android-firebase-failed-to-convert-value-of-type-java-util-hashmap-to-string =D – The Employee 123 Sep 28 '17 at 19:11