Reputation: 1012
I am trying to create a chat. However, I am facing a big issue. When I delete an entry from the chat recyclerView, the entry stays there, but is removed from the firebase db as expected (I mean only removing from db is working). It only disapears when I close the activity and open it again. My recyclerView has a feature of load 10 more entries each time a user scrolls.
I dont understand how can I remove the entry from the recyclerView?
I need to call the function loadMoreMessages()? Refresh the activity?
MessageAdapter.java
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MessageViewHolder>{
public class MessageViewHolder extends RecyclerView.ViewHolder {
public TextView messageText, displayName;
public CircleImageView profileImage;
public ImageView messageImage;
public MessageViewHolder(View itemView) {
super(itemView);
messageText = (TextView) itemView.findViewById(R.id.message_text_layout);
profileImage = (CircleImageView) itemView.findViewById(R.id.message_profile_layout);
displayName = (TextView) itemView.findViewById(R.id.name_text_layout);
messageImage = (ImageView) itemView.findViewById(R.id.message_image_layout);
}
}
private List<Messages> mMessageList;
private FirebaseAuth mAuth;
private DatabaseReference mUserDatabase;
private DatabaseReference mUserDatabaseSettings;
private String rul, fromUser_t;
private UserAccountSettings mUserAccountSettings;
public MessageAdapter(List<Messages> mMessageList) {
this.mMessageList = mMessageList;
}
@Override
public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_single_layout,parent,false);
mAuth = FirebaseAuth.getInstance();
return new MessageViewHolder(v);
}
@Override
public void onBindViewHolder(final MessageViewHolder holder, final int position) {
final String current_user_id = mAuth.getCurrentUser().getUid();
final Messages c = mMessageList.get(position);
final String from_user = c.getFrom();
final String message_type = c.getType();
final String messageContent = c.getMessage();
/**
* popup for text message
*/
// check if the message is from the current user
if(from_user.equals(current_user_id))
{
holder.messageText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//popup menu to select option
CharSequence options[] = new CharSequence[]
{
"Delete from my phone",
"Delete for Everyone" ,
"Cancel"
};
final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());
builder.setTitle("Message Options");
builder.setItems(options, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//Click Event for each item.
if(i == 0)
{
deleteSentMessage(position, holder);
// Intent intent = new Intent(holder.itemView.getContext(), ChatActivity.class);
// holder.itemView.getContext().startActivity(intent);
//MessageViewHolder.class.notify();
holder.messageText.setPaintFlags(holder.messageText.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}
else if(i == 1)
{
deleteMessageForEveryOne(position, holder);
// Intent intent = new Intent(holder.itemView.getContext(), ChatActivity.class);
// holder.itemView.getContext().startActivity(intent);
//notifyDataSetChanged();
//holder.messageText.setVisibility(View.GONE);
}
}
});
builder.show();
}
});
}else
{
// similar code to check if the message is from the other user
}
holder.messageText.setVisibility(View.GONE);
holder.messageImage.setVisibility(View.GONE);
if("text".equals(message_type))
{
holder.messageText.setVisibility(View.VISIBLE);
if(from_user.equals(current_user_id))
{
holder.messageText.setBackgroundResource(R.drawable.message_text_background);
holder.messageText.setTextColor(Color.WHITE);
//todo left right not working
holder.messageText.setGravity(Gravity.LEFT);
}else
{
holder.messageText.setBackgroundColor(Color.WHITE);
holder.messageText.setTextColor(Color.BLACK);
holder.messageText.setGravity(Gravity.RIGHT);
}
holder.messageText.setText(c.getMessage());
}else
{
holder.messageImage.setVisibility(View.VISIBLE);
UniversalImageLoader.setImage(messageContent,holder.messageImage,null,"");
}
}
@Override
public int getItemCount() {
return mMessageList.size();
}
/**
function to Delete from my phone
**/
private void deleteSentMessage(final int position, final MessageViewHolder holder)
{
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.child("messages")
.child(mMessageList.get(position).getFrom())
.child(mMessageList.get(position).getTo())
.child(mMessageList.get(position).getMessageID())
.removeValue().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task)
{
if(task.isSuccessful())
{
Toast.makeText(holder.itemView.getContext(), "Deleted Successfully.", Toast.LENGTH_SHORT).show();
}else
{
Toast.makeText(holder.itemView.getContext(), "Error Occurred.", Toast.LENGTH_SHORT).show();
}
}
});
}
/**
function to Delete from receiver phone
**/
private void deleteReceiveMessage(final int position, final MessageViewHolder holder)
{
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.child("messages")
.child(mMessageList.get(position).getTo())
.child(mMessageList.get(position).getFrom())
.child(mMessageList.get(position).getMessageID())
.removeValue().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task)
{
if(task.isSuccessful())
{
Toast.makeText(holder.itemView.getContext(), "Deleted Successfully.", Toast.LENGTH_SHORT).show();
}else
{
Toast.makeText(holder.itemView.getContext(), "Error Occurred.", Toast.LENGTH_SHORT).show();
}
}
});
}
/**
function to Delete from both sides
**/
private void deleteMessageForEveryOne(final int position, final MessageViewHolder holder)
{
final DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.child("messages")
.child(mMessageList.get(position).getTo())
.child(mMessageList.get(position).getFrom())
.child(mMessageList.get(position).getMessageID())
.removeValue().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task)
{
if(task.isSuccessful())
{
rootRef.child("messages")
.child(mMessageList.get(position).getFrom())
.child(mMessageList.get(position).getTo())
.child(mMessageList.get(position).getMessageID())
.removeValue().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if(task.isSuccessful())
{
Toast.makeText(holder.itemView.getContext(), "Deleted Successfully.", Toast.LENGTH_SHORT).show();
}
}
});
}else
{
Toast.makeText(holder.itemView.getContext(), "Error Occurred.", Toast.LENGTH_SHORT).show();
}
}
});
}
}
ChatActivity.java
public class ChatActivity extends AppCompatActivity {
private static final String TAG = "ChatActivity";
//todo check is user is logedin
//user with whom Iam talking to
private String mChatUser;
private Toolbar mChatToolbar;
private DatabaseReference mRootRef;
private TextView mTitleView, mLastSeenView;
private CircleImageView mProfileImage;
private FirebaseAuth mAuth;
private String mCurrentUserId;
private ImageButton mChatAddBtn;
private ImageButton mChatSendBtn;
private EditText mChatMessageView;
private RecyclerView mMessagesList;
private SwipeRefreshLayout mRefreshLayout;
private final List<Messages> messagesList = new ArrayList<>();
private LinearLayoutManager mLinearLayout;
private MessageAdapter mAdapter;
private static final int TOTAL_ITEMS_TO_LOAD = 10;
private int mCurrentPage = 1;
private int itemPos =0;
private String mLastKey = "";
private String mPrevKey = "";
private static final int GALLERY_PICK = 1;
//Storage Firebase
private StorageReference mImageStorage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
// create a toolbar and set the title as the user with we are chatting to
mChatToolbar = (Toolbar) findViewById(R.id.chat_app_bar);
setSupportActionBar(mChatToolbar);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
// to add a custom view to the toolbar
actionBar.setDisplayShowCustomEnabled(true);
mRootRef = FirebaseDatabase.getInstance().getReference();
mAuth = FirebaseAuth.getInstance();
mCurrentUserId = mAuth.getCurrentUser().getUid();
mChatUser = getIntent().getStringExtra("user_id");
String userName = getIntent().getStringExtra("user_name");
getSupportActionBar().setTitle(userName);
// String userName = getIntent().getStringExtra("user_name");
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View action_bar_view = inflater.inflate(R.layout.chat_custom_bar, null);
actionBar.setCustomView(action_bar_view);
// ---------- Custom Action bar items ---
mTitleView = (TextView) findViewById(R.id.custom_bar_title);
mLastSeenView = (TextView) findViewById(R.id.custom_bar_seen);
mProfileImage = (CircleImageView) findViewById(R.id.custom_bar_image);
mChatAddBtn = (ImageButton) findViewById(R.id.chat_add_btn);
mChatSendBtn = (ImageButton) findViewById(R.id.chat_send_btn);
mChatMessageView = (EditText) findViewById(R.id.chat_message_view);
mAdapter = new MessageAdapter(messagesList);
mImageStorage= FirebaseStorage.getInstance().getReference();
mMessagesList = (RecyclerView) findViewById(R.id.messages_list);
mRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.message_swipe_layout);
mLinearLayout = new LinearLayoutManager(this);
mMessagesList.setHasFixedSize(true);
mMessagesList.setLayoutManager(mLinearLayout);
//mMessagesList.setItemViewCacheSize(25);
mMessagesList.setDrawingCacheEnabled(true);
mMessagesList.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
mMessagesList.setAdapter(mAdapter);
loadMessages();
mTitleView.setText(userName);
mRootRef.child("users").child(mChatUser).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
String online = dataSnapshot.child("lastSeen").getValue().toString();
//String image = dataSnapshot.child()
if(online.equals("true")){
mLastSeenView.setText("Online");
}else {
GetTimeAgo getTimeAgo = new GetTimeAgo();
long lastTime = Long.parseLong(online);
String lastSeenTime = getTimeAgo.getTimeAgo(lastTime, getApplicationContext());
mLastSeenView.setText(lastSeenTime);
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
mRootRef.child("Chat").child(mCurrentUserId).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
if(!dataSnapshot.hasChild(mChatUser)){
Map chatAddMap = new HashMap();
chatAddMap.put("seen", false);
chatAddMap.put("timestamp", ServerValue.TIMESTAMP);
Map chatUserMap = new HashMap();
chatUserMap.put("Chat/" + mCurrentUserId + "/" + mChatUser, chatAddMap); //add map to current user
chatUserMap.put("Chat/" + mChatUser + "/" + mCurrentUserId, chatAddMap);
mRootRef.updateChildren(chatUserMap, new DatabaseReference.CompletionListener() {
@Override
public void onComplete(@Nullable DatabaseError databaseError, @NonNull DatabaseReference databaseReference) {
if(databaseError != null){
//Log.d(TAG, "onComplete: CHAT LOG", databaseError.getMessage());
Log.e(TAG, "onComplete: CHAT_LOG" + databaseError.getMessage().toString());
}
}
});
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
mChatSendBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendMessage();
}
});
// open gallery to get an image
mChatAddBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent galleryIntent = new Intent();
galleryIntent.setType("image/*");
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(galleryIntent,"SELECT IMAGE"), GALLERY_PICK);
}
});
//pagination
mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mCurrentPage++;
// each time it loads a new page should go to position zero
itemPos = 0;
loadMoreMessages();
}
});
}
/**
* load more messages everytime uesr refreshes
*/
private void loadMoreMessages() {
DatabaseReference messageRef = mRootRef.child("messages").child(mCurrentUserId).child(mChatUser);
Query messageQuery = messageRef.orderByKey().endAt(mLastKey).limitToLast(10);
messageQuery.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
Messages message = dataSnapshot.getValue(Messages.class);
String messageKey = dataSnapshot.getKey();
if(!mPrevKey.equals(messageKey)){
messagesList.add(itemPos++, message);
Log.d(TAG, "onChildAdded: xx" + mPrevKey + "---" + mLastKey);
} else {
mPrevKey = mLastKey;
}
if(itemPos == 1){
mLastKey = messageKey;
}
Log.d(TAG, "onChildAdded: TOTALKEUS" + "lastkey: " + mLastKey + " | Prev key : " + mPrevKey + " | Message Key : " + messageKey);
mAdapter.notifyDataSetChanged();
mRefreshLayout.setRefreshing(false);
mLinearLayout.scrollToPositionWithOffset(itemPos , 0);
}
@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) {
}
});
}
/**
* method used to load messages once
*/
private void loadMessages() {
//query to get pagination and last 10 messages
DatabaseReference messageRef = mRootRef.child("messages").child(mCurrentUserId).child(mChatUser);
Query messageQuery = messageRef.limitToLast(mCurrentPage * TOTAL_ITEMS_TO_LOAD);
messageQuery.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
Messages message = dataSnapshot.getValue(Messages.class);
itemPos++;
if(itemPos == 1){
// get the key to be user as a start point when loading more items
String messageKey = dataSnapshot.getKey();
mLastKey = messageKey;
mPrevKey = messageKey;
Log.d(TAG, "onChildAdded: last " +mLastKey + " prev " + mPrevKey + " messa " + messageKey + "-----------");
}
messagesList.add(message);
mAdapter.notifyDataSetChanged();
//pagination - define the bottom of the recycler view
//automatic scroll to bottom
mMessagesList.scrollToPosition(messagesList.size() - 1);
mRefreshLayout.setRefreshing(false);
}
@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) {
}
});
}
/**
* send photo message
* @param requestCode
* @param resultCode
* @param data
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == GALLERY_PICK && resultCode == RESULT_OK){
Uri imageUri = data.getData();
final String current_user_ref = "messages/" + mCurrentUserId + "/" + mChatUser;
final String chat_user_ref = "messages/" + mChatUser + "/" + mCurrentUserId;
DatabaseReference user_message_push = mRootRef.child("messages")
.child(mCurrentUserId).child(mChatUser).push();
final String push_id = user_message_push.getKey();
StorageReference filepath = mImageStorage.child("message_images").child( push_id + ".jpg");
filepath.putFile(imageUri).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
@Override
public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
if(task.isSuccessful()){
Task<Uri> result = task.getResult().getMetadata().getReference().getDownloadUrl();
result.addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri uri) {
String download_url = uri.toString();
Map messageMap = new HashMap();
messageMap.put("message", download_url);
messageMap.put("seen", false);
messageMap.put("type", "image");
messageMap.put("time", ServerValue.TIMESTAMP);
messageMap.put("from", mCurrentUserId);
messageMap.put("to", mChatUser);
messageMap.put("messageID", push_id );
Map messageUserMap = new HashMap();
messageUserMap.put(current_user_ref + "/" + push_id, messageMap);
messageUserMap.put(chat_user_ref + "/" + push_id, messageMap);
mChatMessageView.setText("");
mRootRef.updateChildren(messageUserMap, new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if(databaseError != null){
Log.d("CHAT_LOG", databaseError.getMessage().toString());
}
}
});
}
});
}
}
});
}
}
}
``
Upvotes: 0
Views: 305
Reputation: 11
The list changes size so you have to give new list size to your logic.
Upvotes: 1
Reputation: 1012
Thanks Frank and Vedprakash. It was a quit obvious. At that time I did not understand how to pass the position and remove the item using onChildRemoved. Now I understand. What I did was based on this post: How to use onChildRemoved in Firebase Realtime Database?
@Override
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
Toast.makeText(ChatActivity.this, "Entered on this part!", Toast.LENGTH_SHORT).show();
int index = keyList.indexOf(dataSnapshot.getKey());
messagesList.remove(index);
keyList.remove(index);
//mAdapter.notifyDataSetChanged();
mAdapter.notifyItemRemoved(index);
}
I tested it and is working fine. However, It has a little issue. My chat loads 10 items (defined by: private static final int TOTAL_ITEMS_TO_LOAD = 10;). If the user scrolls it will load more items.
When I open the chat page, the items loaded are from "2" to "11" But, when I delete item, for example, "9" the item "1" show up. Meaning that, the item right before the position zero of the messagesList will show up.
I tried several things. My last try was set the mLinearLayout.scrollToPosition, decrease the TOTAL_ITEMS_TO_LOAD. And the issue stays alive. :(
Before delete
After delete item
Upvotes: 0
Reputation: 598728
You're using a ChildEventListener
in your messageQuery
, which has these main methods:
onChildAdded
, which is called initially for every child node matching your messageQuery
query, and subsequently when any child node is added to the database that falls into messageQuery
.onChildRemoved
, which is called when any child node (that falls) is removed from the database (or at least to the part that messageQuery
is listening to).onChildChanged
, which is called when any child node that messageQuery
is listening is modified in the database.If I look at your implementation, you've only implemented onChildAdded
. So when you remove a child node from the database, Firebase tells your ChildEventListener
about it, but that then does nothing.
To remove the message from the UI when it gets removed from the database, you will need to implement:
@Override
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
// TODO: remove the message matching dataSnapshot from messagesList
// TODO: call adapter.notifyDataSetChanged() so that the UI gets updates
}
Upvotes: 1