Skip to content

Commit

Permalink
Merge pull request #22 from sendbird/release/3.11.0
Browse files Browse the repository at this point in the history
3.11.0
  • Loading branch information
sendbird-sdk-deployment authored Nov 29, 2023
2 parents 02bce9a + cfa72a6 commit 28c5f2f
Show file tree
Hide file tree
Showing 29 changed files with 600 additions and 32 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Changelog
### v3.11.0 (Nov 29, 2023) with Chat SDK `v4.13.0`
* `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.
* Added `typingIndicatorTypes` in `ChannelConfig`.
### v3.10.1 (Nov 9, 2023) with Chat SDK `v4.13.0`
* Added `uikit-samples` project to demonstrate the usage of `UIKit`.
* Added `resetToDefault()` in `FragmentProviders`, `ModuleProviders`, `AdapterProviders` and `ViewModelProviders` to reset the providers to default.
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

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)

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.
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.

- **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.
- **uikit-samples** consists of four use cases of UIKit.
Expand All @@ -33,13 +33,13 @@ This section shows you the prerequisites you need for testing Sendbird UIKit for

The minimum requirements for UIKit for Android are:

- Android 5.0 (API level 21) or higher
- Android 5.0 (API level 21) or higher
- Java 8 or higher
- Support androidx only
- Support androidx only
- Android Gradle plugin 4.0.1 or higher
- Sendbird Chat SDK for Android 4.0.3 and later

### Try the sample app using your data
### Try the sample app using your data

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.

Expand Down Expand Up @@ -95,11 +95,11 @@ Then, open the `build.gradle` file at the application level. For `Java` and `Kot
```gradle
apply plugin: 'com.android.application'
android {
android {
buildFeatures {
viewBinding true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
Expand All @@ -111,6 +111,6 @@ dependencies {
}
```

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

<br />
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ org.gradle.jvmargs=-Xmx1536m
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true

UIKIT_VERSION = 3.10.1
UIKIT_VERSION = 3.11.0
UIKIT_VERSION_CODE = 1
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.sendbird.uikit.SendbirdUIKit
import com.sendbird.uikit.adapter.SendbirdUIKitAdapter
import com.sendbird.uikit.consts.ReplyType
import com.sendbird.uikit.consts.ThreadReplySelectType
import com.sendbird.uikit.consts.TypingIndicatorType
import com.sendbird.uikit.interfaces.CustomParamsHandler
import com.sendbird.uikit.interfaces.UserInfo
import com.sendbird.uikit.model.configurations.UIKitConfig
Expand Down Expand Up @@ -105,6 +106,8 @@ class BaseApplication : MultiDexApplication() {
UIKitConfig.groupChannelConfig.threadReplySelectType = ThreadReplySelectType.THREAD
// set whether to use voice message
UIKitConfig.groupChannelConfig.enableVoiceMessage = true
// set typing indicator types
UIKitConfig.groupChannelConfig.typingIndicatorTypes = setOf(TypingIndicatorType.BUBBLE, TypingIndicatorType.TEXT)

// set custom params
SendbirdUIKit.setCustomParamsHandler(object : CustomParamsHandler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.sendbird.android.user.UnreadMessageCount
import com.sendbird.android.user.User
import com.sendbird.uikit.SendbirdUIKit
import com.sendbird.uikit.activities.ChannelActivity
import com.sendbird.uikit.activities.ChatNotificationChannelActivity
import com.sendbird.uikit.providers.FragmentProviders
import com.sendbird.uikit.samples.R
import com.sendbird.uikit.samples.common.SampleSettingsFragment
Expand Down Expand Up @@ -99,14 +100,19 @@ class GroupChannelMainActivity : AppCompatActivity() {
if (intent.hasExtra(StringSet.PUSH_REDIRECT_CHANNEL)) {
val channelUrl = intent.getStringExtra(StringSet.PUSH_REDIRECT_CHANNEL)
?: return
if (intent.hasExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID)) {
val messageId = intent.getLongExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID, 0L)
if (messageId > 0L) {
startActivity(ChannelActivity.newRedirectToMessageThreadIntent(this, channelUrl, messageId))
intent.removeExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID)
}
val channelType = intent.getStringExtra(StringSet.PUSH_REDIRECT_CHANNEL_TYPE)
if (channelType == StringSet.notification_chat) {
startActivity(ChatNotificationChannelActivity.newIntent(this, channelUrl))
} else {
startActivity(ChannelActivity.newIntent(this, channelUrl))
if (intent.hasExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID)) {
val messageId = intent.getLongExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID, 0L)
if (messageId > 0L) {
startActivity(ChannelActivity.newRedirectToMessageThreadIntent(this, channelUrl, messageId))
intent.removeExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID)
}
} else {
startActivity(ChannelActivity.newIntent(this, channelUrl))
}
}
intent.removeExtra(StringSet.PUSH_REDIRECT_CHANNEL)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ internal fun SampleType?.newRedirectToChannelIntent(
putExtra(StringSet.KEY_CHANNEL_URL, channelUrl)
putExtra(StringSet.PUSH_REDIRECT_CHANNEL, channelUrl)
putExtra(StringSet.PUSH_REDIRECT_MESSAGE_ID, messageId)
putExtra(StringSet.PUSH_REDIRECT_CHANNEL_TYPE, channelType)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ internal object GroupChannelRepository {
worker.submit {
GroupChannel.createMyGroupChannelListQuery(GroupChannelListQueryParams()).next { channels, e ->
WaitingDialog.dismiss()
if (e != null) {
if (e != null || channels.isNullOrEmpty()) {
ContextUtils.toastError(activity, "No channels")
return@next
}
channels?.let { channelCache.addAll(it) }
channelCache.addAll(channels)
activity.runOnUiThread { callback(channelCache.random()) }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.sendbird.uikit.consts.MessageGroupType;
import com.sendbird.uikit.consts.ReplyType;
import com.sendbird.uikit.model.MessageListUIParams;
import com.sendbird.uikit.model.TypingIndicatorMessage;
import com.sendbird.uikit.utils.MessageUtils;

import java.util.List;
Expand Down Expand Up @@ -134,6 +135,10 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return false;
}

if (oldMessage instanceof TypingIndicatorMessage && newMessage instanceof TypingIndicatorMessage) {
return ((TypingIndicatorMessage) oldMessage).getTypingUsers().equals(((TypingIndicatorMessage) newMessage).getTypingUsers()) ;
}

if (messageListUIParams.shouldUseQuotedView()) {
BaseMessage oldParentMessage = oldMessage.getParentMessage();
BaseMessage newParentMessage = newMessage.getParentMessage();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,14 @@ public enum MessageType {
*
* since 3.10.0
*/
VIEW_TYPE_FORM_TYPE_MESSAGE(20);
VIEW_TYPE_FORM_TYPE_MESSAGE(20),

/**
* Type of typing indicator.
*
* since 3.11.0
*/
VIEW_TYPE_TYPING_INDICATOR(21);
final int value;

MessageType(int value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.sendbird.uikit.databinding.SbViewParentMessageInfoHolderBinding;
import com.sendbird.uikit.databinding.SbViewSuggestedRepliesMessageBinding;
import com.sendbird.uikit.databinding.SbViewTimeLineMessageBinding;
import com.sendbird.uikit.databinding.SbViewTypingIndicatorMessageBinding;
import com.sendbird.uikit.internal.extensions.MessageExtensionsKt;
import com.sendbird.uikit.internal.ui.viewholders.AdminMessageViewHolder;
import com.sendbird.uikit.internal.ui.viewholders.FormMessageViewHolder;
Expand All @@ -57,9 +58,11 @@
import com.sendbird.uikit.internal.ui.viewholders.ParentMessageInfoViewHolder;
import com.sendbird.uikit.internal.ui.viewholders.SuggestedRepliesViewHolder;
import com.sendbird.uikit.internal.ui.viewholders.TimelineViewHolder;
import com.sendbird.uikit.internal.ui.viewholders.TypingIndicatorViewHolder;
import com.sendbird.uikit.model.MessageListUIParams;
import com.sendbird.uikit.model.SuggestedRepliesMessage;
import com.sendbird.uikit.model.TimelineMessage;
import com.sendbird.uikit.model.TypingIndicatorMessage;
import com.sendbird.uikit.utils.MessageUtils;

import java.util.Map;
Expand Down Expand Up @@ -228,6 +231,9 @@ public static MessageViewHolder createViewHolder(@NonNull LayoutInflater inflate
case VIEW_TYPE_FORM_TYPE_MESSAGE:
holder = new FormMessageViewHolder(SbViewFormMessageBinding.inflate(inflater, parent, false), messageListUIParams);
break;
case VIEW_TYPE_TYPING_INDICATOR:
holder = new TypingIndicatorViewHolder(SbViewTypingIndicatorMessageBinding.inflate(inflater, parent, false), messageListUIParams);
break;
default:
// unknown message type
if (viewType == MessageType.VIEW_TYPE_UNKNOWN_MESSAGE_ME) {
Expand Down Expand Up @@ -323,6 +329,8 @@ public static MessageType getMessageType(@NonNull BaseMessage message) {
type = MessageType.VIEW_TYPE_ADMIN_MESSAGE;
} else if (message instanceof SuggestedRepliesMessage) {
type = MessageType.VIEW_TYPE_SUGGESTED_REPLIES;
} else if (message instanceof TypingIndicatorMessage) {
type = MessageType.VIEW_TYPE_TYPING_INDICATOR;
} else {
if (MessageUtils.isMine(message)) {
type = MessageType.VIEW_TYPE_UNKNOWN_MESSAGE_ME;
Expand Down
1 change: 1 addition & 0 deletions uikit/src/main/java/com/sendbird/uikit/consts/StringSet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ object StringSet {
const val EVENT_MESSAGE_SENT = "EVENT_MESSAGE_SENT"
const val EVENT_MESSAGE_RECEIVED = "EVENT_MESSAGE_RECEIVED"
const val EVENT_MESSAGE_UPDATED = "EVENT_MESSAGE_UPDATED"
const val EVENT_TYPING_STATUS_UPDATED = "EVENT_TYPING_STATUS_UPDATED"
const val INVALID_URL = "INVALID_URL"
const val photo = "photo"
const val photos = "photos"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.sendbird.uikit.consts

/**
* Represents how the typing indicator is displayed in the channel fragment.
*
* @since 3.11.0
*/
enum class TypingIndicatorType {
/**
* The user nicknames of those who are typing are visible in the header.
*
* @since 3.11.0
*/
TEXT,

/**
* Users who are typing are visible at the bottom of the message list.
*
* @since 3.11.0
*/
BUBBLE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.sendbird.uikit.consts.ReplyType;
import com.sendbird.uikit.consts.StringSet;
import com.sendbird.uikit.consts.ThreadReplySelectType;
import com.sendbird.uikit.consts.TypingIndicatorType;
import com.sendbird.uikit.interfaces.LoadingDialogHandler;
import com.sendbird.uikit.interfaces.MessageDisplayDataProvider;
import com.sendbird.uikit.interfaces.OnConsumableClickListener;
Expand Down Expand Up @@ -222,7 +223,7 @@ protected void onBindChannelHeaderComponent(@NonNull ChannelHeaderComponent head
startActivity(intent);
});

if (channelConfig.getEnableTypingIndicator()) {
if (channelConfig.getEnableTypingIndicator() && channelConfig.getTypingIndicatorTypes().contains(TypingIndicatorType.TEXT)) {
viewModel.getTypingMembers().observe(getViewLifecycleOwner(), typingMembers -> {
String description = null;
if (typingMembers != null && getContext() != null) {
Expand Down Expand Up @@ -330,6 +331,9 @@ protected void onBindMessageListComponent(@NonNull MessageListComponent messageL
case StringSet.MESSAGE_FILL:
messageListComponent.notifyMessagesFilled(!anchorDialogShowing.get());
break;
case StringSet.EVENT_TYPING_STATUS_UPDATED:
messageListComponent.notifyTypingIndicatorUpdated(!anchorDialogShowing.get());
break;
}
}
if (!isInitialCallFinished) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ internal object KeySet {
const val enable_ogtag = "enable_ogtag"
const val enable_mention = "enable_mention"
const val enable_typing_indicator = "enable_typing_indicator"
const val typing_indicator_types = "typing_indicator_types"
const val enable_reactions = "enable_reactions"
const val enable_voice_message = "enable_voice_message"
const val enable_multiple_files_message = "enable_multiple_files_message"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.sendbird.uikit.internal.ui.messages

import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.content.res.ColorStateList
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.content.res.AppCompatResources
import com.sendbird.uikit.R
import com.sendbird.uikit.SendbirdUIKit
import com.sendbird.uikit.databinding.SbViewTypingIndicatorDotsComponentBinding
import com.sendbird.uikit.utils.DrawableUtils

internal class TypingIndicatorDotsView @JvmOverloads internal constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : BaseMessageView(context, attrs, defStyle) {
override val binding: SbViewTypingIndicatorDotsComponentBinding = SbViewTypingIndicatorDotsComponentBinding.inflate(
LayoutInflater.from(getContext()),
this,
true
)

override val layout: View
get() = binding.root
private var animatorSet: AnimatorSet? = null

init {
val messageBackground = R.drawable.sb_shape_chat_bubble
val messageBackgroundTint = if (SendbirdUIKit.isDarkMode()) {
AppCompatResources.getColorStateList(context, R.color.ondark_04)
} else {
AppCompatResources.getColorStateList(context, R.color.onlight_04)
}
val dotImageTintList = if (SendbirdUIKit.isDarkMode()) {
AppCompatResources.getColorStateList(context, R.color.ondark_01)
} else {
AppCompatResources.getColorStateList(context, R.color.onlight_01)
}

binding.root.background =
DrawableUtils.setTintList(context, messageBackground, messageBackgroundTint)

setDotsImageTintList(dotImageTintList)
}

override fun onAttachedToWindow() {
super.onAttachedToWindow()
startAnimation()
}

override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
stopAnimation()
}

private fun setDotsImageTintList(dotImageTintList: ColorStateList) {
with(binding) {
ivLeftTypingDot.imageTintList = dotImageTintList
ivCenterTypingDot.imageTintList = dotImageTintList
ivRightTypingDot.imageTintList = dotImageTintList
}
}

private fun startAnimation() {
with(binding) {
setAnimation(ivLeftTypingDot, 400L)
setAnimation(ivCenterTypingDot, 600L)
setAnimation(ivRightTypingDot, 800L)
}
}

private fun stopAnimation() {
animatorSet?.cancel()
animatorSet = null
}

private fun setAnimation(targetView: View, startDelay: Long) {
val startAlphaAnimator = ObjectAnimator.ofFloat(targetView, "alpha", 0f, 0.12f)
val scaleXAnimator = ObjectAnimator.ofFloat(targetView, "scaleX", 1.0f, 1.2f).apply {
this.startDelay = startDelay
this.duration = 400
this.repeatCount = ObjectAnimator.INFINITE
this.repeatMode = ObjectAnimator.REVERSE
}
val scaleYAnimator = ObjectAnimator.ofFloat(targetView, "scaleY", 1.0f, 1.2f).apply {
this.startDelay = startDelay
this.duration = 400
this.repeatCount = ObjectAnimator.INFINITE
this.repeatMode = ObjectAnimator.REVERSE
}
val alphaAnimator = ObjectAnimator.ofFloat(targetView, "alpha", 0.12f, 0.38f).apply {
this.startDelay = startDelay
this.duration = 400
this.repeatCount = ObjectAnimator.INFINITE
this.repeatMode = ObjectAnimator.REVERSE
}

animatorSet = AnimatorSet().apply {
playTogether(startAlphaAnimator, scaleXAnimator, scaleYAnimator, alphaAnimator)
start()
}
}
}
Loading

0 comments on commit 28c5f2f

Please sign in to comment.