I need to make background process in android studio, that will work when phone is turned off and will listen to the firebase realtime database and send notifications if the user data matches sent from another user firebase data.
I have a realtime database in which, when the "SOS" button is pressed, data is sent from which user the message goes and to whom.
That's how it looks like in Firebase console:
Firebase database {
Sos(all sos button clicks){
RandomID( like -N3AB5hwgkPO5kCqdaJf){
emailfrom(who clicked button)
emailto(user who will receive message)
time
}
}
}
i need to scan section "sos" all the time, and if emailto matches with my email, app will send notification.
I made the prototype with "extends service", but it doesn't even work, and does not perform any function.
public class SosListener extends Service {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String email = user.getEmail();
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
DatabaseReference dataBase = FirebaseDatabase.getInstance().getReference();
dataBase.child("Sos/").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
String emailTo = snapshot.child("emailTo").getValue(String.class);
if (emailTo == email) {
}
}
#Override
public void onChildChanged(#NonNull DataSnapshot snapshot, #Nullable String previousChildName) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot snapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot snapshot, #Nullable String previousChildName) {
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
}
Here is also MainActivity where I start this Service:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this, SosListener.class));
}
}
How can I make it?
Related
I create a function that retrieves data from realtime firebase and Displays it to the user on android studio and when I run it, it displays null.
Here's the code :
public class ProcessSuccessed extends AppCompatActivity {
ActivityProcessSuccessedBinding binding;
DatabaseReference reference;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityProcessSuccessedBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
}
private void readData(String name) {
reference = FirebaseDatabase.getInstance().getReference("users");
reference.child(name).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
DataSnapshot dataSnapshot = task.getResult();
String name = String.valueOf(dataSnapshot.child("name").getValue());
binding.textView.setText(name);
}});}}
The problem that I am having is that when a user adds a comment the entire list refreshes because I have notifyDataSetChanged(); set on the CommentAdapter. Everything jumps, and refreshes and I want it to be smoother, so I decided to use notifyItemInserted(); instead of notifyDataSetChanged();, but it isn't doing anything different.
notifyItemInserted(); should only update the last item or the newest item added to the list, so why is everything being refreshed/updated...?
Can someone tell me how to fix this? Only want last item added to list to be "added"/"updated"/whatever, not the entire list because if many people start commenting everything is always reloading...
In my readComments(); what I have now is mCommentAdapter.notifyItemInserted(mCommentList.size() - 1);, and what I had before was mCommentAdapter.notifyDataSetChanged();, but they are having the same effect. How can I fix this?
CommentsActivity
public class CommentsActivity extends AppCompatActivity {
private CommentAdapter mCommentAdapter;
private List<Comment> mCommentList;
private RecyclerView mRecyclerView;
EditText mAddComment;
ImageView mImageProfile;
TextView mPost;
String mPostId;
String mPublisherId;
String mNotificationId;
FirebaseUser mFirebaseUser;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comments);
mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(linearLayoutManager);
mCommentList = new ArrayList<>();
mCommentAdapter = new CommentAdapter(this, mCommentList, mPostId);
mRecyclerView.setAdapter(mCommentAdapter);
mAddComment = findViewById(R.id.add_comment);
mImageProfile = findViewById(R.id.image_profile);
mPost = findViewById(R.id.post_comment);
mFirebaseUser = FirebaseAuth.getInstance().getCurrentUser();
mPost.setOnClickListener(v -> {
if (mAddComment.getText().toString().equals("")) {
Toast.makeText(CommentsActivity.this, "Can't send empty comments", Toast.LENGTH_SHORT).show();
} else {
addCommentNotification(mPublisherId, mPostId);
}
});
getImage();
readComments();
}
private void getImage() {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Users").child(mFirebaseUser.getUid());
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
if (user != null)
Glide.with(getApplicationContext()).load(user.getImageurl()).into(mImageProfile);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void readComments() {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Comments").child(mPostId);
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
mCommentList.clear();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Comment comment = snapshot.getValue(Comment.class);
mCommentList.add(comment);
}
mCommentAdapter = new CommentAdapter(CommentsActivity.this, mCommentList, mPostId);
mRecyclerView.setAdapter(mCommentAdapter);
mCommentAdapter.notifyItemInserted(mCommentList.size() - 1);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
I am guessing you should be using a ChildEventListener:
Your readComments() must be like this:
private void readComments() {
//your reference
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Comments").child(mPostId);
//the child listener
ChildEventListener listener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
// A new comment has been added
Comment comment = dataSnapshot.getValue(Comment.class);
mCommentList.add(comment);
//Notify adapter
mCommentAdapter.notifyItemInserted(mCommentList.size() - 1);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
//attach the listener
ref.addChildEventListener(listener);
}
I have created an Android chat application which incorporates Firebase and created a fragment with a current users messages. The error occurs when a user sends a message to more than one person and the following image link shows how the first user is displayed whereas the most recent one who I have sent a message to is invisible. Does anyone know why this would be? I will include a few of my classes below.
My firebase is displayed as the following with messages containing first the user IDs, recipient IDs and then the unique message ID followed by the actual message and who sent it.
Thanks for your help.
https://i.stack.imgur.com/TrFUg.png
Here on the link below you will see two tabs, the first is not displaying and the second is. Both messages are to different recipients.
https://i.stack.imgur.com/M5LXa.png
Fragment Class
public class ChatFragment extends Fragment {
private RecyclerView convList;
private FirebaseAuth auth;
private DatabaseReference userDatabase, messageDatabase;
private View view;
public ChatFragment() {
// Required empty public constructor
}
// Chat Fragment initialised
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_chat, container, false);
convList = (RecyclerView) view.findViewById(R.id.conv_list);
convList.setHasFixedSize(true);
convList.setLayoutManager(new LinearLayoutManager(getContext()));
auth = FirebaseAuth.getInstance();
String currentUser;
currentUser = auth.getCurrentUser().getUid();
messageDatabase = FirebaseDatabase.getInstance().getReference().child("Messages").child(currentUser);
messageDatabase.keepSynced(true);
userDatabase = FirebaseDatabase.getInstance().getReference().child("Users");
userDatabase.keepSynced(true);
return view;
}
// Use FirebaseRecyclerAdapter to populate each user of which messages have been sent between, the latest message sent between each user displayed, ordering the list by most recent message and the user's online status
#Override
public void onStart() {
super.onStart();
Query conversationQuery = messageDatabase.orderByChild("time");
FirebaseRecyclerAdapter<UserConversation, ConvViewHolder> friendsRecyclerViewAdapter = new FirebaseRecyclerAdapter<UserConversation, ConvViewHolder>(
UserConversation.class,
R.layout.activity_user_list,
ConvViewHolder.class,
conversationQuery
) {
#Override
protected void populateViewHolder(final ConvViewHolder friendsViewHolder, final UserConversation friends, int i) {
final String user_id = getRef(i).getKey();
Query lastMessageQuery = messageDatabase.child(user_id).limitToLast(1);
// Retrieving data stored in Firebase for each message and its information
lastMessageQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String data = dataSnapshot.child("message").getValue().toString();
ConvViewHolder.setMessage(data);
String time = dataSnapshot.child("time").getValue().toString();
ConvViewHolder.setTime(time);
}
#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) {
}
});
userDatabase.child(user_id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final String userName = dataSnapshot.child("name").getValue().toString();
String userThumb = dataSnapshot.child("thumb_image").getValue().toString();
String online = dataSnapshot.child("online").getValue().toString();
friendsViewHolder.setName(userName);
friendsViewHolder.setUserImage(userThumb, getContext());
friendsViewHolder.setUserOnline(online);
friendsViewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(getContext(), Messenger.class);
intent.putExtra("user_id", user_id);
intent.putExtra("user_name", userName);
try {
startActivity(intent);
}
catch (ActivityNotFoundException e) {
Log.i("Activity Not Found", "Error");
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
convList.setAdapter(friendsRecyclerViewAdapter);
}
public static class ConvViewHolder extends RecyclerView.ViewHolder {
static View mView;
public ConvViewHolder(View itemView) {
super(itemView);
mView = itemView;
}
public static void setMessage(String message){
TextView userStatusView = (TextView) mView.findViewById(R.id.user_single_status);
userStatusView.setText(message);
}
public void setName(String name) {
TextView userNameView = (TextView) mView.findViewById(R.id.user_single_name);
userNameView.setText(name);
}
public void setUserImage(String thumb_image, Context ctx) {
CircleImageView userImageView = (CircleImageView) mView.findViewById(R.id.user_single_image);
Picasso.with(ctx).load(thumb_image).placeholder(R.drawable.default_avatar).into(userImageView);
}
public static void setTime (String time){
TextView test = (TextView) mView.findViewById(R.id.time_sent);
test.setText(DateFormat.format("dd-MM-yyyy (HH:mm:ss)", Long.parseLong(time)));
}
public void setUserOnline(String online) {
ImageView userOnlineView = (ImageView) mView.findViewById(R.id.user_single_online_icon);
if(online.equals("true")){
userOnlineView.setVisibility(View.VISIBLE);
} else {
userOnlineView.setVisibility(View.INVISIBLE);
}
}
}
}
I have created an Android chat application which incorporates Firebase and created a fragment with a current users messages. The error occurs when a user sends a message to more than one person and the following image link shows how the first user is displayed whereas the most recent one who I have sent a message to is invisible. Does anyone know why this would be? I will include a few of my classes below.
My firebase is displayed as the following with messages containing first the user IDs, recipient IDs and then the unique message ID followed by the actual message and who sent it.
Thanks for your help.
https://i.stack.imgur.com/TrFUg.png
https://i.stack.imgur.com/M5LXa.png
Fragment Class
public class ChatFragment extends Fragment {
private RecyclerView convList;
private FirebaseAuth auth;
private DatabaseReference userDatabase, messageDatabase;
private View view;
public ChatFragment() {
// Required empty public constructor
}
// Chat Fragment initialised
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_chat, container, false);
convList = (RecyclerView) view.findViewById(R.id.conv_list);
convList.setHasFixedSize(true);
convList.setLayoutManager(new LinearLayoutManager(getContext()));
auth = FirebaseAuth.getInstance();
String currentUser;
currentUser = auth.getCurrentUser().getUid();
messageDatabase = FirebaseDatabase.getInstance().getReference().child("Messages").child(currentUser);
messageDatabase.keepSynced(true);
userDatabase = FirebaseDatabase.getInstance().getReference().child("Users");
userDatabase.keepSynced(true);
return view;
}
// Use FirebaseRecyclerAdapter to populate each user of which messages have been sent between, the latest message sent between each user displayed, ordering the list by most recent message and the user's online status
#Override
public void onStart() {
super.onStart();
Query conversationQuery = messageDatabase.orderByChild("time");
FirebaseRecyclerAdapter<UserConversation, ConvViewHolder> friendsRecyclerViewAdapter = new FirebaseRecyclerAdapter<UserConversation, ConvViewHolder>(
UserConversation.class,
R.layout.activity_user_list,
ConvViewHolder.class,
conversationQuery
) {
#Override
protected void populateViewHolder(final ConvViewHolder friendsViewHolder, final UserConversation friends, int i) {
final String user_id = getRef(i).getKey();
Query lastMessageQuery = messageDatabase.child(user_id).limitToLast(1);
// Retrieving data stored in Firebase for each message and its information
lastMessageQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String data = dataSnapshot.child("message").getValue().toString();
ConvViewHolder.setMessage(data);
String time = dataSnapshot.child("time").getValue().toString();
ConvViewHolder.setTime(time);
}
#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) {
}
});
userDatabase.child(user_id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final String userName = dataSnapshot.child("name").getValue().toString();
String userThumb = dataSnapshot.child("thumb_image").getValue().toString();
String online = dataSnapshot.child("online").getValue().toString();
friendsViewHolder.setName(userName);
friendsViewHolder.setUserImage(userThumb, getContext());
friendsViewHolder.setUserOnline(online);
friendsViewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(getContext(), Messenger.class);
intent.putExtra("user_id", user_id);
intent.putExtra("user_name", userName);
try {
startActivity(intent);
}
catch (ActivityNotFoundException e) {
Log.i("Activity Not Found", "Error");
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
convList.setAdapter(friendsRecyclerViewAdapter);
}
public static class ConvViewHolder extends RecyclerView.ViewHolder {
static View mView;
public ConvViewHolder(View itemView) {
super(itemView);
mView = itemView;
}
public static void setMessage(String message){
TextView userStatusView = (TextView) mView.findViewById(R.id.user_single_status);
userStatusView.setText(message);
}
public void setName(String name) {
TextView userNameView = (TextView) mView.findViewById(R.id.user_single_name);
userNameView.setText(name);
}
public void setUserImage(String thumb_image, Context ctx) {
CircleImageView userImageView = (CircleImageView) mView.findViewById(R.id.user_single_image);
Picasso.with(ctx).load(thumb_image).placeholder(R.drawable.default_avatar).into(userImageView);
}
public static void setTime (String time){
TextView test = (TextView) mView.findViewById(R.id.time_sent);
test.setText(DateFormat.format("dd-MM-yyyy (HH:mm:ss)", Long.parseLong(time)));
}
public void setUserOnline(String online) {
ImageView userOnlineView = (ImageView) mView.findViewById(R.id.user_single_online_icon);
if(online.equals("true")){
userOnlineView.setVisibility(View.VISIBLE);
} else {
userOnlineView.setVisibility(View.INVISIBLE);
}
}
}
}
I am getting a lot of memory leaks in an application I created. I created a very simple app to reproduce the problem. This application just makes a reference to the FirebaseDatabase and sets up a ChildEventListener. When the user clicks the button it adds a record to the database, and starts a new activity which does System.gc().
Pressing the button multiple times will cause Leak Canary to generate a dump.
MainActivity.java:
public class MainActivity extends AppCompatActivity {
private FirebaseDatabase firebaseDatabase;
private DatabaseReference dbRef;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
firebaseDatabase = FirebaseDatabase.getInstance();
dbRef = firebaseDatabase.getReference("leak");
dbRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
findViewById(R.id.btn_leak).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dbRef.child(UUID.randomUUID().toString()).setValue("Yes");
Intent leakIntent = new Intent(getApplicationContext(), LeakActivity.class);
startActivity(leakIntent);
}
});
}
}
LeakActivity.java:
public class LeakActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.leak);
System.gc();
}
}
Due to the post limit, the leak canary log is here.
Am I doing something wrong in my code, or is this related to Firebase?
EDIT: #qbix's answer seemed to work. For others, here is the working version of MainActivity.java:
public class MainActivity extends AppCompatActivity {
private FirebaseDatabase firebaseDatabase;
private DatabaseReference dbRef;
private ChildEventListener dbListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
firebaseDatabase = FirebaseDatabase.getInstance();
dbRef = firebaseDatabase.getReference("leak");
dbListener = getDbListener();
dbRef.addChildEventListener(dbListener);
findViewById(R.id.btn_leak).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dbRef.child(UUID.randomUUID().toString()).setValue("Yes");
Intent leakIntent = new Intent(getApplicationContext(), LeakActivity.class);
startActivity(leakIntent);
}
});
}
#Override
protected void onStop() {
dbRef.removeEventListener(dbListener);
super.onStop();
}
private ChildEventListener getDbListener(){
return new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
}
#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) {
}
};
}
}
I haven't used LeakCanary, so this is just an educated guess.
ChildEventListeners need to be unregistered when they are no longer needed. Often listeners are added and removed in activity lifecycle methods, such as onCreate() and onDestroy(). Instead of creating an anonymous listener, create an object of that type and remove it using Query.removeEventListener() when no longer needed to see if that eliminates the leak report.
I think that is better to add/remove listeners within the onStart()/onStop() or the onCreate()/onDestroy() callbacks respectively.
If a listener was added in onCreate() and removed in onStop() there could occur a situation when an activity will be restored without of calling the onCreate() but with calling of onStart() and listener will not be set.
https://developer.android.com/images/activity_lifecycle.png