I have chat app with recyclerview, Its working fine until i come back to chat screen from notification, when i come to chat screen from notification i can send message to other user and other user can send message to me but the chat screen not updating at all whether i send message or i receive. There is function called chatDatabase() i notice if i comment that function then screen works perfectly without any issue even if i come on chat screen from notification. But i am not able to figure out why its not working with chatDatabase()
ChatScreenFragment
public class Chat_Screen_Fragment extends Fragment implements View.OnClickListener, ChildEventListener{
public static final String TAG = "###CHAT SCREEN###";
List<Chat_Wrapper> message = new ArrayList<>();
Chat_Adapter adapter;
RecyclerView recyclerView;
LinearLayoutManager layoutManager;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View v=inflater.inflate(R.layout.chat_screen_main_fragment,container,false);
setRetainInstance(true);
// GET INTENT VALUES FROM USER PROFILE CLASS
UserName_Intent = getArguments().getString("Get_Name");
UserImage_Intent = getArguments().getString("Get_Image");
UserPhone_Intent = getArguments().getString("Get_Phone");
UserID_Intent = getArguments().getString("Get_ID");
FirebaseToken_Intent = getArguments().getString("Get_Token"); //Firebase Token of other person
Room_Name_Intent = getArguments().getString("Get_Other"); // Room Name of chat
UserLastSeen_Intent=getArguments().getString("LastSeen");
//Sender_FCMToken = Session.getFirebaseID();
// RECYCLER VIEW
recyclerView = v.findViewById(R.id.Chat_Screen_Message_List);
layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setStackFromEnd(true);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(layoutManager);
databaseReference = FirebaseDatabase.getInstance().getReference().child(Room_Name_Intent);
databaseReference.addChildEventListener(this);
adapter = new Chat_Adapter(getActivity(), message);
recyclerView.setAdapter(adapter);
// FETCH OLD MESSAGE FROM DATABASE
chatDatabase();
return v;
}
// FIREBASE REAL TIME DATABASE WHICH FETCH ALL MESSAGES (SYNC) FROM ONLINE DATABASE
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
append_chat_conversation(dataSnapshot);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
append_chat_conversation(dataSnapshot);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
private synchronized void append_chat_conversation(DataSnapshot dataSnapshot) {
iterator = dataSnapshot.getChildren().iterator();
while (iterator.hasNext()) {
// NOW GET ALL DATA FROM FIREBASE DATABASE AND SAVE IT INTO STRINGS THEN CHECK EACH BY ITS MESSAGE TYPE
Chat_Msg = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_FROM = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_FCM_TO= (String) ((DataSnapshot) iterator.next()).getValue();
Chat_Database tempChatDatabase = new Chat_Database(getActivity());
boolean hasValue=tempChatDatabase.CheckValueExist(_ID);
if (!hasValue) {
Log.d(TAG,"Chat Message "+Chat_Msg);
long id=chat_database.Insert_Chat(Session.getUserID(),Room_Name_Intent, UserID_Intent, "Text", Chat_Msg, Chat_FROM, Chat_TO, Chat_TimeStamp, Chat_FCM_FROM, Chat_FCM_TO, Session.getPhoneNO(), UserPhone_Intent,Random_ID,UserImage_Intent,UserLastSeen_Intent,Chat_FROM_ID);
//Adding Chat Data Into Database
Log.d(TAG,"Database Entry ID "+id);
if (id==0){
Log.d(TAG,"Database Already Has Value Of This Random Id ");
return;
}
Chat_Wrapper chat_wrapper = new Chat_Wrapper(Chat_Msg, null, null, null, null, null, null, Chat_TimeStamp, User_Intent, UserImage_Intent, Chat_FROM, null,null,id);
message.add(chat_wrapper);
adapter.notifyDataSetChanged();
Log.d(TAG, "FIREBASE STORAGE PHOTO-3-MESSAGE ARRAY SIZE " + message.size());
recyclerView.post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(adapter.getItemCount());
}
});
}
}
}
Log.d(TAG, "MESSAGE ARRAY SIZE " + message.size());
tempChatDatabase.isDatabaseClose();
}
adapter.notifyDataSetChanged();
recyclerView.post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(message.size()-1);
//recyclerView.smoothScrollToPosition(adapter.getItemCount());
}
});
}
private void chatDatabase(){
//Database Init and Filling Adapter
Log.d(TAG,"Chat Database Function");
chat_database=new Chat_Database(getActivity());
chatCursor=chat_database.getUserChat(UserID_Intent);
boolean checkDB_Exist=functions.DatabaseExist(getActivity(),"CHAT_DATABASE.DB");
boolean chatItemsCounts=chatCursor.getCount()>0;
chatCursor.moveToFirst();
Log.d(TAG,"Value At Chat Database "+ checkDB_Exist+" "+chatItemsCounts);
if (checkDB_Exist && chatCursor.getCount()>0 && chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID")).equals(UserID_Intent)){
Log.d(TAG,"Database Exist Chat Database");
message.clear();
chatCursor.moveToFirst();
do {
database_rowID=chatCursor.getInt(chatCursor.getColumnIndex("ID"));
database_userID=chatCursor.getString(chatCursor.getColumnIndex("USER_ID"));
database_RoomName =chatCursor.getString(chatCursor.getColumnIndex("ROOM_NAME"));
database_ReceiverID=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID"));
database_MessageType=chatCursor.getString(chatCursor.getColumnIndex("MESSAGE_TYPE"));
database_Message=chatCursor.getString(chatCursor.getColumnIndex("USER_MESSAGE"));
database_MsgFrom=chatCursor.getString(chatCursor.getColumnIndex("SENDER_NAME"));
database_MsgTo=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_NAME"));
database_TimeStamp=chatCursor.getString(chatCursor.getColumnIndex("TIME_STAMP"));
database_FCMfrom=chatCursor.getString(chatCursor.getColumnIndex("SENDER_TOKEN"));
database_FCMto=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_TOKEN"));
database_LocalPath=chatCursor.getString(chatCursor.getColumnIndex("DOWNLOADED_AT"));
database_PhoneFrom=chatCursor.getString(chatCursor.getColumnIndex("MY_PHONE"));
database_PhoneTo=chatCursor.getString(chatCursor.getColumnIndex("OTHER_PHONE"));
Log.d(TAG,"Value Of Database Message String = "+database_Message);
Log.d(TAG,"Row ID of Database "+database_rowID);
// Check Message Type
Log.d(TAG,"Message Type Is Text");
Chat_Wrapper text = new Chat_Wrapper(database_Message, null, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom,null,null,database_rowID);
message.add(text);
}
while(chatCursor.moveToNext());
Room_Name_Intent = database_RoomName;
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
adapter.notifyDataSetChanged();
chatCursor.close();
boolean value = chat_database.isDatabaseClose();
recyclerView.post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(message.size()-1);
}
});
Log.d(TAG,"Value Of Database Close or Not "+value);
}
}
}
UPDATE: i just logout and login again then entered into chat screen from notification then its working fine. Also if i remove app from recent app then it also work but i am still unable to understand, why its happening.
I looked through your chatDatabase function and noticed some suspicious behavior right before you call notifyDataSetChanged on your adapter. I tested in my own project and noticed my rendering also stopped if I tried to set the LayoutManager on the RecyclerView after it was already set.
You set up your RecyclerView fine with it at the beginning, so removing the new calls should solve the issue, as long as there isn't anything else wrong with the chatDatabase function. Let me know if removing the LayoutManager lines from the chatDatabase function solve the issue!
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
adapter.notifyDataSetChanged();
Related
I am attempting to implement my own CardView within a Fragment following previous help from another SO user. I think I have done everything correctly however I am not seeing the expected results and think the fault is elsewhere..
What I have is a CalendarView within a Fragment which displays the selected date inside a TextView using setOnDateChangeListener. I then have a hidden RecyclerView which has a CardView within that, which I am trying to call if the CalendarView date matches the date stored in my Firebase database.. Still with me?
I am creating an event schedule, using a form which has event name, date, time, description, all stored as strings, see below:
I have no idea how to retrieve the push() value when referencing my database so I have just used the event name for ease at the moment..
Here is what I have, it is a bit all over the place as I have been testing here and there but please let me know if you have questions.. I tried separating my database references to use them in different areas but this did not work either.
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_schedule, container, false);
intentEvent = getActivity().getIntent().getStringExtra("name");
// ------------- firebase --------------
firebaseAuth = FirebaseAuth.getInstance();
currentUser = firebaseAuth.getCurrentUser();
uid = currentUser.getUid();
databaseReference = FirebaseDatabase.getInstance().getReference("user").child(uid).child("dogs").child(intentEvent);
eventReference = databaseReference.child("events");
dateRef = eventReference.child("date");
// -------------------------------------
calendarView = view.findViewById(R.id.calendarView);
scheduleTitle = view.findViewById(R.id.scheduleTitle);
noEventPlaceholder = view.findViewById(R.id.noEventPlaceholder);
addNewEvent = view.findViewById(R.id.addNewEvent);
eventRecycler = view.findViewById(R.id.eventRecycler);
eventRecycler.setVisibility(View.GONE);
eventLayoutManager = new LinearLayoutManager(getContext());
eventRecycler.setLayoutManager(eventLayoutManager);
FirebaseRecyclerOptions<Event> options
= new FirebaseRecyclerOptions.Builder<Event>()
.setQuery(databaseReference, Event.class)
.build();
eventAdapter = new EventAdapter(options, new EventAdapter.EventCallback() {
#Override
public void onCardViewClick(Event event) {
// view event in full?
}
});
if (eventReference == null) {
addNewEvent.setVisibility(View.VISIBLE);
noEventPlaceholder.setText("Nothing planned for today.. Let's go walkies!");
} else {
eventRecycler.setVisibility(View.VISIBLE);
}
eventRecycler.setAdapter(eventAdapter);
getEventData();
dog = new Dog();
calendarView
.setOnDateChangeListener(
new CalendarView
.OnDateChangeListener() {
#Override
public void onSelectedDayChange(
#NonNull CalendarView view,
int year,
int month,
int dayOfMonth)
{
String date
= dayOfMonth + "-"
+ (month + 1) + "-" + year;
scheduleTitle.setText(date);
}
});
// this is an example of what i am trying to do..
if (String.valueOf(dateRef).equals(scheduleTitle)) {
eventRecycler.setVisibility(View.VISIBLE);
noEventPlaceholder.setText("Nothing planned for today.. Let's go walkies!");
}
// ---------------------------------------------
addNewEvent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String name = dataSnapshot.child("name").getValue(String.class);
Intent intentEventForm = new Intent(getContext(), EventForm.class);
intentEventForm.putExtra("name", name);
startActivity(intentEventForm);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w("TAG", "onCancelled", databaseError.toException());
}
});
}
});
return view;
}
public void getEventData() {
eventReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String eventName = dataSnapshot.child("name").getValue(String.class);
String eventDate = dataSnapshot.child("date").getValue(String.class);
String eventTime = dataSnapshot.child("time").getValue(String.class);
String eventDescription = dataSnapshot.child("description").getValue(String.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w("TAG", "onCancelled", databaseError.toException());
}
});
}
I'm trying to learn the android and need your help!
Problem 1:
I store my data with ".push()" . How do I update the data on the random number that I declared with ".push()".
Once I update that only create new data but not a data update
Problem 2
Once I delete the data will delete whole data but not only the specific data.
This is my timetable.java.
public class Timetable extends AppCompatActivity{
//View
RecyclerView timeTable;
RecyclerView.LayoutManager layoutManager;
DatabaseReference counterRef,currentRef;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ttable);
timeTable = (RecyclerView)findViewById(R.id.timeTable);
timeTable.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(this);
timeTable.setLayoutManager(layoutManager);
Toolbar toolbar = (Toolbar)findViewById(R.id.toolBar);
toolbar.setTitle("Time Table");
setSupportActionBar(toolbar);
counterRef = FirebaseDatabase.getInstance().getReference("Time");
currentRef = counterRef.child(FirebaseAuth.getInstance().getCurrentUser().getUid());
//online list here
updateList();
}
private void updateList() {
FirebaseRecyclerAdapter<Time, ListTimeViewHolder> adapter = new FirebaseRecyclerAdapter<Time, ListTimeViewHolder>(
Time.class,
R.layout.time_table,
ListTimeViewHolder.class,
currentRef
) {
#Override
protected void populateViewHolder(final ListTimeViewHolder viewHolder, final Time model, int position) {
viewHolder.txtEmail.setText("Time (" +model.getTime()+ ")");
viewHolder.itemClickListenener1 = new ItemClickListenener1() {
#Override
public void onClick(View view, int position) {
showUpdateDeleteLog(model.getEmail(),model.getTime());
//alert dialog
// AlertDialog alertDialog = new AlertDialog.Builder(Timetable.this).create();
//
// // Setting Dialog Title
// alertDialog.setTitle("Message");
//
// // Setting Dialog Message
// alertDialog.setMessage("Unable to click");
//
// alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
// public void onClick(DialogInterface dialog, int which) {
// // Write your code here to execute after dialog closed
// Toast.makeText(getApplicationContext(), "OK", Toast.LENGTH_SHORT).show();
// }
// });
//
// // Showing Alert Message
// alertDialog.show();
}
private void showUpdateDeleteLog(String email, String time) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(Timetable.this);
LayoutInflater inflater = getLayoutInflater();
final View dialogView = inflater.inflate(R.layout.update_dialog, null);
dialogBuilder.setView(dialogView);
final EditText editTextName = (EditText) dialogView.findViewById(R.id.editTextName);
final Button buttonUpdate = (Button) dialogView.findViewById(R.id.buttonUpdateArtist);
final Button buttonDelete = (Button) dialogView.findViewById(R.id.buttonDeleteArtist);
dialogBuilder.setTitle(email);
final AlertDialog b = dialogBuilder.create();
b.show();
buttonUpdate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String name = editTextName.getText().toString().trim();
if (!TextUtils.isEmpty(name)) {
updateArtist(model.getEmail(),name);
b.dismiss();
}
}
});
buttonDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
deleteArtist(model.getEmail(),model.getTime());
b.dismiss();
}
});
}
private boolean deleteArtist(String email, String time) {
//getting the specified artist reference
DatabaseReference dR = FirebaseDatabase.getInstance().getReference("Time").child(FirebaseAuth.getInstance().getCurrentUser().getUid());
//removing artist
dR.removeValue();
Toast.makeText(getApplicationContext(), "Time is deleted", Toast.LENGTH_LONG).show();
return true;
}
private boolean updateArtist(String email, String time) {
//getting the specified artist reference
DatabaseReference dR = FirebaseDatabase.getInstance().getReference("Time").child(FirebaseAuth.getInstance().getCurrentUser().getUid());
//updating artist
Time artist = new Time(email,time);
dR.push().setValue(artist);
Toast.makeText(getApplicationContext(), "Time is updated", Toast.LENGTH_LONG).show();
return true;
}
};
}
};
adapter.notifyDataSetChanged();
timeTable.setAdapter(adapter);
}
}
Thanks for help! I'm newbie in Android need pro to help me up! Thanks guys and this platform!
First of all, why do you need to push() after Uid? As Uid is always unique, you can remove that to easily access the child node like below:
-Time
-UID
-email
-time
Problem 1: I store my data with ".push()" . How do I update the data on the random number that I declared with ".push()".
You need to get the reference of that particular node to update. For that, you can store the push Id along with email and time. Also, you don't need to push while updating.
private boolean updateArtist(String email, String time) {
//getting the specified artist reference
DatabaseReference dR = FirebaseDatabase.getInstance().getReference("Time").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child(timeModel.getPushId());
//updating artist
Time artist = new Time(email,time);
dR.updateChildren(artist); // instead use updateChildren
Toast.makeText(getApplicationContext(), "Time is updated", Toast.LENGTH_LONG).show();
return true;
}
Once I delete the data will delete whole data but not only the specific data
Same goes for problem 2, you need that particular node to delete,
private boolean deleteArtist(String email, String time) {
//getting the specified artist reference
DatabaseReference dR = FirebaseDatabase.getInstance().getReference("Time").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child(timeModel.getPushId());
//removing artist
dR.getRef().removeValue();
Toast.makeText(getApplicationContext(), "Time is deleted", Toast.LENGTH_LONG).show();
return true;
}
I have chat fragment which is backed by recyclerview. When i enter chat screen manually to send or receive message its work fine. but when i closed app and enter app from message notification, Its sending message to other user but not updating own recyclerview.
Chat Fragment
public class Chat_Screen_Fragment extends Fragment implements View.OnClickListener, ChildEventListener{
public static final String TAG = "###CHAT SCREEN###";
List<Chat_Wrapper> message = new ArrayList<>();
Chat_Adapter adapter;
RecyclerView recyclerView;
LinearLayoutManager layoutManager;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View v=inflater.inflate(R.layout.chat_screen_main_fragment,container,false);
setRetainInstance(true);
// GET INTENT VALUES FROM USER PROFILE CLASS
UserName_Intent = getArguments().getString("Get_Name");
UserImage_Intent = getArguments().getString("Get_Image");
UserPhone_Intent = getArguments().getString("Get_Phone");
UserID_Intent = getArguments().getString("Get_ID");
FirebaseToken_Intent = getArguments().getString("Get_Token"); //Firebase Token of other person
Room_Name_Intent = getArguments().getString("Get_Other"); // Room Name of chat
UserLastSeen_Intent=getArguments().getString("LastSeen");
//Sender_FCMToken = Session.getFirebaseID();
// RECYCLER VIEW
recyclerView = v.findViewById(R.id.Chat_Screen_Message_List);
layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setStackFromEnd(true);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(layoutManager);
databaseReference = FirebaseDatabase.getInstance().getReference().child(Room_Name_Intent);
databaseReference.addChildEventListener(this);
adapter = new Chat_Adapter(getActivity(), message);
recyclerView.setAdapter(adapter);
// FETCH OLD MESSAGE FROM DATABASE
chatDatabase();
return v;
}
// FIREBASE REAL TIME DATABASE WHICH FETCH ALL MESSAGES (SYNC) FROM ONLINE DATABASE
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
append_chat_conversation(dataSnapshot);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
append_chat_conversation(dataSnapshot);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
//THIS METHOD WILL FETCH ADD ALL MESSAGES FROM FIREBASE DATABASE AND ALSO SEARCH RESULT
private synchronized void append_chat_conversation(DataSnapshot dataSnapshot) {
iterator = dataSnapshot.getChildren().iterator();
while (iterator.hasNext()) {
// NOW GET ALL DATA FROM FIREBASE DATABASE AND SAVE IT INTO STRINGS THEN CHECK EACH BY ITS MESSAGE TYPE
Chat_Msg = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_FROM = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_FCM_TO= (String) ((DataSnapshot) iterator.next()).getValue();
Chat_Database tempChatDatabase = new Chat_Database(getActivity());
boolean hasValue=tempChatDatabase.CheckValueExist(_ID);
if (!hasValue) {
Log.d(TAG,"Chat Message "+Chat_Msg);
long id=chat_database.Insert_Chat(Session.getUserID(),Room_Name_Intent, UserID_Intent, "Text", Chat_Msg, Chat_FROM, Chat_TO, Chat_TimeStamp, Chat_FCM_FROM, Chat_FCM_TO, Session.getPhoneNO(), UserPhone_Intent,Random_ID,UserImage_Intent,UserLastSeen_Intent,Chat_FROM_ID);
//Adding Chat Data Into Database
Log.d(TAG,"Database Entry ID "+id);
if (id==0){
Log.d(TAG,"Database Already Has Value Of This Random Id ");
return;
}
Chat_Wrapper chat_wrapper = new Chat_Wrapper(Chat_Msg, null, null, null, null, null, null, Chat_TimeStamp, User_Intent, UserImage_Intent, Chat_FROM, null,null,id);
message.add(chat_wrapper);
adapter.notifyDataSetChanged();
Log.d(TAG, "FIREBASE STORAGE PHOTO-3-MESSAGE ARRAY SIZE " + message.size());
recyclerView.post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(adapter.getItemCount());
}
});
}
}
}
Log.d(TAG, "MESSAGE ARRAY SIZE " + message.size());
tempChatDatabase.isDatabaseClose();
}
adapter.notifyDataSetChanged();
recyclerView.post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(message.size()-1);
//recyclerView.smoothScrollToPosition(adapter.getItemCount());
}
});
}
private void chatDatabase(){
//Database Init and Filling Adapter
Log.d(TAG,"Chat Database Function");
chat_database=new Chat_Database(getActivity());
chatCursor=chat_database.getUserChat(UserID_Intent);
boolean checkDB_Exist=functions.DatabaseExist(getActivity(),"CHAT_DATABASE.DB");
boolean chatItemsCounts=chatCursor.getCount()>0;
chatCursor.moveToFirst();
Log.d(TAG,"Value At Chat Database "+ checkDB_Exist+" "+chatItemsCounts);
if (checkDB_Exist && chatCursor.getCount()>0 && chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID")).equals(UserID_Intent)){
Log.d(TAG,"Database Exist Chat Database");
message.clear();
chatCursor.moveToFirst();
do {
database_rowID=chatCursor.getInt(chatCursor.getColumnIndex("ID"));
database_userID=chatCursor.getString(chatCursor.getColumnIndex("USER_ID"));
database_RoomName =chatCursor.getString(chatCursor.getColumnIndex("ROOM_NAME"));
database_ReceiverID=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID"));
database_MessageType=chatCursor.getString(chatCursor.getColumnIndex("MESSAGE_TYPE"));
database_Message=chatCursor.getString(chatCursor.getColumnIndex("USER_MESSAGE"));
database_MsgFrom=chatCursor.getString(chatCursor.getColumnIndex("SENDER_NAME"));
database_MsgTo=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_NAME"));
database_TimeStamp=chatCursor.getString(chatCursor.getColumnIndex("TIME_STAMP"));
database_FCMfrom=chatCursor.getString(chatCursor.getColumnIndex("SENDER_TOKEN"));
database_FCMto=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_TOKEN"));
database_LocalPath=chatCursor.getString(chatCursor.getColumnIndex("DOWNLOADED_AT"));
database_PhoneFrom=chatCursor.getString(chatCursor.getColumnIndex("MY_PHONE"));
database_PhoneTo=chatCursor.getString(chatCursor.getColumnIndex("OTHER_PHONE"));
Log.d(TAG,"Value Of Database Message String = "+database_Message);
Log.d(TAG,"Row ID of Database "+database_rowID);
// Check Message Type
Log.d(TAG,"Message Type Is Text");
Chat_Wrapper text = new Chat_Wrapper(database_Message, null, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom,null,null,database_rowID);
message.add(text);
}
while(chatCursor.moveToNext());
Room_Name_Intent = database_RoomName;
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
adapter.notifyDataSetChanged();
chatCursor.close();
boolean value = chat_database.isDatabaseClose();
recyclerView.post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(message.size()-1);
}
});
Log.d(TAG,"Value Of Database Close or Not "+value);
}
}
}
Chat Adapter
public class Chat_Adapter extends RecyclerView.Adapter<Chat_Adapter.ViewHolder> {
public static final String TAG="###CHAT_ADAPTER###";
private Context context;
Chat_Database database;
Chat_Wrapper chat_wrapper;
public Chat_Adapter(Context context, List<Chat_Wrapper> message) {
this.context = context;
this.arrayList_message = message;
}
#Override
public Chat_Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View Layout;
Log.d(TAG,"On Create View Holder Calling ");
if (viewType==1){
Log.d(TAG,"View Tyoe Is "+viewType);
Layout=LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_screen_message_item,parent,false);
// ImagePath=Session.getUserImage();
}
else {
Log.d(TAG,"View Type Is "+viewType);
Layout=LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_screen_message_item_other,parent,false);
// ImagePath=chat_wrapper.getImageView();
}
return new ViewHolder(Layout);
}
#Override
public void onBindViewHolder(final Chat_Adapter.ViewHolder holder, final int position) {
chat_wrapper=arrayList_message.get(position);
database=new Chat_Database(context);
holder.Message.setVisibility(View.VISIBLE);
holder.TimeStamp.setVisibility(View.VISIBLE);
holder.User_Image.setVisibility(View.VISIBLE);
//CHECK SENDER IS SAME AS LOGGED IN USER
if ((Session.getUserFname()+" "+Session.getUserLname()).equals(chat_wrapper.getSender_UserName())){
ImagePath=Session.getUserImage();
Log.d(TAG,"Session.getUserImage() "+Session.getUserImage());
Log.d(TAG,"Value Of Message Running ImagePath "+ImagePath);
}
else {
//String filePath="/data/data/com.boysjoys.com.pro_working1/my_picture.jpg"
ImagePath=chat_wrapper.getImageView();
Log.d(TAG,"Value Of Message Running ImagePath "+ImagePath);
}
holder.Message.setText(chat_wrapper.getMessage());
holder.TimeStamp.setText(chat_wrapper.getTimestamp());
holder.TimeStamp.setVisibility(View.GONE);
Glide.with(MyApplication.getmContext())
.load(ImagePath)
.apply(new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.fitCenter().skipMemoryCache(false))
.into(holder.User_Image);
//Make TimeStamp Visible or Hidden
holder.Message.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.d(TAG,"On Click Text View Message");
if (isShowing){
holder.TimeStamp.setVisibility(View.VISIBLE);
Log.d(TAG,"Is Showing Is True");
isShowing=false;
}
else {
holder.TimeStamp.setVisibility(View.GONE);
isShowing=true;
}
}
});
}
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemCount() {
Log.d(TAG,"GET ITEM COUNT--Array Message List Size "+arrayList_message.size());
return arrayList_message.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView Message;
TextView TimeStamp;
ImageView User_Image;
public ViewHolder(View itemView) {
super(itemView);
Log.d(TAG,"View Holder Constructor Calling. Now Inflating Layout Items");
Message = itemView.findViewById(R.id.Single_Item_Chat_Message);
TimeStamp = itemView.findViewById(R.id.Single_Item_Chat_TimeStamp);
User_Image = itemView.findViewById(R.id.Single_Item_Chat_ImageView);
}
}
Whenever i received message as notification after tapping on notification it open same chat screen (Load all previous message first) and when i click on send button to send message it send to other user but not updating my recyclerview.
UPDATE: I just found the issue which is fetching earlier messages from local database when i comment chatDatabase() function it works fine. but still not able to fix the issue.
When you are appending a new item to the list in append_chat_conversation method, you are not notifying the adapter about it. I don't know how that framework works but you should notify the adapter about any change in the dataset by using the notifyDataSetChaged on the adapter or better using the specific inserted/changed/deleted notify methods for better performance.
Why don't you call
adapter.notifyDataSetChanged();
after
Chat_Wrapper chat_wrapper = new Chat_Wrapper(Chat_Msg, null, null, null, null, null, null, Chat_TimeStamp, User_Intent, UserImage_Intent, Chat_FROM, null,null,id);
message.add(chat_wrapper);
as
notifyDataSetChanged only works if you use the add(), insert(), remove(), and clear() on the Adapter.
On my onBindViewHolder I have this to set the setImageResource
holder.card_image.setImageResource(image);
But my items can be purchased so, I have this to purchase on my holder.view.setOnClickListener()
bp.purchase((Activity) mContext,model.getProduct_id());
so, it goes to this method :
bp = new BillingProcessor() new BillingProcessor.IBillingHandler() {
#Override
public void onProductPurchased(#NonNull String productId, #Nullable TransactionDetails details) {
showToast("onProductPurchased: " + productId);
//Purchased OK
//WANT TO CHANGE THE IMAGE ONCE PURCHASE IS OK
}
#Override
public void onBillingError(int errorCode, #Nullable Throwable error) {
showToast("onBillingError: " + Integer.toString(errorCode));
}
#Override
public void onBillingInitialized() {
showToast("onBillingInitialized");
readyToPurchase = true;
}
#Override
public void onPurchaseHistoryRestored() {
showToast("onPurchaseHistoryRestored");
for(String sku : bp.listOwnedProducts())
Log.d("skuProducts", "Owned Managed Product: " + sku);
for(String sku : bp.listOwnedSubscriptions())
Log.d("skuProducts", "Owned Subscription: " + sku);
}
});
How do I change it if I'm not onBindViewHolder?
My adapter looks like :
FirebaseRecyclerAdapter adapter = new FirebaseRecyclerAdapter< CardPOJO, CardHolder>(options) {
#Override
public CardHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//inflate the single recycler view layout(item)
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_product, parent, false);
int width = parent.getMeasuredWidth() / 2;
width -= mContext.getResources().getDimensionPixelSize(R.dimen._8sdp);
final CardHolder cardViewHolder = new CardHolder(view,width);
return cardViewHolder;
}
#Override
public void onDataChanged() {
super.onDataChanged();
tv.setVisibility(getItemCount() == 0 ? View.VISIBLE : View.GONE);
}
#Override
protected void onBindViewHolder(CardHolder holder, int position, final CardPOJO model) {
holder.state.setText(model.getState());
holder.cardName.setText(model.getName());
switch (model.getState()){
case "free":
//Img free
break;
case "not_free":
//Img not free
break;
default:
break;
}
holder.view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(model.getState().equals("free")){
//stuff
}
else{
//stuff
}
root_ref.child("PurchasedProducts").child(currentuser).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
bp.purchase((Activity) mContext,model.getProduct_id()); //HERE I CALL THE PURCHASE SO IF IT'S OK I WANT TO DO SOMETHING LIKE holder.card_image.setImageResource(image);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
});
}
};
adapter.startListening();
products_recycler.setAdapter(adapter);
If I assume correctly you want to change the view appearance or some image change if some payment is done successful or failed.
for that, you can have a callback which will give you the item position in activity or fragment back from there you can make a server call to make the purchase happen and if everything goes well.
when you make your adapter constructor pass the callback
final SomeAdapter obj = new SomeAdapter(this,new Callback(){
#Override
onPaymentRequested(int position, View view){
//this will get called when you press click on image in bindviewholder
bp = new BillingProcessor() new BillingProcessor.IBillingHandler() {
#Override
public void onProductPurchased(#NonNull String productId, #Nullable TransactionDetails details) {
showToast("onProductPurchased: " + productId);
//Purchased OK
adapterModelList.get(position).setPayment(true);
obj.notifyDataSetChanged();
}
#Override
public void onBillingError(int errorCode, #Nullable Throwable error) {
showToast("onBillingError: " + Integer.toString(errorCode));
}
#Override
public void onBillingInitialized() {
showToast("onBillingInitialized");
readyToPurchase = true;
}
#Override
public void onPurchaseHistoryRestored() {
showToast("onPurchaseHistoryRestored");
for(String sku : bp.listOwnedProducts())
Log.d("skuProducts", "Owned Managed Product: " + sku);
for(String sku : bp.listOwnedSubscriptions())
Log.d("skuProducts", "Owned Subscription: " + sku);
}
});
}
});
recyclerView.setAdapter(obj);
so when you call your obj.notifyDataSetChanged(); it will make the adapter to draw all views again where you can set some flag according to int position recieved for click callback and make it change accordingly.
Edit=>07/12/2018: Tried the Firebase Adapter and made few changes since the code was not enough to replicate the scenario but I have made a sample class made few changes but the basic idea is like below.
1: When user click on view in onBindViewHolder we receive a callback which gives a position parameter in fragment or activity from where we are calling
2: Now we process the payment and when we are done we make a change in Database firebase also by updating the CardPojo to server for that particular user item.
3: while we update the CardPojo on server we also set a flag in card pojo which is a boolean for paymentSuccess which will be true when payment is done.
4: since our payment is done and is synced with server with new flag data now we can just call firebaseRecycler.notifyItemChanged(position); which will get the lates update from the server for that particular position which we have received on callback.
5: Now populateViewHolder() gives you a cardpojo object you can check if payment is done then you can change the image
so here is the sample code involved I have tried to match the scenario at best, hope you understand what I am trying to do here.
so first create a listener or a callback
public interface CallBackInterface {
void onClick(int position,CardPOJO cardPOJO);
}
now instead of initializing the FirebaseRecyclerAdapter in activity or fragment just create a class and extend it this separates your ui logic and gives us the extensibility of doing extra things like adding callback.
public class FirebaseRecycler extends FirebaseRecyclerAdapter<CardPOJO,CardHolder> {
CallBackInterface callBackInterface;
public FirebaseRecycler(Class<CardPOJO> modelClass, int modelLayout, Class<CardHolder> viewHolderClass, DatabaseReference ref) {
super(modelClass, modelLayout, viewHolderClass, ref);
this.callBackInterface = callBackInterface;
}
public FirebaseRecycler(Class<CardPOJO> modelClass, int modelLayout, Class<CardHolder> viewHolderClass, Query ref) {
super(modelClass, modelLayout, viewHolderClass, ref);
this.callBackInterface = callBackInterface;
}
#Override
public CardHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//your inflater logic goes here
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_product, parent, false);
CardHolder cardHolder = new CardHolder(view);
return cardHolder;
}
#Override
protected void populateViewHolder(CardHolder viewHolder, final CardPOJO model, final int position) {
//your populate logic
//your existing code here
if (model.isPaymentDone){
//set payment success image holder.card_image.setImageResource(image);
}else{
//set payment failure image
}
//setting the card click listener
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//we have the card click listener, we will start the payment processing in activity
callBackInterface.onClick(position,model);
}
});
}
public void setCallBackInterface(CallBackInterface callBackInterface) {
this.callBackInterface = callBackInterface;
}
}
now almost everything is done we need to call this Custom Firebase adapter and pass the required things and it will do its job.
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final DatabaseReference mDatabaseRef = FirebaseDatabase.getInstance().getReference();
/*
if you have any other database child then you can refer to it using
DatabaseReference child = mDatabaseRef.child("yourchilddatabase");
and pass this to the last argument
*/
final FirebaseRecycler firebaseRecycler = new FirebaseRecycler(CardPOJO.class, R.layout.card_product, CardHolder.class, mDatabaseRef);
firebaseRecycler.setCallBackInterface(new CallBackInterface() {
#Override
public void onClick(final int position, final CardPOJO cardPOJO) {
//start processing the payment
bp = new BillingProcessor() new BillingProcessor.IBillingHandler() {
#Override
public void onProductPurchased(#NonNull String productId, #Nullable TransactionDetails details) {
/**
*when you have processed the payment just enable the flag on server database by having a extra boolean flag for this
* and check in onBindViewHolder if this is enabled if so then replace your image
* updating the values on server, you can handle it according to your user case
*/
cardPOJO.setPaymentDone(true);
mDatabaseRef.push().setValue(cardPOJO);
firebaseRecycler.notifyItemChanged(position);
}
#Override
public void onBillingError(int errorCode, #Nullable Throwable error) {
//existing logic
}
#Override
public void onBillingInitialized() {
//existing logic
}
#Override
public void onPurchaseHistoryRestored() {
//existing logic
}
};
}
});
}
this demonstrates the basic logic you can patch it according to your requirement.
Get your item from RecyclerView's adapter and edit it. Then just call Adapter.onItemChanged(int position), this will cause to call onBindViewholder to be called specifically for that position.
PostListFragment is extended by other fragments in my app. I need the uid of the current user, but it always returns null. When I try to run my app, I always get the error:
FATAL EXCEPTION: main
Process: com.example.cleeg.squad, PID: 8524
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.google.firebase.auth.FirebaseUser.getUid()' on a null object reference
at com.example.cleeg.squad.fragments.PostListFragment.getUid(PostListFragment.java:162)
at com.example.cleeg.squad.fragments.MyPostsFragment.getQuery(MyPostsFragment.java:19)
at com.example.cleeg.squad.fragments.PostListFragment.onActivityCreated(PostListFragment.java:76)
I've tried to find out why this is online, but I just get more confused and I don't really know how to fix it.
The function getUid() is at the bottom of the code.
public abstract class PostListFragment extends Fragment {
private static final String TAG = "PostListFragment";
private DatabaseReference mDatabaseReference;
private FirebaseRecyclerAdapter<Post, PostViewHolder> mAdapter;
private RecyclerView mRecycler;
private LinearLayoutManager mManager;
public PostListFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(R.layout.fragment_all_posts, container, false);
mDatabaseReference = FirebaseDatabase.getInstance().getReference();
mRecycler = (RecyclerView) rootView.findViewById(R.id.messages_list);
mRecycler.setHasFixedSize(true);
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Set up Layout Manager, reverse layout
mManager = new LinearLayoutManager(getActivity());
mManager.setReverseLayout(true);
mManager.setStackFromEnd(true);
mRecycler.setLayoutManager(mManager);
// Set up FirebaseRecyclerAdapter with the Query
Query postsQuery = getQuery(mDatabaseReference);
mAdapter = new FirebaseRecyclerAdapter<Post, PostViewHolder>(Post.class, R.layout.item_post,
PostViewHolder.class, postsQuery) {
#Override
protected void populateViewHolder(final PostViewHolder viewHolder, final Post model, final int position) {
final DatabaseReference postRef = getRef(position);
// Set click listener for the whole post view
final String postKey = postRef.getKey();
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Launch PostDetailActivity
Intent intent = new Intent(getActivity(), PostDetailActivity.class);
intent.putExtra(PostDetailActivity.EXTRA_POST_KEY, postKey);
startActivity(intent);
}
});
// Determine if the current user has liked this post and set UI accordingly
if (model.stars.containsKey(getUid())) {
viewHolder.starView.setImageResource(R.drawable.ic_toggle_star_24);
} else {
viewHolder.starView.setImageResource(R.drawable.ic_toggle_star_outline_24);
}
// Bind Post to ViewHolder, setting OnClickListener for the star button
viewHolder.bindToPost(model, new View.OnClickListener() {
#Override
public void onClick(View starView) {
// Need to write to both places the post is stored
DatabaseReference globalPostRef = mDatabaseReference.child("posts").child(postRef.getKey());
DatabaseReference userPostRef = mDatabaseReference.child("user-posts").child(model.uid).child(postRef.getKey());
// Run two transactions
onStarClicked(globalPostRef);
onStarClicked(userPostRef);
}
});
}
};
mRecycler.setAdapter(mAdapter);
}
private void onStarClicked(DatabaseReference postRef) {
postRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
Post p = mutableData.getValue(Post.class);
if (p == null) {
return Transaction.success(mutableData);
}
if (p.stars.containsKey(getUid())) {
// Unstar the post and remove self from stars
p.starCount = p.starCount - 1;
p.stars.remove(getUid());
} else {
// Star the post and add self to stars
p.starCount = p.starCount + 1;
p.stars.put(getUid(), true);
}
// Set value and report transaction success
mutableData.setValue(p);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.d(TAG, "postTransaction:onComplete:" + databaseError);
}
});
}
#Override
public void onDestroy() {
super.onDestroy();
if (mAdapter != null) {
mAdapter.cleanup();
}
}
public String getUid() {
return FirebaseAuth.getInstance().getCurrentUser().getUid();
}
public abstract Query getQuery(DatabaseReference databaseReference);
}
The crash is because of no user is linked, i.e., getCurrentUser() is null. Please make user you have the user before fetching the userid.
if (FirebaseAuth.getInstance().getCurrentUser() != null) {
mUserID = FirebaseAuth.getInstance().getCurrentUser().getUid();
} else {
//login or register screen
}
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
// User is authenticated and now you can access uesr's properties as followings
mUserID = user.getUid();
} else {
// User is authenticated. So, let's try to re-authenticate
AuthCredential credential = EmailAuthProvider
.getCredential("user#example.com", "password1234");
// Prompt the user to re-provide their sign-in credentials
user.reauthenticate(credential)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Log.d(TAG, "User re-authenticated.");
}
});
}
You can get details on it in this firebase document: https://firebase.google.com/docs/auth/android/manage-users