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);
}
}
}
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);} } }
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!