sejn
sejn

Reputation: 2644

How to receive the chat message in real time using the recyclerview in android java

I have implemented chat view list using the recyclerview. But the list not showing the message to the receiver in real time.

If I send a message, the receiver needs to receive the message in real time. How can I make this to work.

I don't have an idea about the broadCastReceiver. Here I am working with the chat screen. Need to show the chat message in my app.

Issue is if a user sends me a message, I can't able to see the message. I need to receive the message via the recyclerview.

In My chatFragment:

  private RecyclerView mChatMessageRecyclerView;
    
     @Override
        public void onResume() {
            super.onResume();
            IntentFilter filter = new IntentFilter("Message_send");
            registerForContextMenu(mChatMessageRecyclerView);
        }
    
        @Override
        public void onPause() {
            super.onPause();
            unregisterForContextMenu(mChatMessageRecyclerView);
        }
    
        private final BroadcastReceiver mReceiverRecyclerview = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                mAdapter.notifyDataSetChanged();
                mAdapter.notifyItemInserted(getItemCount() + 1);
                mChatMessageRecyclerView.smoothScrollToPosition(getItemCount() + 1);
            }
        };
    
     LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
            
     @Override
        public void onViewCreated(View v, @Nullable Bundle b) {
            super.onViewCreated(v, b);
    linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
            linearLayoutManager.setStackFromEnd(true);
            linearLayoutManager.setSmoothScrollbarEnabled(true);
            linearLayoutManager.setReverseLayout(false);
         mChatMessageRecyclerView.setLayoutManager(linearLayoutManager);
            LocalBroadcastReceiver br = new LocalBroadcastReceiver() {
                @Override
                public void onReceiveIntent(@NonNull Context context, @NonNull Intent intent) {
                    mAdapter.notifyDataSetChanged();
                }
            };
            mChatMessageRecyclerView.getLayoutManager().smoothScrollToPosition(mChatMessageRecyclerView, new RecyclerView.State(), mChatMessageRecyclerView.getAdapter().getItemCount());
            
            int newMsgPosition = mAdapter.getItemCount();
            mChatMessageRecyclerView.setHasFixedSize(true);
            mChatMessageRecyclerView.setAdapter(mAdapter);
    }
    
     mSendMsg.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    String msgContent = mSendMsgInputBox.getText().toString();
    
                    if(!TextUtils.isEmpty(msgContent))
                    {
                        execute(new PostMessageRequest(message, Chatmessage.getmId(),

mPostMessageListener);
                    mSendMsgInputBox.setText("");
                }
            }
        });

My Adapter:

       private static final int VIEW_TYPE_MESSAGE_SENT = 1;
        private static final int VIEW_TYPE_MESSAGE_RECEIVED = 2;
        private List<MyMessageModel> msgDtoList; 
        MyMessageModel msgDto = this.msgDtoList.get(position);


         @Override
    public int getItemViewType(int position) {
        MyMessageModel message = (MyMessageModel) msgDtoList.get(position);

        if (message.getId().equals(Profile.getId())) {
            // If the current user is the sender of the message
            return VIEW_TYPE_MESSAGE_SENT;
        } else {
            // If some other user sent the message
            return VIEW_TYPE_MESSAGE_RECEIVED;
        }
    }

 @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;

        if (viewType == VIEW_TYPE_MESSAGE_SENT) {
            view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.sender_message_layout, parent, false);
            return new SenderMessageHolder(view);

        } else if (viewType == VIEW_TYPE_MESSAGE_RECEIVED) {
            view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.receiver_message_layout, parent, false);
            return new ReceiverMessageHolder(view);
        }
    return null;
    }
             public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
                LastMessageContent message = (LastMessageContent) msgDtoList.get(position);

                switch (holder.getItemViewType()) {
                    case VIEW_TYPE_MESSAGE_SENT:
                        populateSentViewHolder(holder, position);   break;
                    case VIEW_TYPE_MESSAGE_RECEIVED:
                        populateReceivedViewHolder(holder, position);

                }

 @SuppressLint("ResourceAsColor")
    private void populateSentViewHolder(RecyclerView.ViewHolder holder, int position) {
        MyMessageModal msgDto = this.msgDtoList.get(position);
        ((SenderMessageHolder) holder).rightMsgTextView.setText(msgDto.getmMSg());
}

 @SuppressLint("ResourceAsColor")
    private void populateReceivedViewHolder(RecyclerView.ViewHolder holder, int position) {
        MyMessageModal msgDto = this.msgDtoList.get(position);
        ((ReceiverMessageHolder) holder).leftMsgTextView.setText(msgDto.getmMSg());
}

Upvotes: 0

Views: 2027

Answers (2)

furkan
furkan

Reputation: 542

In order to display real-time data on your RecycleView, there are two things you need to implement:

  1. Your data source which listens to the real time data. This could be coming from e.g. WebSockets, Firebase, some message queue, etc.
  2. Then you need to notify your adaptor about the incoming messages which then updates your RecycleView. You would like to update the RecycleView on only with the new messages and not the entire list each time a message is received. For that you would use the ListAdaptor and DiffUtil.ItemCallback. Here is a nice tutorial explaining how to manage RecycleView updates (explained with Kotlin): https://developer.android.com/codelabs/kotlin-android-training-diffutil-databinding/#0

Upvotes: 1

user15397738
user15397738

Reputation:

follow this step.

Add these to your app-module build.Gradle file:

 dependencies {
    // [...]

    implementation 'com.pusher:pusher-java-client:1.8.0'
    implementation 'com.android.support:recyclerview-v7:28.0.0'
}

Open the activity_main.xml generated for you replace the contents with the following:

 <?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerViewContents"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:textSize="16sp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_margin="16dp"
        android:background="@drawable/rounded_corner"
        android:textColor="@android:color/black"
        android:visibility="gone"
        android:layout_gravity="right|bottom"
        android:id="@+id/textViewNewContents" />

</FrameLayout>

**Create a new drawable named rounded_corner and paste this:**

  <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <stroke
        android:width="1dp" />

    <solid android:color="#ffffff" />

    <padding
        android:left="1dp"
        android:right="1dp"
        android:bottom="1dp"
        android:top="1dp" />

    <corners android:radius="5dp" />
</shape>

Next, let us create an adapter for the recycler view. Create a new class called RecyclerListAdapter and paste this:

  import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView

class RecyclerListAdapter(private val listener:OnLastPositionReached): RecyclerView.Adapter<RecyclerListAdapter.ViewHolder>() {

  private val contentList: ArrayList<String> = ArrayList()

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    return ViewHolder(
       LayoutInflater
         .from(parent.context)
         .inflate(android.R.layout.simple_list_item_1, parent, false)
    )
  }

  override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.bind(contentList[position])

    if (position == contentList.size-1){
      listener.lastPositionReached()
    } else {
      listener.otherPosition()
    }
  }

  override fun getItemCount(): Int = contentList.size

  fun addItem(item:String) {
    contentList.add(item)
    notifyDataSetChanged()
  }

  inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val userName: TextView = itemView.findViewById(android.R.id.text1)

    fun bind(item: String) = with(itemView) {
      userName.text = item
    }
  }

  interface OnLastPositionReached {
    fun lastPositionReached()
    fun otherPosition()
  }
}

Finally, open the MainActivity and set it up like so:

   import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.view.View
import com.pusher.client.Pusher
import com.pusher.client.PusherOptions
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONObject

class MainActivity : AppCompatActivity(), RecyclerListAdapter.OnLastPositionReached {

  private var count = 0
  private val recyclerListAdapter = RecyclerListAdapter(this)

  private var lastPosition = false
  override fun otherPosition() {
    lastPosition = false
  }

  override fun lastPositionReached() {
    lastPosition = true
    textViewNewContents.visibility = View.GONE
    count = 0
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setupClickListeners()
    setupRecyclerView()
    setupPusher()
  }
}

When the textview that shows the count of the new messages is clicked, it scrolls down immediately to recent messages, set the count to 0 and hides the text view.

The next method is the setupRecyclerView method. Set it up like this:

 private fun setupRecyclerView() {
  with(recyclerViewContents){
    layoutManager = LinearLayoutManager(this@MainActivity)
    adapter = recyclerListAdapter
  }

  recyclerListAdapter.addItem("Hello World")
  recyclerListAdapter.addItem("New article alert!")
  recyclerListAdapter.addItem("Pusher is actually awesome")
  recyclerListAdapter.addItem("Realtime functionality freely provided ")
  recyclerListAdapter.addItem("Checkout pusher.com/tutorials")
  recyclerListAdapter.addItem("You can also checkout blog.pusher.com")
  recyclerListAdapter.addItem("Learn how to update contents properly ")
  recyclerListAdapter.addItem("Hello World")
  recyclerListAdapter.addItem("New article alert!")
  recyclerListAdapter.addItem("Pusher is actually awesome")
  recyclerListAdapter.addItem("Realtime functionality freely provided ")
  recyclerListAdapter.addItem("Checkout pusher.com/tutorials")
  recyclerListAdapter.addItem("You can also checkout blog.pusher.com")
  recyclerListAdapter.addItem("Learn how to update contents properly ")
}

The next method is the setupPusher method. Set it up like so:

private fun setupPusher() {
  val options = PusherOptions()
  options.setCluster("PUSHER_CLUSTER")

  val pusher = Pusher("PUSHER_KEY", options)
  val channel = pusher.subscribe("my-channel")

  channel.bind("my-event") { channelName, eventName, data ->
    runOnUiThread {  
      if (!lastPosition){
        count ++
        textViewNewContents.text = count.toString()
        textViewNewContents.visibility = View.VISIBLE
        recyclerListAdapter.addItem(JSONObject(data).getString("message"))    
      } else {
        recyclerListAdapter.addItem(JSONObject(data).getString("message"))
        recyclerViewContents.scrollToPosition(recyclerListAdapter.itemCount-1)
      } 
    }
  }

  pusher.connect()
}

now it's visible.

Upvotes: 0

Related Questions