Skip to content

Commit 2b89f90

Browse files
Added v3.10.0
1 parent 54b4a7a commit 2b89f90

File tree

53 files changed

+1544
-44
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1544
-44
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
# Changelog
2+
### v3.10.0 (Nov 3, 2023) with Chat SDK `v4.13.0`
3+
* Added the `Suggested Replies` feature to enable quick and effective question asking to the bot.
4+
* Added `ChannelConfig.enableSuggestedReplies` configuration to enable/disable `Suggested Replies` feature.
5+
* Added the `Form type message` feature to enable the user to submit a form type message received by the bot.
6+
* Added `ChannelConfig.enableFormTypeMessage` configuration to enable/disable `Form type message` feature.
27
### v3.9.3 (Oct 26, 2023) with Chat SDK `v4.13.0`
38
* Improve stability.
49

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ org.gradle.jvmargs=-Xmx1536m
1616
# https://developer.android.com/topic/libraries/support-library/androidx-rn
1717
android.useAndroidX=true
1818

19-
UIKIT_VERSION = 3.9.3
19+
UIKIT_VERSION = 3.10.0
2020
UIKIT_VERSION_CODE = 1

uikit-sample/src/main/kotlin/com/sendbird/uikit_messaging_android/BaseApplication.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ class BaseApplication : MultiDexApplication() {
117117
UIKitConfig.groupChannelConfig.enableVoiceMessage = true
118118
// set whether to use Multiple Files Message
119119
UIKitConfig.groupChannelConfig.enableMultipleFilesMessage = true
120+
// set whether to use suggested replies
121+
UIKitConfig.groupChannelConfig.enableSuggestedReplies = true
122+
// set whether to use form type message
123+
UIKitConfig.groupChannelConfig.enableFormTypeMessage = true
120124

121125
// set custom params
122126
SendbirdUIKit.setCustomParamsHandler(object : CustomParamsHandler {

uikit/src/main/java/com/sendbird/uikit/SendbirdUIKit.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ public static CustomParamsHandler getCustomParamsHandler() {
977977
* @param botId The bot ID that is created in dashboard.
978978
* @param isDistinct Determines whether to reuse an existing channel or create a new channel.
979979
* @param handler The callback handler that lets you know if the request was successful or not.
980-
* @since 3.8.0
980+
* since 3.8.0
981981
*/
982982
public static void startChatWithAiBot(@NonNull Context context, @NonNull String botId, boolean isDistinct, @Nullable CompletionHandler handler) {
983983
User currentUser = SendbirdChat.getCurrentUser();

uikit/src/main/java/com/sendbird/uikit/activities/adapter/BaseMessageListAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import com.sendbird.uikit.model.MessageListUIParams;
4141
import com.sendbird.uikit.model.MessageUIConfig;
4242
import com.sendbird.uikit.model.configurations.ChannelConfig;
43-
import com.sendbird.uikit.utils.TextUtils;
4443

4544
import org.jetbrains.annotations.TestOnly;
4645

@@ -70,6 +69,7 @@ abstract public class BaseMessageListAdapter extends BaseMessageAdapter<BaseMess
7069
private OnIdentifiableItemLongClickListener<BaseMessage> listItemLongClickListener;
7170
@Nullable
7271
protected OnItemClickListener<User> mentionClickListener;
72+
7373
@NonNull
7474
private final MessageListUIParams messageListUIParams;
7575
@Nullable
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.sendbird.uikit.activities.adapter
2+
3+
import android.view.LayoutInflater
4+
import android.view.ViewGroup
5+
import androidx.recyclerview.widget.DiffUtil
6+
import com.sendbird.uikit.activities.viewholder.BaseViewHolder
7+
import com.sendbird.uikit.databinding.SbViewFormFieldBinding
8+
import com.sendbird.uikit.internal.model.Form
9+
import com.sendbird.uikit.internal.model.FormField
10+
11+
internal class FormFieldAdapter : BaseAdapter<FormField, BaseViewHolder<FormField>>() {
12+
private val formFields: MutableList<FormField> = mutableListOf()
13+
14+
fun isReadyToSubmit(): Boolean {
15+
return formFields.all { it.isReadyToSubmit() }
16+
}
17+
18+
fun updateValidation() {
19+
formFields.forEachIndexed { index, formField ->
20+
val lastValidation = formField.lastValidation
21+
val validation = formField.isReadyToSubmit()
22+
formField.lastValidation = validation
23+
if (lastValidation != validation) {
24+
notifyItemChanged(index)
25+
}
26+
}
27+
}
28+
29+
fun setFormFields(form: Form) {
30+
val newFormFields = if (form.isAnswered) {
31+
form.formFields.filter { it.answer != null }
32+
} else {
33+
form.formFields
34+
}
35+
val diffResult = DiffUtil.calculateDiff(FormFieldDiffCallback(formFields, newFormFields))
36+
formFields.clear()
37+
formFields.addAll(newFormFields)
38+
diffResult.dispatchUpdatesTo(this)
39+
}
40+
41+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<FormField> {
42+
return FormFieldViewHolder(
43+
SbViewFormFieldBinding.inflate(LayoutInflater.from(parent.context), parent, false)
44+
)
45+
}
46+
47+
override fun getItemCount(): Int {
48+
return formFields.size
49+
}
50+
51+
override fun getItem(position: Int): FormField {
52+
return formFields[position]
53+
}
54+
55+
override fun getItems(): List<FormField> {
56+
return formFields.toList()
57+
}
58+
59+
override fun onBindViewHolder(holder: BaseViewHolder<FormField>, position: Int) {
60+
holder.bind(getItem(position))
61+
}
62+
63+
internal class FormFieldViewHolder(
64+
val binding: SbViewFormFieldBinding
65+
) : BaseViewHolder<FormField>(binding.root) {
66+
override fun bind(item: FormField) {
67+
binding.formFieldView.drawFormField(item)
68+
}
69+
}
70+
71+
private class FormFieldDiffCallback(
72+
private val oldList: List<FormField>,
73+
private val newList: List<FormField>
74+
) : DiffUtil.Callback() {
75+
override fun getOldListSize(): Int {
76+
return oldList.size
77+
}
78+
79+
override fun getNewListSize(): Int {
80+
return newList.size
81+
}
82+
83+
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
84+
val oldItem = oldList[oldItemPosition]
85+
val newItem = newList[newItemPosition]
86+
return oldItem.formFieldKey == newItem.formFieldKey &&
87+
oldItem.messageId == newItem.messageId
88+
}
89+
90+
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
91+
return oldList[oldItemPosition] == newList[newItemPosition]
92+
}
93+
}
94+
}

uikit/src/main/java/com/sendbird/uikit/activities/adapter/MessageDiffCallback.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.sendbird.uikit.utils.MessageUtils;
1919

2020
import java.util.List;
21+
import java.util.Map;
2122

2223
class MessageDiffCallback extends DiffUtil.Callback {
2324
@NonNull
@@ -83,6 +84,12 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
8384
}
8485
}
8586

87+
Map<String, String> oldExtendedMessagePayload = oldMessage.getExtendedMessagePayload();
88+
Map<String, String> newExtendedMessagePayload = newMessage.getExtendedMessagePayload();
89+
if (!oldExtendedMessagePayload.equals(newExtendedMessagePayload)) {
90+
return false;
91+
}
92+
8693
if (messageListUIParams.shouldUseMessageReceipt()) {
8794
if (oldChannel.getUnreadMemberCount(newMessage) != newChannel.getUnreadMemberCount(newMessage)) {
8895
return false;

uikit/src/main/java/com/sendbird/uikit/activities/adapter/MessageListAdapter.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
package com.sendbird.uikit.activities.adapter;
22

3+
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
4+
35
import androidx.annotation.NonNull;
46
import androidx.annotation.Nullable;
57
import androidx.annotation.VisibleForTesting;
68

79
import com.sendbird.android.channel.GroupChannel;
810
import com.sendbird.android.message.BaseMessage;
11+
import com.sendbird.uikit.activities.viewholder.MessageViewHolder;
12+
import com.sendbird.uikit.interfaces.OnItemClickListener;
13+
import com.sendbird.uikit.internal.extensions.MessageListAdapterExtensionsKt;
14+
import com.sendbird.uikit.internal.interfaces.OnSubmitButtonClickListener;
15+
import com.sendbird.uikit.internal.ui.viewholders.FormMessageViewHolder;
16+
import com.sendbird.uikit.internal.ui.viewholders.SuggestedRepliesViewHolder;
917
import com.sendbird.uikit.internal.wrappers.SendbirdUIKitImpl;
1018
import com.sendbird.uikit.internal.wrappers.SendbirdUIKitWrapper;
1119
import com.sendbird.uikit.model.MessageListUIParams;
@@ -14,6 +22,9 @@
1422
* MessageListAdapter provides a binding from a {@link BaseMessage} type data set to views that are displayed within a RecyclerView.
1523
*/
1624
public class MessageListAdapter extends BaseMessageListAdapter {
25+
@Nullable
26+
protected OnItemClickListener<String> suggestedRepliesClickListener;
27+
1728
public MessageListAdapter(boolean useMessageGroupUI) {
1829
this(null, useMessageGroupUI);
1930
}
@@ -40,4 +51,49 @@ public MessageListAdapter(@Nullable GroupChannel channel, @NonNull MessageListUI
4051
.build(),
4152
sendbirdUIKit);
4253
}
54+
55+
@Override
56+
public void onBindViewHolder(@NonNull MessageViewHolder holder, int position) {
57+
super.onBindViewHolder(holder, position);
58+
if (holder instanceof SuggestedRepliesViewHolder) {
59+
SuggestedRepliesViewHolder suggestedRepliesViewHolder = (SuggestedRepliesViewHolder) holder;
60+
suggestedRepliesViewHolder.setSuggestedRepliesClickedListener((view, pos, data) -> {
61+
int messagePosition = holder.getBindingAdapterPosition();
62+
if (messagePosition != NO_POSITION && suggestedRepliesClickListener != null) {
63+
suggestedRepliesClickListener.onItemClick(view, pos, data);
64+
}
65+
});
66+
}
67+
68+
if (holder instanceof FormMessageViewHolder) {
69+
FormMessageViewHolder formMessageViewHolder = (FormMessageViewHolder) holder;
70+
formMessageViewHolder.setOnSubmitClickListener((message, form) -> {
71+
final OnSubmitButtonClickListener finalListener = MessageListAdapterExtensionsKt.getSubmitButtonClickListener(this);
72+
if (finalListener != null) {
73+
finalListener.onClicked(message, form);
74+
}
75+
});
76+
}
77+
}
78+
79+
/**
80+
* Returns a callback to be invoked when the suggested replies is clicked.
81+
*
82+
* @return {OnItemClickListener<String>} to be invoked when the suggested replies is clicked.
83+
* since 3.10.0
84+
*/
85+
@Nullable
86+
public OnItemClickListener<String> getSuggestedRepliesClickListener() {
87+
return suggestedRepliesClickListener;
88+
}
89+
90+
/**
91+
* Register a callback to be invoked when the suggested replies is clicked.
92+
*
93+
* @param suggestedRepliesClickListener The callback to be registered.
94+
* since 3.10.0
95+
*/
96+
public void setSuggestedRepliesClickListener(@Nullable OnItemClickListener<String> suggestedRepliesClickListener) {
97+
this.suggestedRepliesClickListener = suggestedRepliesClickListener;
98+
}
4399
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.sendbird.uikit.activities.adapter
2+
3+
import android.view.LayoutInflater
4+
import android.view.ViewGroup
5+
import androidx.recyclerview.widget.DiffUtil
6+
import com.sendbird.uikit.activities.viewholder.BaseViewHolder
7+
import com.sendbird.uikit.databinding.SbViewSuggestedReplyBinding
8+
import com.sendbird.uikit.interfaces.OnItemClickListener
9+
10+
internal class SuggestedRepliesAdapter : BaseAdapter<String, BaseViewHolder<String>>() {
11+
var onItemClickListener: OnItemClickListener<String>? = null
12+
var suggestedReplies: List<String> = listOf()
13+
set(value) {
14+
val diffResult = DiffUtil.calculateDiff(SuggestedReplyDiffCallback(field, value))
15+
field = value
16+
diffResult.dispatchUpdatesTo(this)
17+
}
18+
19+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<String> {
20+
return SuggestedReplyViewHolder(
21+
SbViewSuggestedReplyBinding.inflate(LayoutInflater.from(parent.context), parent, false)
22+
).apply {
23+
this.binding.suggestedReplyView.setOnClickListener {
24+
val item = getItem(absoluteAdapterPosition)
25+
val index = suggestedReplies.indexOf(item)
26+
if (index == -1) return@setOnClickListener
27+
onItemClickListener?.onItemClick(binding.root, index, item)
28+
}
29+
}
30+
}
31+
32+
override fun getItemCount(): Int {
33+
return suggestedReplies.size
34+
}
35+
36+
override fun getItem(position: Int): String {
37+
return suggestedReplies[position]
38+
}
39+
40+
override fun getItems(): List<String> {
41+
return suggestedReplies.toList()
42+
}
43+
44+
override fun onBindViewHolder(holder: BaseViewHolder<String>, position: Int) {
45+
holder.bind(getItem(position))
46+
}
47+
48+
private class SuggestedReplyViewHolder(
49+
val binding: SbViewSuggestedReplyBinding
50+
) : BaseViewHolder<String>(binding.root) {
51+
override fun bind(item: String) {
52+
binding.suggestedReplyView.drawSuggestedReplies(item)
53+
}
54+
}
55+
56+
private class SuggestedReplyDiffCallback(
57+
private val oldList: List<String>,
58+
private val newList: List<String>
59+
) : DiffUtil.Callback() {
60+
override fun getOldListSize(): Int {
61+
return oldList.size
62+
}
63+
64+
override fun getNewListSize(): Int {
65+
return newList.size
66+
}
67+
68+
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
69+
return oldList[oldItemPosition] == newList[newItemPosition]
70+
}
71+
72+
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
73+
return oldList[oldItemPosition] == newList[newItemPosition]
74+
}
75+
}
76+
}

uikit/src/main/java/com/sendbird/uikit/activities/viewholder/MessageType.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,30 @@ public enum MessageType {
8989
/**
9090
* Type of MultipleFilesMessage sent by the current user.
9191
*
92-
* @since 3.9.0
92+
* since 3.9.0
9393
*/
9494
VIEW_TYPE_MULTIPLE_FILES_MESSAGE_ME(17),
9595

9696
/**
9797
* Type of MultipleFilesMessage sent by users other than the current user .
9898
*
99-
* @since 3.9.0
99+
* since 3.9.0
100100
*/
101-
VIEW_TYPE_MULTIPLE_FILES_MESSAGE_OTHER(18);
101+
VIEW_TYPE_MULTIPLE_FILES_MESSAGE_OTHER(18),
102+
103+
/**
104+
* Type of suggested replies.
105+
*
106+
* since 3.10.0
107+
*/
108+
VIEW_TYPE_SUGGESTED_REPLIES(19),
109+
110+
/**
111+
* Type of forms message.
112+
*
113+
* since 3.10.0
114+
*/
115+
VIEW_TYPE_FORM_TYPE_MESSAGE(20);
102116

103117
final int value;
104118

0 commit comments

Comments
 (0)