ChildEventListener not populating listview properly - android

I am trying to work with ChildEventListener in Firebase to populate the listView with the "title" field of every child nodes. But it's not working properly. My first activity is AuthActivity, which contains googleSignIn Button, by which the user can log in or authenticate. After that, in MainActivity a listView supposes to populate with the data using ChildEventListener datasnapshot. But it remains blank.
The interesting part is, If I go back to the AuthActivity with the back button pressed and again click the login button, it agains taking me to the MainActivity and somehow this time it's shows the listView with the correct data. And after closing the app, the same error happens. I am not getting any errors in the Logcat.
Here is my AuthActivity File
public class AuthActivity extends AppCompatActivity {
public static final int RC_SIGN_IN = 100;
SignInButton signInButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_auth);
signInButton = findViewById(R.id.signInButton);
signInButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Choose authentication providers
List<AuthUI.IdpConfig> providers = Arrays.asList(
new AuthUI.IdpConfig.GoogleBuilder().build());
// Create and launch sign-in intent
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.build(),
RC_SIGN_IN);
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
IdpResponse response = IdpResponse.fromResultIntent(data);
if (resultCode == RESULT_OK) {
Intent intent = new Intent(AuthActivity.this, MainActivity.class);
startActivity(intent);
} else {
Toast.makeText(this, "Login Failed! Please try again.", Toast.LENGTH_LONG).show();
}
}
}
}
And The MainActivity File
public class MainActivity extends AppCompatActivity {
public static final int RC_SIGN_IN = 100;
ListView listView;
ArrayList<String> titlesArray;
FirebaseDatabase database;
DatabaseReference databaseReference;
FirebaseUser user;
ChildEventListener childEventListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.listView);
titlesArray = new ArrayList();
//Get the associated login user info
user = FirebaseAuth.getInstance().getCurrentUser();
//Write a message to the database
database = FirebaseDatabase.getInstance();
databaseReference = database.getReference().child("notes").child(user.getUid());
//Adding adapter to the list of titles
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, titlesArray);
listView.setAdapter(adapter);
childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Note notes = dataSnapshot.getValue(Note.class);
titlesArray.add(notes.getTitle());
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(MainActivity.this, "Loading failed!", Toast.LENGTH_SHORT).show();
}
};
databaseReference.addChildEventListener(childEventListener);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.sign_out_menu:
AuthUI.getInstance()
.signOut(this);
return true;
case R.id.add_new:
Intent intent = new Intent(MainActivity.this, AddActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

It is because you are not notifying adapter after getting data from firebase callback.
Just replace your listener like below.
childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Note notes = dataSnapshot.getValue(Note.class);
titlesArray.add(notes.getTitle());
adapter.notifyDataSetChanged();
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(MainActivity.this, "Loading failed!", Toast.LENGTH_SHORT).show();
}
};

Related

Is there a way to reference a variable outside onCreate method?

I can't seem to reference a variable outside the onCreate method. Is there a way to overcome this? I'm a student and still learning. If anyone can help, that would be great. Thank you. I would like to reference the "url" variable to the "onItemClick" method, it says "Cannot resolve symbol".
Here is my code :
public class CominSoonActivity extends AppCompatActivity implements RecyclerViewClickItemInterface {
DatabaseReference reference;
RecyclerView recyclerView;
ArrayList<Event> list;
MyAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comin_soon);
recyclerView = findViewById(R.id.myRecycler);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
list = new ArrayList<Event>();
adapter = new MyAdapter(CominSoonActivity.this, list, this);
recyclerView.setAdapter(adapter);
reference = FirebaseDatabase.getInstance().getReference().child("EventDisplay");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot dataSnapshot1: dataSnapshot.getChildren())
{
Event e = dataSnapshot1.getValue(Event.class);
String url = (String) dataSnapshot1.child("website").getValue();
list.add(e);
adapter.notifyDataSetChanged();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(CominSoonActivity.this, "Opps.... Something went wrong", Toast.LENGTH_SHORT).show();
}
});
//add back button
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
}
#Override
public void onItemClick(int position) {
//Uri uri = Uri.parse("http://fareezdanial19.wixsite.com/merlimaulibrary");
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
//taking the user back to the previous activity
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == android.R.id.home) {
Intent homeActivity = new Intent(getApplicationContext(), HomeActivity.class);
startActivity(homeActivity);
finish();
}
return super.onOptionsItemSelected(item);
}
}
You can do it like this,
public class CominSoonActivity extends AppCompatActivity implements RecyclerViewClickItemInterface {
DatabaseReference reference;
RecyclerView recyclerView;
ArrayList<Event> list;
MyAdapter adapter;
String url;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comin_soon);
recyclerView = findViewById(R.id.myRecycler);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
list = new ArrayList<Event>();
adapter = new MyAdapter(CominSoonActivity.this, list, this);
recyclerView.setAdapter(adapter);
reference = FirebaseDatabase.getInstance().getReference().child("EventDisplay");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot dataSnapshot1: dataSnapshot.getChildren())
{
Event e = dataSnapshot1.getValue(Event.class);
url = (String) dataSnapshot1.child("website").getValue();
list.add(e);
adapter.notifyDataSetChanged();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(CominSoonActivity.this, "Opps.... Something went wrong", Toast.LENGTH_SHORT).show();
}
});
//add back button
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
}
#Override
public void onItemClick(int position) {
//Uri uri = Uri.parse("http://fareezdanial19.wixsite.com/merlimaulibrary");
if(!url.isEmpty()){
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
}
//taking the user back to the previous activity
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == android.R.id.home) {
Intent homeActivity = new Intent(getApplicationContext(), HomeActivity.class);
startActivity(homeActivity);
finish();
}
return super.onOptionsItemSelected(item);
}
}

DataSnapShot Object's value returning null on getvalue(Boolean.class)

I'm doing a realtime tracking application from a tutorial online, Here i'm setting the presence system using firebase. But it's crashing with:
/java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Boolean.booleanValue()' on a null object reference
I don't understand what's wrong the guy who coded this has it working perfectly.
The exception is happening at this line :if(dataSnapshot.getValue(Boolean.class)){
When i log this on screen the datasnapshot object has a key but no value
HELP!
ListOnline Class
//firebase
DatabaseReference onlineRef,currentUserRef,counterRef;
FirebaseRecyclerAdapter<User,ListOnlineViewHolder> adapter;
//View
RecyclerView listOnline;
RecyclerView.LayoutManager layoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_online);
//setting the recyclerview
listOnline = (RecyclerView)findViewById(R.id.listOnlineRecyclerview);
listOnline.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(this);
listOnline.setLayoutManager(layoutManager);
//set toolbar and menu / join,logout
Toolbar toolbar = (Toolbar)findViewById(R.id.toolbarID);
toolbar.setTitle("Presence System");
setSupportActionBar(toolbar);
//firebase
onlineRef = FirebaseDatabase.getInstance().getReference().child("info/connected");
counterRef = FirebaseDatabase.getInstance().getReference("lastOnline"); //create new child name lastOnline
currentUserRef = FirebaseDatabase.getInstance().getReference().child(FirebaseAuth.getInstance().getCurrentUser().getUid());
setupSystem();
//after setup we load all users and display in recyclerview
//this is online list
updateList();
}
private void updateList() {
adapter = new FirebaseRecyclerAdapter<User, ListOnlineViewHolder>(
User.class,R.layout.user_layout,ListOnlineViewHolder.class,counterRef
) {
#Override
protected void populateViewHolder(ListOnlineViewHolder viewHolder, User model, int position) {
viewHolder.emailTextView.setText(model.getEmail());
}
};
adapter.notifyDataSetChanged();
listOnline.setAdapter(adapter);
}
private void setupSystem() {
onlineRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.getValue(Boolean.class)){
currentUserRef.onDisconnect().removeValue();
//set online user in list
counterRef.child(FirebaseAuth.getInstance().getCurrentUser().getUid())
.setValue(FirebaseAuth.getInstance().getCurrentUser().getEmail(),"Online");
adapter.notifyDataSetChanged();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
counterRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot postSnapshot:dataSnapshot.getChildren()){
User user = postSnapshot.getValue(User.class);
Log.d("LOG",""+user.getEmail()+"is "+user.getStatus());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.main_menu,menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.action_join:
counterRef.child(FirebaseAuth.getInstance().getCurrentUser().getUid())
.setValue(FirebaseAuth.getInstance().getCurrentUser().getEmail(),"Online");
break;
case R.id.action_logout:
currentUserRef.removeValue();
}
return super.onOptionsItemSelected(item);
}
}
User Class
public class User {
private String email,status;
public User(String email, String status) {
this.email = email;
this.status = status;
}
public User() {
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}}
MainActivity
public class MainActivity extends AppCompatActivity {
Button signInButton;
private final static int LOGIN_PERMISSION = 1000;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
signInButton = (Button) findViewById(R.id.signInButton);
signInButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivityForResult(AuthUI.getInstance().createSignInIntentBuilder().setAllowNewEmailAccounts(true).build(),LOGIN_PERMISSION);
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == LOGIN_PERMISSION){
startNewActivity(resultCode,data);
}
}
private void startNewActivity(int resultcode, Intent data) {
if(resultcode == RESULT_OK){
Intent intent = new Intent(MainActivity.this,ListOnline.class);
startActivity(intent);
finish();
}
else{
Toast.makeText(this,"login failed!!",Toast.LENGTH_SHORT).show();
}
}}
In your setupSystem() method, you are attaching a listener to onlineRef (the info/connected node) and then marshalling the returned value to a Boolean value.
However, DataSnapshot#getValue() will return null if there is no data at the specified location in the database. If this happens, the dataSnapshot.getValue(Boolean.class) call will create a Boolean variable with the value of null, which then cannot be checked for a true value in your current if statement (see Check if null Boolean is true results in exception).
You could check that getValue() does not return null first by adding a null-check to your if statement:
if(dataSnapshot.getValue() != null && dataSnapshot.getValue(Boolean.class)){
// ...
}
Or check that the location exists using DataSnapshot#exists():
if(dataSnapshot.exists() && dataSnapshot.getValue(Boolean.class)){
// ...
}
However, if you're trying to detect connection state, did you mean to attach the listener to the .info/connected node instead? As from the documentation:
For many presence-related features, it is useful for your app to know
when it is online or offline. Firebase Realtime Database provides a
special location at /.info/connected which is updated every time the
Firebase Realtime Database client's connection state changes. Here is
an example:
DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
boolean connected = snapshot.getValue(Boolean.class);
if (connected) {
System.out.println("connected");
} else {
System.out.println("not connected");
}
}
#Override
public void onCancelled(DatabaseError error) {
System.err.println("Listener was cancelled");
}
});
its null since it does not exist in the database..
onlineRef = FirebaseDatabase.getInstance().getReference().child("info/connected");
you are querying on the above location. So dataSnapshot is a snapshot of the above..
if(dataSnapshot.getValue(Boolean.class)){
This does not exist in the database.. Thus you get that error
It seems that you do not have a value in database. This will handle the error
if(dataSnapshot.getValue(Boolean.class) != null && dataSnapshot.getValue(Boolean.class)){

Firebase database listview open another activity

I am using firebase database where I am saving data manually and retrieving data in the app and that is working well but I want that when user clicks the listview item that must open another activity and within that must retrieve another table.
Here is below my code
private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthListener;
private ProgressDialog progressDialog;
DatabaseReference mref =
FirebaseDatabase.getInstance().getReference("users");
ListView mlistview;
ArrayList<String> arrayList=new ArrayList<>();
ArrayAdapter<String> arrayAdapter;
private BottomNavigationView.OnNavigationItemSelectedListener
mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
progressDialog.show();
startActivity(new
Intent(profilemain.this,profilemain.class));
return true;
case R.id.navigation_dashboard:
Toast.makeText(profilemain.this,"hi
hello",Toast.LENGTH_SHORT).show();
return true;
case R.id.navigation_notifications:
Toast.makeText(profilemain.this,"hi
hello",Toast.LENGTH_SHORT).show();
return true;
case R.id.navigation_signout:
Toast.makeText(profilemain.this,"You have successfully
Signed out",Toast.LENGTH_SHORT).show();
mAuth.signOut();
return true;
}
return false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profilemain);
progressDialog.setMessage("loading");
progressDialog.setTitle("database is");
mlistview=(ListView) findViewById(R.id.listview);
Firebase.setAndroidContext(this);
DatabaseReference ref = FirebaseDatabase.getInstance()
.getReference("users");
//mref=new Firebase("https://stark-1dffd.firebaseio.com/users");
arrayAdapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,arrayList);
mlistview.setAdapter(arrayAdapter);
mref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String value= dataSnapshot.getValue(String.class);
arrayList.add(value);
arrayAdapter.notifyDataSetChanged();
//
//
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
mAuth = FirebaseAuth.getInstance();
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
if (firebaseAuth.getCurrentUser()==null){
startActivity(new
Intent(profilemain.this,MainActivity.class));
}
}
};
}
#Override
protected void onStart() {
super.onStart();
mAuth.addAuthStateListener(mAuthListener);
}
add an ItemClickListener on your ListView
mlistview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent in = new Intent(MyActivity.this, DetailActivity.class);
in.putExtra("data", arrayList.get(position);// add the selected String from the ListView
startActivity(in);
}
});
and read the data from the other activity:
String data = getIntent().getStringExtra("data");

the result of pressing button is displayed after two presses

I have an activity for searching in firebase, it contains edittext for typing the text and button when I press the button The results supposed to appear in the recyclerview but when I press the button the first time nothing appears unless I press the button for the second time
the code of findDataActivity
public class FindDataActivity extends AppCompatActivity {
DatabaseReference db;
FirebaseHelper helper;
MyAdapter adapter;
RecyclerView rv;
EditText findETxt;
Button findBtn;
ArrayList<Paints> p;
String s;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_find_data);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//SETUP RECYCLER
rv = (RecyclerView) findViewById(R.id.rvsearch);
rv.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
//INITIALIZE FIREBASE DB
db = FirebaseDatabase.getInstance().getReference("Database");
adapter = new MyAdapter(FindDataActivity.this, p);
rv.setAdapter(adapter);
findBtn = (Button) findViewById(R.id.find_btn);
helper = new FirebaseHelper(db);
}
public void search(View view) {
db.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
findETxt = (EditText) findViewById(R.id.findETxt);
s = findETxt.getText().toString();
p=helper.retrieveSearch(s);
adapter = new MyAdapter(FindDataActivity.this, p);
rv.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id=item.getItemId();
if(id == android.R.id.home){
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
}
the code of firebase
public class FirebaseHelper {
DatabaseReference db;
Boolean saved=null;
Boolean saved1=null;
ArrayList<Paints> paints=new ArrayList<>();
ArrayList<String> keys=new ArrayList<>();
public FirebaseHelper(DatabaseReference db) {
this.db = db;
}
//READ THEN RETURN ARRAYLIST FOR SEARCH ITEMS IN CASE OF SEARCHING ABOUT ITEM
public ArrayList<Paints> retrieveSearch(final String item) {
db.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
fetchDataSearch(dataSnapshot,item);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
fetchDataSearch(dataSnapshot,item);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return paints;
}
//IMPLEMENT FETCH DATA AND FILL ARRAYLIST FOR SEARCH
private void fetchDataSearch( DataSnapshot dataSnapshot,String item)
{
paints.clear();
//sorting and searching
final DatabaseReference myRef = db.child("items");
Query query = myRef.orderByChild("type").startAt(item)
.endAt(item + "\uf8ff");
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot messageSnapshot : dataSnapshot.getChildren()) {
Paints paint=messageSnapshot.getValue(Paints.class);
paints.add(paint);
//Toast.makeText(FindDataActivity.this, "found " + type + " unit: " + unit + " price " + price, Toast.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
In your MyAdapter class you must create a method like this:
public void updateAdapter(ArrayList<Paints> paint){
this.paint= paint;
notifyDataSetChanged();
}
In your search method replace adapter.notifyDataSetChanged(); for adapter.updateAdapter(p);

RecyclerView isn't showing data until refreshed

I'm using Firebase to populate my RecyclerView, but the problem is that every time I open the app, I need to swipe down to refresh and then the list of items is available.
Here is my code
public class MainActivity extends AppCompatActivity {
private static final int RC_PHOTO_PICKER = 2;
DatabaseReference db;
FirebaseHelper helper;
MyAdapter adapter;
RecyclerView rv;
EditText nameEditTxt,grpTxt,descTxt,linkTxt;
FirebaseAuth.AuthStateListener authListener;
SwipeRefreshLayout swipeRefresh;
Uri downloadUrl;
String Admin_code;
FirebaseStorage mfirebaseStorage;
private StorageReference mEventPhotoReference;
FloatingActionButton fab;
SharedPreferences sharedPreferences;
ProgressBar spinner;
static boolean calledAlready=false;
public MyAdapter adapter1;
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
spinner = (ProgressBar)findViewById(R.id.progressBar);
spinner.setVisibility(View.GONE);
mfirebaseStorage=FirebaseStorage.getInstance();
if(!calledAlready){
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
calledAlready=true;
}
swipeRefresh=(SwipeRefreshLayout)findViewById(R.id.swiperefresh);
//SETUP RECYCLER
rv = (RecyclerView) findViewById(R.id.rv);
rv.setLayoutManager(new LinearLayoutManager(this));
//INITIALIZE FIREBASE DB
db= FirebaseDatabase.getInstance().getReference();
mEventPhotoReference=mfirebaseStorage.getReference().child("Event Photos");
helper=new FirebaseHelper(db);
//ADAPTER
adapter=new MyAdapter(this,helper.retrieve());
rv.setAdapter(adapter);
adapter.notifyDataSetChanged();
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
rv.setAdapter(adapter);
swipeRefresh.setRefreshing(false);
}
});
sharedPreferences= PreferenceManager.getDefaultSharedPreferences(this);
Admin_code=sharedPreferences.getString(getString(R.string.Admin_code),getString(R.string.Admin_default_value));
Log.e("MainActivity","" + Admin_code);
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
displayInputDialog();
}
});
fab.setVisibility(View.GONE);
showBtn();
}
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
#Override
protected void onResume() {
showBtn();
super.onResume();
}
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void showBtn(){
Admin_code=sharedPreferences.getString(getString(R.string.Admin_code),getString(R.string.Admin_default_value));
if(Objects.equals(Admin_code, "28011996")){
fab.setVisibility(View.VISIBLE);
}
else
fab.setVisibility(View.GONE);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_logout) {
FirebaseAuth.getInstance().signOut();
startActivity(new Intent(MainActivity.this, LoginActivity.class));
return true;
}
if(id==R.id.settings){
Intent settingsIntent=new Intent(this,Settings.class);
startActivity(settingsIntent);
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if ((requestCode& 0xffff) == RC_PHOTO_PICKER && resultCode == RESULT_OK) {
Uri selectedImageUri = data.getData();
StorageReference photoRef=mEventPhotoReference.child(selectedImageUri.getLastPathSegment());
photoRef.putFile(selectedImageUri)
.addOnSuccessListener(this, new OnSuccessListener<UploadTask.TaskSnapshot>() {
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
// When the image has successfully uploaded, we get its download URL
downloadUrl = taskSnapshot.getDownloadUrl();
Toast.makeText(MainActivity.this,"Photo selected successfully",Toast.LENGTH_LONG).show();
}
}).addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(MainActivity.this,"there was a problem uploading photo",Toast.LENGTH_LONG).show();
}
});
}
}
//DISPLAY INPUT DIALOG
private void displayInputDialog()
{
Dialog d=new Dialog(this);
d.setTitle("Save To Firebase");
d.setContentView(R.layout.input_dialog);
nameEditTxt= (EditText) d.findViewById(R.id.nameEditText);
grpTxt= (EditText) d.findViewById(R.id.propellantEditText);
descTxt= (EditText) d.findViewById(R.id.descEditText);
Button saveBtn= (Button) d.findViewById(R.id.saveBtn);
Button photoBtn=(Button)d.findViewById(R.id.photoBtn);
linkTxt = (EditText) d.findViewById(R.id.linkEditText);
photoBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent=new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/jpeg");
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
startActivityForResult(Intent.createChooser(intent, "Complete action using"), RC_PHOTO_PICKER);
}
});
//SAVE
saveBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//GET DATA
String name=nameEditTxt.getText().toString();
String propellant=grpTxt.getText().toString();
String desc=descTxt.getText().toString();
String link=linkTxt.getText().toString();
Long tsLong = System.currentTimeMillis()/1000;
String ts = tsLong.toString();
//SET DATA
Spacecraft s=new Spacecraft();
s.setName(name);
s.setPropellant(propellant);
s.setDescription(desc);
s.setLink(link);
s.setImageUrl(downloadUrl.toString());
s.setTimestamp(ts);
//SIMPLE VALIDATION
if(name != null && name.length()>0)
{
//THEN SAVE
if(helper.save(s))
{
//IF SAVED CLEAR EDITXT
nameEditTxt.setText("");
grpTxt.setText("");
descTxt.setText("");
linkTxt.setText("");
downloadUrl=null;
adapter=new MyAdapter(MainActivity.this,helper.retrieve());
rv.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}else
{
Toast.makeText(MainActivity.this, "Name Must Not Be Empty", Toast.LENGTH_SHORT).show();
}
}
});
d.show();
}
After Searching for a while, I found out that I should use notifyDataSetChanged(); but it doesn't work.
Here is my helper class where I'm fetching data:
public class FirebaseHelper {
DatabaseReference db;
Boolean saved=null;
ArrayList<Spacecraft> spacecrafts=new ArrayList<>();
public FirebaseHelper(DatabaseReference db) {
this.db = db;
}
//WRITE IF NOT NULL
public Boolean save(Spacecraft spacecraft)
{
if(spacecraft==null)
{
saved=false;
}else
{
try
{
db.child("Spacecraft").push().setValue(spacecraft);
saved=true;
}catch (DatabaseException e)
{
e.printStackTrace();
saved=false;
}
}
return saved;
}
//IMPLEMENT FETCH DATA AND FILL ARRAYLIST
private void fetchData(DataSnapshot dataSnapshot)
{
spacecrafts.clear();
for (DataSnapshot ds : dataSnapshot.getChildren())
{
Spacecraft spacecraft=ds.getValue(Spacecraft.class);
spacecrafts.add(spacecraft);
}
}
//READ THEN RETURN ARRAYLIST
public ArrayList<Spacecraft> retrieve() {
db.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
fetchData(dataSnapshot);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
fetchData(dataSnapshot);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
fetchData(dataSnapshot);
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return spacecrafts;
}
It'll be great if someone could help me out here. This is the last problem in my project.
The problem is due to the adapter not knowing about the data that has been fetched asynchronously. Let's try to look at your code step-by-step:
adapter = new MyAdapter(this, helper.retrieve());
helper.retrieve() will initiate an asynchronous request to fetch data from firebase and immediately returns an empty ArrayList (as the data from firebase hasn't reached the app yet).
rv.setAdapter(adapter);
This line sets the adapter to recyclerView, which has an empty arraylist as data, so it won't show anything on the UI.
After the data is received from firebase, ArrayList<Spacecraft> spacecrafts is updated with new data. But the adapter is not notified about this new data. Ideally, adapter.notifyDataSetChanged() should be called after new data is put into the array list.
Hence, when you swipe to refresh after the initial call, the adapter is set to recycler view, leading to an internal call to notifyDataSetChanged(), which in turn loads the new data on to the UI.
Easiest solution is to notify the adapter when new data is received from Firebase. Below is the pseudo code for it -
Create an interface OnFirebaseDataChanged and add a method void dataChanged(); to it.
MainAcitivty should implement this interface and the implementation of that method should call adapter.notifyDataSetChanged()
helper = new FirebaseHelper(db); should be changed to helper = new FirebaseHelper(db, this);
FirebaseHelper's constructor should have a 2nd parameter of type OnFirebaseDataChanged and should save it in a field called dataChangeListener.
Add a line at the end of fetchData method in FirebaseHelper dataChangeListener.dataChanged();
The ideal solution would be to architect the code differently, but that topic is out of scope of this question.

Categories

Resources