Skip to content

Commit cfa72a6

Browse files
Added v3.11.0
1 parent 02bce9a commit cfa72a6

29 files changed

+600
-32
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# Changelog
2+
### v3.11.0 (Nov 29, 2023) with Chat SDK `v4.13.0`
3+
* `VIEW_TYPE_TYPING_INDICATOR` is a new typing indicator UI that can be turned on through `typingIndicatorTypes` option. When turned on, it will be displayed in `ChannelFragment` upon receiving typing event in real time.
4+
* Added `typingIndicatorTypes` in `ChannelConfig`.
25
### v3.10.1 (Nov 9, 2023) with Chat SDK `v4.13.0`
36
* Added `uikit-samples` project to demonstrate the usage of `UIKit`.
47
* Added `resetToDefault()` in `FragmentProviders`, `ModuleProviders`, `AdapterProviders` and `ViewModelProviders` to reset the providers to default.

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
We are introducing a new version of the Sendbird UIKit. Version 3 features a new modular architecture with more granular components that give you enhanced flexibility to customize your web and mobile apps. Check out our [migration guides](/changelogs/MIGRATIONGUIDE_V3.md) and download [our samples](https://github.com/sendbird/sendbird-uikit-android/tree/main/uikit-samples)
88

9-
Sendbird UIKit for Android is a development kit with an user interface that enables an easy and fast integration of standard chat features into new or existing client apps. This repository houses the UIKit source code in addition to two samples as explained below.
9+
Sendbird UIKit for Android is a development kit with an user interface that enables an easy and fast integration of standard chat features into new or existing client apps. This repository houses the UIKit source code in addition to two samples as explained below.
1010

1111
- **uikit** is where you can find the open source code. Check out [UIKit Open Source Guidelines](https://github.com/sendbird/sendbird-uikit-android-sources/blob/main/OPENSOURCE_GUIDELINES.md) for more information regarding our stance on open source.
1212
- **uikit-samples** consists of four use cases of UIKit.
@@ -33,13 +33,13 @@ This section shows you the prerequisites you need for testing Sendbird UIKit for
3333

3434
The minimum requirements for UIKit for Android are:
3535

36-
- Android 5.0 (API level 21) or higher
36+
- Android 5.0 (API level 21) or higher
3737
- Java 8 or higher
38-
- Support androidx only
38+
- Support androidx only
3939
- Android Gradle plugin 4.0.1 or higher
4040
- Sendbird Chat SDK for Android 4.0.3 and later
4141

42-
### Try the sample app using your data
42+
### Try the sample app using your data
4343

4444
If you would like to try the sample app specifically fit to your usage, you can do so by replacing the default sample app ID with yours, which you can obtain by [creating your Sendbird application from the dashboard](https://sendbird.com/docs/chat/v4/android/quickstart/send-first-message#3-install-and-configure-the-chat-sdk-4-step-1-create-a-sendbird-application-from-your-dashboard). Furthermore, you could also add data of your choice on the dashboard to test. This will allow you to experience the sample app with data from your Sendbird application.
4545

@@ -95,11 +95,11 @@ Then, open the `build.gradle` file at the application level. For `Java` and `Kot
9595
```gradle
9696
apply plugin: 'com.android.application'
9797
98-
android {
98+
android {
9999
buildFeatures {
100100
viewBinding true
101101
}
102-
102+
103103
compileOptions {
104104
sourceCompatibility JavaVersion.VERSION_1_8
105105
targetCompatibility JavaVersion.VERSION_1_8
@@ -111,6 +111,6 @@ dependencies {
111111
}
112112
```
113113

114-
After saving your `build.gradle` file, click the **Sync** button to apply all the changes.
114+
After saving your `build.gradle` file, click the **Sync** button to apply all the changes.
115115

116116
<br />

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.10.1
19+
UIKIT_VERSION = 3.11.0
2020
UIKIT_VERSION_CODE = 1

uikit-samples/src/main/java/com/sendbird/uikit/samples/BaseApplication.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.sendbird.uikit.SendbirdUIKit
1212
import com.sendbird.uikit.adapter.SendbirdUIKitAdapter
1313
import com.sendbird.uikit.consts.ReplyType
1414
import com.sendbird.uikit.consts.ThreadReplySelectType
15+
import com.sendbird.uikit.consts.TypingIndicatorType
1516
import com.sendbird.uikit.interfaces.CustomParamsHandler
1617
import com.sendbird.uikit.interfaces.UserInfo
1718
import com.sendbird.uikit.model.configurations.UIKitConfig
@@ -105,6 +106,8 @@ class BaseApplication : MultiDexApplication() {
105106
UIKitConfig.groupChannelConfig.threadReplySelectType = ThreadReplySelectType.THREAD
106107
// set whether to use voice message
107108
UIKitConfig.groupChannelConfig.enableVoiceMessage = true
109+
// set typing indicator types
110+
UIKitConfig.groupChannelConfig.typingIndicatorTypes = setOf(TypingIndicatorType.BUBBLE, TypingIndicatorType.TEXT)
108111

109112
// set custom params
110113
SendbirdUIKit.setCustomParamsHandler(object : CustomParamsHandler {

uikit-samples/src/main/java/com/sendbird/uikit/samples/basic/GroupChannelMainActivity.kt

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import com.sendbird.android.user.UnreadMessageCount
1717
import com.sendbird.android.user.User
1818
import com.sendbird.uikit.SendbirdUIKit
1919
import com.sendbird.uikit.activities.ChannelActivity
20+
import com.sendbird.uikit.activities.ChatNotificationChannelActivity
2021
import com.sendbird.uikit.providers.FragmentProviders
2122
import com.sendbird.uikit.samples.R
2223
import com.sendbird.uikit.samples.common.SampleSettingsFragment
@@ -99,14 +100,19 @@ class GroupChannelMainActivity : AppCompatActivity() {
99100
if (intent.hasExtra(StringSet.PUSH_REDIRECT_CHANNEL)) {
100101
val channelUrl = intent.getStringExtra(StringSet.PUSH_REDIRECT_CHANNEL)
101102
?: return
102-
if (intent.hasExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID)) {
103-
val messageId = intent.getLongExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID, 0L)
104-
if (messageId > 0L) {
105-
startActivity(ChannelActivity.newRedirectToMessageThreadIntent(this, channelUrl, messageId))
106-
intent.removeExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID)
107-
}
103+
val channelType = intent.getStringExtra(StringSet.PUSH_REDIRECT_CHANNEL_TYPE)
104+
if (channelType == StringSet.notification_chat) {
105+
startActivity(ChatNotificationChannelActivity.newIntent(this, channelUrl))
108106
} else {
109-
startActivity(ChannelActivity.newIntent(this, channelUrl))
107+
if (intent.hasExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID)) {
108+
val messageId = intent.getLongExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID, 0L)
109+
if (messageId > 0L) {
110+
startActivity(ChannelActivity.newRedirectToMessageThreadIntent(this, channelUrl, messageId))
111+
intent.removeExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID)
112+
}
113+
} else {
114+
startActivity(ChannelActivity.newIntent(this, channelUrl))
115+
}
110116
}
111117
intent.removeExtra(StringSet.PUSH_REDIRECT_CHANNEL)
112118
}

uikit-samples/src/main/java/com/sendbird/uikit/samples/common/extensions/UIKitExtensions.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ internal fun SampleType?.newRedirectToChannelIntent(
108108
putExtra(StringSet.KEY_CHANNEL_URL, channelUrl)
109109
putExtra(StringSet.PUSH_REDIRECT_CHANNEL, channelUrl)
110110
putExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID, messageId)
111+
putExtra(StringSet.PUSH_REDIRECT_CHANNEL_TYPE, channelType)
111112
}
112113
}
113114
}

uikit-samples/src/main/java/com/sendbird/uikit/samples/customization/GroupChannelRepository.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ internal object GroupChannelRepository {
2020
worker.submit {
2121
GroupChannel.createMyGroupChannelListQuery(GroupChannelListQueryParams()).next { channels, e ->
2222
WaitingDialog.dismiss()
23-
if (e != null) {
23+
if (e != null || channels.isNullOrEmpty()) {
2424
ContextUtils.toastError(activity, "No channels")
2525
return@next
2626
}
27-
channels?.let { channelCache.addAll(it) }
27+
channelCache.addAll(channels)
2828
activity.runOnUiThread { callback(channelCache.random()) }
2929
}
3030
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.sendbird.uikit.consts.MessageGroupType;
1616
import com.sendbird.uikit.consts.ReplyType;
1717
import com.sendbird.uikit.model.MessageListUIParams;
18+
import com.sendbird.uikit.model.TypingIndicatorMessage;
1819
import com.sendbird.uikit.utils.MessageUtils;
1920

2021
import java.util.List;
@@ -134,6 +135,10 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
134135
return false;
135136
}
136137

138+
if (oldMessage instanceof TypingIndicatorMessage && newMessage instanceof TypingIndicatorMessage) {
139+
return ((TypingIndicatorMessage) oldMessage).getTypingUsers().equals(((TypingIndicatorMessage) newMessage).getTypingUsers()) ;
140+
}
141+
137142
if (messageListUIParams.shouldUseQuotedView()) {
138143
BaseMessage oldParentMessage = oldMessage.getParentMessage();
139144
BaseMessage newParentMessage = newMessage.getParentMessage();

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,14 @@ public enum MessageType {
112112
*
113113
* since 3.10.0
114114
*/
115-
VIEW_TYPE_FORM_TYPE_MESSAGE(20);
115+
VIEW_TYPE_FORM_TYPE_MESSAGE(20),
116116

117+
/**
118+
* Type of typing indicator.
119+
*
120+
* since 3.11.0
121+
*/
122+
VIEW_TYPE_TYPING_INDICATOR(21);
117123
final int value;
118124

119125
MessageType(int value) {

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.sendbird.uikit.databinding.SbViewParentMessageInfoHolderBinding;
3535
import com.sendbird.uikit.databinding.SbViewSuggestedRepliesMessageBinding;
3636
import com.sendbird.uikit.databinding.SbViewTimeLineMessageBinding;
37+
import com.sendbird.uikit.databinding.SbViewTypingIndicatorMessageBinding;
3738
import com.sendbird.uikit.internal.extensions.MessageExtensionsKt;
3839
import com.sendbird.uikit.internal.ui.viewholders.AdminMessageViewHolder;
3940
import com.sendbird.uikit.internal.ui.viewholders.FormMessageViewHolder;
@@ -57,9 +58,11 @@
5758
import com.sendbird.uikit.internal.ui.viewholders.ParentMessageInfoViewHolder;
5859
import com.sendbird.uikit.internal.ui.viewholders.SuggestedRepliesViewHolder;
5960
import com.sendbird.uikit.internal.ui.viewholders.TimelineViewHolder;
61+
import com.sendbird.uikit.internal.ui.viewholders.TypingIndicatorViewHolder;
6062
import com.sendbird.uikit.model.MessageListUIParams;
6163
import com.sendbird.uikit.model.SuggestedRepliesMessage;
6264
import com.sendbird.uikit.model.TimelineMessage;
65+
import com.sendbird.uikit.model.TypingIndicatorMessage;
6366
import com.sendbird.uikit.utils.MessageUtils;
6467

6568
import java.util.Map;
@@ -228,6 +231,9 @@ public static MessageViewHolder createViewHolder(@NonNull LayoutInflater inflate
228231
case VIEW_TYPE_FORM_TYPE_MESSAGE:
229232
holder = new FormMessageViewHolder(SbViewFormMessageBinding.inflate(inflater, parent, false), messageListUIParams);
230233
break;
234+
case VIEW_TYPE_TYPING_INDICATOR:
235+
holder = new TypingIndicatorViewHolder(SbViewTypingIndicatorMessageBinding.inflate(inflater, parent, false), messageListUIParams);
236+
break;
231237
default:
232238
// unknown message type
233239
if (viewType == MessageType.VIEW_TYPE_UNKNOWN_MESSAGE_ME) {
@@ -323,6 +329,8 @@ public static MessageType getMessageType(@NonNull BaseMessage message) {
323329
type = MessageType.VIEW_TYPE_ADMIN_MESSAGE;
324330
} else if (message instanceof SuggestedRepliesMessage) {
325331
type = MessageType.VIEW_TYPE_SUGGESTED_REPLIES;
332+
} else if (message instanceof TypingIndicatorMessage) {
333+
type = MessageType.VIEW_TYPE_TYPING_INDICATOR;
326334
} else {
327335
if (MessageUtils.isMine(message)) {
328336
type = MessageType.VIEW_TYPE_UNKNOWN_MESSAGE_ME;

uikit/src/main/java/com/sendbird/uikit/consts/StringSet.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ object StringSet {
126126
const val EVENT_MESSAGE_SENT = "EVENT_MESSAGE_SENT"
127127
const val EVENT_MESSAGE_RECEIVED = "EVENT_MESSAGE_RECEIVED"
128128
const val EVENT_MESSAGE_UPDATED = "EVENT_MESSAGE_UPDATED"
129+
const val EVENT_TYPING_STATUS_UPDATED = "EVENT_TYPING_STATUS_UPDATED"
129130
const val INVALID_URL = "INVALID_URL"
130131
const val photo = "photo"
131132
const val photos = "photos"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.sendbird.uikit.consts
2+
3+
/**
4+
* Represents how the typing indicator is displayed in the channel fragment.
5+
*
6+
* @since 3.11.0
7+
*/
8+
enum class TypingIndicatorType {
9+
/**
10+
* The user nicknames of those who are typing are visible in the header.
11+
*
12+
* @since 3.11.0
13+
*/
14+
TEXT,
15+
16+
/**
17+
* Users who are typing are visible at the bottom of the message list.
18+
*
19+
* @since 3.11.0
20+
*/
21+
BUBBLE,
22+
}

uikit/src/main/java/com/sendbird/uikit/fragments/ChannelFragment.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import com.sendbird.uikit.consts.ReplyType;
4141
import com.sendbird.uikit.consts.StringSet;
4242
import com.sendbird.uikit.consts.ThreadReplySelectType;
43+
import com.sendbird.uikit.consts.TypingIndicatorType;
4344
import com.sendbird.uikit.interfaces.LoadingDialogHandler;
4445
import com.sendbird.uikit.interfaces.MessageDisplayDataProvider;
4546
import com.sendbird.uikit.interfaces.OnConsumableClickListener;
@@ -222,7 +223,7 @@ protected void onBindChannelHeaderComponent(@NonNull ChannelHeaderComponent head
222223
startActivity(intent);
223224
});
224225

225-
if (channelConfig.getEnableTypingIndicator()) {
226+
if (channelConfig.getEnableTypingIndicator() && channelConfig.getTypingIndicatorTypes().contains(TypingIndicatorType.TEXT)) {
226227
viewModel.getTypingMembers().observe(getViewLifecycleOwner(), typingMembers -> {
227228
String description = null;
228229
if (typingMembers != null && getContext() != null) {
@@ -330,6 +331,9 @@ protected void onBindMessageListComponent(@NonNull MessageListComponent messageL
330331
case StringSet.MESSAGE_FILL:
331332
messageListComponent.notifyMessagesFilled(!anchorDialogShowing.get());
332333
break;
334+
case StringSet.EVENT_TYPING_STATUS_UPDATED:
335+
messageListComponent.notifyTypingIndicatorUpdated(!anchorDialogShowing.get());
336+
break;
333337
}
334338
}
335339
if (!isInitialCallFinished) {

uikit/src/main/java/com/sendbird/uikit/internal/model/template_messages/KeySet.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ internal object KeySet {
8888
const val enable_ogtag = "enable_ogtag"
8989
const val enable_mention = "enable_mention"
9090
const val enable_typing_indicator = "enable_typing_indicator"
91+
const val typing_indicator_types = "typing_indicator_types"
9192
const val enable_reactions = "enable_reactions"
9293
const val enable_voice_message = "enable_voice_message"
9394
const val enable_multiple_files_message = "enable_multiple_files_message"
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.sendbird.uikit.internal.ui.messages
2+
3+
import android.animation.AnimatorSet
4+
import android.animation.ObjectAnimator
5+
import android.content.Context
6+
import android.content.res.ColorStateList
7+
import android.util.AttributeSet
8+
import android.view.LayoutInflater
9+
import android.view.View
10+
import androidx.appcompat.content.res.AppCompatResources
11+
import com.sendbird.uikit.R
12+
import com.sendbird.uikit.SendbirdUIKit
13+
import com.sendbird.uikit.databinding.SbViewTypingIndicatorDotsComponentBinding
14+
import com.sendbird.uikit.utils.DrawableUtils
15+
16+
internal class TypingIndicatorDotsView @JvmOverloads internal constructor(
17+
context: Context,
18+
attrs: AttributeSet? = null,
19+
defStyle: Int = 0
20+
) : BaseMessageView(context, attrs, defStyle) {
21+
override val binding: SbViewTypingIndicatorDotsComponentBinding = SbViewTypingIndicatorDotsComponentBinding.inflate(
22+
LayoutInflater.from(getContext()),
23+
this,
24+
true
25+
)
26+
27+
override val layout: View
28+
get() = binding.root
29+
private var animatorSet: AnimatorSet? = null
30+
31+
init {
32+
val messageBackground = R.drawable.sb_shape_chat_bubble
33+
val messageBackgroundTint = if (SendbirdUIKit.isDarkMode()) {
34+
AppCompatResources.getColorStateList(context, R.color.ondark_04)
35+
} else {
36+
AppCompatResources.getColorStateList(context, R.color.onlight_04)
37+
}
38+
val dotImageTintList = if (SendbirdUIKit.isDarkMode()) {
39+
AppCompatResources.getColorStateList(context, R.color.ondark_01)
40+
} else {
41+
AppCompatResources.getColorStateList(context, R.color.onlight_01)
42+
}
43+
44+
binding.root.background =
45+
DrawableUtils.setTintList(context, messageBackground, messageBackgroundTint)
46+
47+
setDotsImageTintList(dotImageTintList)
48+
}
49+
50+
override fun onAttachedToWindow() {
51+
super.onAttachedToWindow()
52+
startAnimation()
53+
}
54+
55+
override fun onDetachedFromWindow() {
56+
super.onDetachedFromWindow()
57+
stopAnimation()
58+
}
59+
60+
private fun setDotsImageTintList(dotImageTintList: ColorStateList) {
61+
with(binding) {
62+
ivLeftTypingDot.imageTintList = dotImageTintList
63+
ivCenterTypingDot.imageTintList = dotImageTintList
64+
ivRightTypingDot.imageTintList = dotImageTintList
65+
}
66+
}
67+
68+
private fun startAnimation() {
69+
with(binding) {
70+
setAnimation(ivLeftTypingDot, 400L)
71+
setAnimation(ivCenterTypingDot, 600L)
72+
setAnimation(ivRightTypingDot, 800L)
73+
}
74+
}
75+
76+
private fun stopAnimation() {
77+
animatorSet?.cancel()
78+
animatorSet = null
79+
}
80+
81+
private fun setAnimation(targetView: View, startDelay: Long) {
82+
val startAlphaAnimator = ObjectAnimator.ofFloat(targetView, "alpha", 0f, 0.12f)
83+
val scaleXAnimator = ObjectAnimator.ofFloat(targetView, "scaleX", 1.0f, 1.2f).apply {
84+
this.startDelay = startDelay
85+
this.duration = 400
86+
this.repeatCount = ObjectAnimator.INFINITE
87+
this.repeatMode = ObjectAnimator.REVERSE
88+
}
89+
val scaleYAnimator = ObjectAnimator.ofFloat(targetView, "scaleY", 1.0f, 1.2f).apply {
90+
this.startDelay = startDelay
91+
this.duration = 400
92+
this.repeatCount = ObjectAnimator.INFINITE
93+
this.repeatMode = ObjectAnimator.REVERSE
94+
}
95+
val alphaAnimator = ObjectAnimator.ofFloat(targetView, "alpha", 0.12f, 0.38f).apply {
96+
this.startDelay = startDelay
97+
this.duration = 400
98+
this.repeatCount = ObjectAnimator.INFINITE
99+
this.repeatMode = ObjectAnimator.REVERSE
100+
}
101+
102+
animatorSet = AnimatorSet().apply {
103+
playTogether(startAlphaAnimator, scaleXAnimator, scaleYAnimator, alphaAnimator)
104+
start()
105+
}
106+
}
107+
}

0 commit comments

Comments
 (0)