0

I am creating an android app that is going to host a chat with my Chatbot. I have created the Chatbot in Dialogflow and i have already integrated with android studio.

In order to create the Interface i have created three XML files.

1) activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:orientation="vertical"
tools:context=".MainActivity"
tools:showIn="@layout/activity_main">

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:layout_above="@id/inputLayout"/>
<RelativeLayout
    android:id="@+id/inputLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:background="@android:color/white"
    android:gravity="bottom"
    android:paddingBottom="9dp"
    android:paddingRight="8dp"
    android:paddingLeft="8dp"
    android:paddingTop="8dp">

    <ImageView
        android:id="@+id/sendBtn"
        android:layout_width="49dp"
        android:layout_height="28dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_alignBottom="@+id/queryEditText"
        android:layout_marginBottom="20dp"
        android:contentDescription="This is the button to send your 
message!"
        android:paddingTop="4dp"
        app:srcCompat="@drawable/chatbot_send_btn" />

    <EditText
        android:id="@+id/queryEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:layout_toStartOf="@+id/sendBtn"
        android:layout_toLeftOf="@+id/sendBtn"
        android:hint="Aa"
        android:imeOptions="actionSend"
        android:inputType="textMultiLine"
        android:paddingTop="4dp"
        android:textSize="18sp" />

</RelativeLayout>


</RelativeLayout>

Here we are creating the input EditText and the ImageView for sending the message. And then inside a scrollview, a LinearLayout chatLayout that is going to host all the chat bubbles.

2) bot_msg_layout

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/yo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp">

<TextView
    android:id="@+id/timestamp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="left"
    android:text="time"
    android:textSize="13dp"
    android:visibility="invisible"
    />

<LinearLayout
    android:id="@+id/bot_msg_layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="start|center_vertical"
    android:layout_marginTop="12dp"
    android:layout_marginBottom="4dp"
    android:layout_marginEnd="16dp"
    android:layout_marginRight="16dp"
    android:layout_marginStart="8dp"
    android:layout_marginLeft="8dp"
    android:background="@drawable/bot_chat_bubble"
    android:gravity="start|center_vertical"
    android:orientation="vertical">

    <TextView
        android:id="@+id/chatMessage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:padding="12dp"
        android:text="skereeeeeeeeeeee"
        android:textSize="18sp"
        android:clickable="true"/>

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp">

    </FrameLayout>

</LinearLayout>



</FrameLayout>

Here we are creating the interface for when the bot replies to the user and the answer appear to the textview.

3)user_msg_layout Which is exactly the same as the one above but with different positioning.

Finally my MainActivity code

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ImageView sendBtn = findViewById(R.id.sendBtn);
        queryEditText = findViewById(R.id.queryEditText);

        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendMessage(v);
            }
        });

        queryEditText.setOnKeyListener((view, keyCode, event) -> {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                switch (keyCode) {
                    case KeyEvent.KEYCODE_DPAD_CENTER:
                    case KeyEvent.KEYCODE_ENTER:
                        sendMessage(sendBtn);
                        return true;
                    default:
                        break;
                }
            }
            return false;
        });

        initV2Chatbot();

        messageList = new ArrayList<MsgClass>();
        mMessageRecycler = findViewById(R.id.recyclerview);
        mMessageAdapter = new MessageListAdapter(this, messageList);
        mMessageRecycler.setLayoutManager(new LinearLayoutManager(this));
        mMessageRecycler.setHasFixedSize(true);
        mMessageRecycler.setAdapter(mMessageAdapter);



    }

    private void initV2Chatbot(){
        try{
            //V2 api
            //Initializing the chatbot from json
            InputStream stream = 
getResources().openRawResource(R.raw.test_agent_credentials);
            GoogleCredentials credentials = 
GoogleCredentials.fromStream(stream);
            String projectId = 
((ServiceAccountCredentials)credentials).getProjectId();
            SessionsSettings.Builder settingsBuilder = 
SessionsSettings.newBuilder();
            sessionsClient = SessionsClient.create(sessionsSettings);
            session = SessionName.of(projectId, uuid);

        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

    private void sendMessage(View view){
        //Retrieving the query written by user
        String msg = queryEditText.getText().toString();
        //if queryEditText is empty show the warning!
        if(msg.trim().isEmpty()){
            Toast.makeText(MainActivity.this, "Please enter your query!", 
Toast.LENGTH_SHORT).show();
        }
        else{
            //adding user message
            MsgClass msg_To = new MsgClass(MsgClass.FROM_USER, msg, 
quickR);
            messageList.add(msg_To);
            int NewMsgPosition = messageList.size() - 1;
            // Notify recycler view insert one new data.
            mMessageAdapter.notifyItemInserted(NewMsgPosition);
            // Scroll RecyclerView to the last message.
            mMessageRecycler.scrollToPosition(NewMsgPosition);
            // Empty the input edit text box.
            queryEditText.setText("");
            // Java V2 sending query to cloud
            QueryInput queryInput =   
QueryInput.newBuilder().setText(TextInput.newBuilder()
.setText(msg).setLanguageCode("en")).build();
            new com.example.smokebot.RequestJavaV2Task(MainActivity.this, 
session, sessionsClient, queryInput).execute();
        }
    }

    public void callbackV2(DetectIntentResponse response) {
        quickR.removeAll(quickR);
        if(response != null){
            //process aiResponse here
            int responsesCount = 
response.getQueryResult().getFulfillmentMessagesCount();
            String botReply= "";
            QuickReplies hasQuickReply;
            //Checking if Fullfillment has more than one responses in 
order to view them all
            if(responsesCount >=2){
                //Looping through Fullfillment responses
                for(int i=0; i < responsesCount; i++){
                    Message botReplies = 
response.getQueryResult().getFulfillmentMessages(i);
                    //Counting the text amount of each response
                    int TextResponseCount = 
botReplies.getText().getTextCount();
                    for(int y=0; y < TextResponseCount; y++){

                        botReply += botReplies.getText().getText(y) + 
"\n";

                    }
                    //checking if response contains any QuickReply
                    hasQuickReply = 
response.getQueryResult().getFulfillmentMessages(i).getQuickReplies();
                    if(hasQuickReply.toString() != " "){
                        //counting the quick replies existing
                        int QuickRepliesCount = 
hasQuickReply.getQuickRepliesCount();
                        //Looping through quickreplies to fetch them all
                        for (int q = 0; q < QuickRepliesCount; q++){
                            //Updating the list with the new QuickReplies
                            quickR.add(hasQuickReply.getQuickReplies(q));
                            System.out.println(quickR);

                        }
                    }
                }
                MsgClass msg_To = new MsgClass(MsgClass.FROM_BOT, 
botReply, quickR);
                messageList.add(msg_To);
                int NewMsgPosition = messageList.size() - 1;
                // Notify recycler view insert one new data.
                mMessageAdapter.notifyItemInserted(NewMsgPosition);
                // Scroll RecyclerView to the last message.
                mMessageRecycler.scrollToPosition(NewMsgPosition);
            }
            //if response is only one
            else{
                botReply = response.getQueryResult().getFulfillmentText();
                MsgClass msg_To = new MsgClass(MsgClass.FROM_BOT, 
botReply, quickR);
                messageList.add(msg_To);
                int NewMsgPosition = messageList.size() - 1;
                // Notify recycler view insert one new data.
                mMessageAdapter.notifyItemInserted(NewMsgPosition);
                // Scroll RecyclerView to the last message.
                mMessageRecycler.scrollToPosition(NewMsgPosition);
            }
        }
        else{
            String ErrorMessage = "There was some communication issue. 
Please Try again!";
            MsgClass msg_To = new MsgClass(MsgClass.FROM_BOT, 
ErrorMessage, quickR);
            messageList.add(msg_To);
            int NewMsgPosition = messageList.size() - 1;
            // Notify recycler view insert one new data.
            mMessageAdapter.notifyItemInserted(NewMsgPosition);
            // Scroll RecyclerView to the last message.
            mMessageRecycler.scrollToPosition(NewMsgPosition);

        }
    }
}
  1. MessageListAdapter.java

     public class MessageListAdapter extends 
     RecyclerView.Adapter<RecyclerView.ViewHolder>{
    
     private Context mContext;
     private List<MsgClass> mMessageList;
     private static final int FROM_BOT = 0;
     private static final int FROM_USER = 1;
    
    
     public MessageListAdapter(Context context, List<MsgClass> messageList) {
     mContext = context;
     mMessageList = messageList;
     }
    
     // Determines the appropriate ViewType according to the sender of the 
     message.
     @Override
     public int getItemViewType(int position) {
     MsgClass msg_obj = this.mMessageList.get(position);
     if (MsgClass.FROM_BOT == msg_obj.getMsgType()) {
         // If the current user is the sender of the message
         return FROM_BOT;
     } else {
         // If some other user sent the message
         return FROM_USER;
     }
     }
    
    
     @Override
     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int 
     viewType) {
    
     View view;
    
     if (viewType == FROM_BOT) {
         view = LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.bot_msg_layout, parent, false);
         return new BotMessageHolder(view);
     } else if (viewType == FROM_USER) {
         view = LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.user_msg_layout, parent, false);
         return new UserMessageHolder(view);
     }
    
     return null;
     }
    
    
     @Override
     public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int 
     position) {
     MsgClass msg_obj = this.mMessageList.get(position);
     List<String> QR = msg_obj.getQuickR();
     if(getItemViewType(position) == FROM_BOT){
         BotMessageHolder botHolder = (BotMessageHolder) holder;
         botHolder.bind(msg_obj.getMsgContent());
         if(QR.size() != 0){
             addButtons(QR, botHolder);
         }
    
     }
     else{
         UserMessageHolder userHolder = (UserMessageHolder) holder;
         userHolder.bind(msg_obj.getMsgContent());
     }
     //        switch (holder.getItemViewType()) {
     //            case FROM_BOT:
     //                ((BotMessageHolder) 
     holder).bind(msg_obj.getMsgContent());
     //                break;
     //            case FROM_USER:
     //                
     //                ((UserMessageHolder) 
     holder).bind(msg_obj.getMsgContent());
     //            default:
     //                break;
     //        }
    
    
     }
    
     @Override
     public int getItemCount() {
     if(mMessageList==null)
     {
         mMessageList = new ArrayList<MsgClass>();
     }
     return mMessageList.size();
     }
    
     private void addButtons(List<String> QR, BotMessageHolder botHolder){
     int QRcount = QR.size();
     System.out.println(QRcount);
     LinearLayout ll = botHolder.bot_layout;
     LinearLayout.LayoutParams layout_params = new 
     LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, 
     LinearLayout.LayoutParams.MATCH_PARENT);
     layout_params.setMargins(4,4,4,4);
     layout_params.gravity = Gravity.CENTER_HORIZONTAL;
     //        int ViewCount = ll.getChildCount();
     //        View v = null;
     //        for(int x=0; x< ViewCount; x++){
     //            v = ll.getChildAt(x);
     //            if(v instanceof Button){
     //                if(v.getTag().equals(1) || v.getTag().equals(2) || 
     v.getTag().equals(3)){
     //                       ll.removeView(v);
     //                }
     //            }
     //        }
    
     for (int i = 0; i< QRcount; i ++){
         Button qr_btn = new Button(mContext);
         qr_btn.setId(i);
         qr_btn.setTag(i);
         qr_btn.setText(QR.get(i));
         qr_btn.setBackgroundColor(Color.rgb(255,255,255));
    
         ll.addView(qr_btn);}
    
     }
     }
    
  2. UserMessageHolder.java

     public class UserMessageHolder extends RecyclerView.ViewHolder {
    
     TextView user_msg;
     LinearLayout user_layout;
    
    
     UserMessageHolder(View itemView) {
     super(itemView);
     if(itemView != null){
    
         user_msg = itemView.findViewById(R.id.chatMessage);
         user_layout = itemView.findViewById(R.id.user_msg_layout);
    
     }
    
     }
     void bind(String message){
     user_msg.setText(message);}
    
     }
    

6)BotMessageHolder.java Same but for the bot references.

The thing is that i am fetching Quick Replies from Cloud and i want to view them as Buttons in order for the user to just click them. I call the method addButtons(List QR, BotMessageHolder botHolder) to do this job inside onBindViewHolder(). the first answers appear correct like buttons but then they stack on each other. I will attach some images for you to understand what i am saying. This one, thats the first quick replies seems fine. They are on the right bubble.

But when i scroll on the first bot message they exist there too!

And when the bot message with the next quick replies comes, it just stack onto the others and on other previous bot bubbles not just the current. Also seems everytime i am scrolling they get more.. I cannot understand..

  • The first part of code content_main.xml is not so useful for this question and i am sorry for that. May skip it.. – Panagiotis Jun 18 '20 at 20:00
  • Does this answer your question? [How to Programmatically Add Views to Views](https://stackoverflow.com/questions/2395769/how-to-programmatically-add-views-to-views) – Ryan M Jun 19 '20 at 01:18
  • I would suggest a different approach in your aim .if you dont mind ,use recycleview and an arraylist of the messages which you can update which is cleaner and more made for your purpose See https://www.dev2qa.com/android-chat-app-example-using-recyclerview/ – Code Demon Jun 19 '20 at 07:20
  • @RyanM no i don't think so.. – Panagiotis Jun 19 '20 at 11:49
  • @Thompson sparta you think that if i use recycleview i will not face the same problem? – Panagiotis Jun 19 '20 at 11:51
  • Nop all you will have to do is update an arraylist . This aproach is very flexible and simple – Code Demon Jun 19 '20 at 17:58
  • @Thompsonsparta I will try it and i will let you know! Thanks in advance! – Panagiotis Jun 20 '20 at 13:42
  • @Thompsonsparta i made it with recycler view. facing exactly the same issue. all buttons created from quick replies are not going to the specific bubble that contains them but for some reason, to the second bubble, all of them just stacking there.. – Panagiotis Jun 21 '20 at 19:40
  • Please post your new code – Code Demon Jun 22 '20 at 02:53
  • @Thompsonsparta I added everything! – Panagiotis Jun 22 '20 at 14:58

1 Answers1

0

HERE IS THE XML CODE TO ADD BUTTONS <Button     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:text="@string/button_text"     android:drawableLeft="@drawable/button_icon"     ... />

CHAVDA MEET
  • 777
  • 8
  • 14