Skip to content

Commit 9913c5d

Browse files
authored
Merge pull request #64 from GetStream/sdk/5.0.1
Update SDK version to 5.0.1
2 parents 20785fb + 23fbe84 commit 9913c5d

File tree

7 files changed

+97
-71
lines changed

7 files changed

+97
-71
lines changed

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ buildscript {
44
mavenCentral()
55
}
66
dependencies {
7-
classpath "com.android.tools.build:gradle:7.1.0"
8-
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30"
7+
classpath "com.android.tools.build:gradle:7.1.2"
8+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
99
}
1010
}
1111

samplekotlin/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ android {
2222

2323
dependencies {
2424
// Add new dependencies
25-
implementation "io.getstream:stream-chat-android-ui-components:4.28.2"
26-
implementation "com.google.android.material:material:1.4.0"
25+
implementation "io.getstream:stream-chat-android-ui-components:5.0.1"
26+
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1"
27+
implementation "com.google.android.material:material:1.5.0"
2728
implementation "androidx.activity:activity-ktx:1.4.0"
2829
implementation "io.coil-kt:coil:1.4.0"
2930
}

samplekotlin/src/main/java/com/example/chattutorial/ChannelActivity2.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.Mode.Threa
1414
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.State.NavigateUp
1515
import io.getstream.chat.android.client.models.Channel
1616
import io.getstream.chat.android.ui.message.input.viewmodel.bindView
17+
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactoryManager
1718
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModel
1819
import io.getstream.chat.android.ui.message.list.header.viewmodel.bindView
1920
import io.getstream.chat.android.ui.message.list.viewmodel.bindView
@@ -41,8 +42,10 @@ class ChannelActivity2 : AppCompatActivity() {
4142
val messageListViewModel: MessageListViewModel by viewModels { factory }
4243
val messageInputViewModel: MessageInputViewModel by viewModels { factory }
4344

44-
// Set view factory for Imgur attachments
45-
binding.messageListView.setAttachmentViewFactory(ImgurAttachmentViewFactory())
45+
// Set view factory manager for Imgur attachments
46+
val imgurAttachmentViewFactory = ImgurAttachmentFactory()
47+
val attachmentViewFactory = AttachmentFactoryManager(listOf(imgurAttachmentViewFactory))
48+
binding.messageListView.setAttachmentFactoryManager(attachmentViewFactory)
4649

4750
// Step 2 - Bind the view and ViewModels, they are loosely coupled so it's easy to customize
4851
messageListHeaderViewModel.bindView(binding.messageListHeaderView, this)

samplekotlin/src/main/java/com/example/chattutorial/ChannelActivity3.kt

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,23 @@ import android.os.Bundle
66
import androidx.activity.addCallback
77
import androidx.activity.viewModels
88
import androidx.appcompat.app.AppCompatActivity
9+
import androidx.lifecycle.lifecycleScope
910
import com.example.chattutorial.databinding.ActivityChannel3Binding
1011
import com.getstream.sdk.chat.viewmodel.MessageInputViewModel
1112
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel
1213
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.Mode.Normal
1314
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.Mode.Thread
1415
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.State.NavigateUp
16+
import io.getstream.chat.android.client.ChatClient
1517
import io.getstream.chat.android.client.models.Channel
16-
import io.getstream.chat.android.livedata.ChatDomain
18+
import io.getstream.chat.android.offline.extensions.globalState
1719
import io.getstream.chat.android.ui.message.input.viewmodel.bindView
20+
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactoryManager
1821
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModel
1922
import io.getstream.chat.android.ui.message.list.header.viewmodel.bindView
2023
import io.getstream.chat.android.ui.message.list.viewmodel.bindView
2124
import io.getstream.chat.android.ui.message.list.viewmodel.factory.MessageListViewModelFactory
25+
import kotlinx.coroutines.flow.collect
2226

2327
class ChannelActivity3 : AppCompatActivity() {
2428

@@ -42,8 +46,10 @@ class ChannelActivity3 : AppCompatActivity() {
4246
val messageListViewModel: MessageListViewModel by viewModels { factory }
4347
val messageInputViewModel: MessageInputViewModel by viewModels { factory }
4448

45-
// Set view factory for Imgur attachments
46-
binding.messageListView.setAttachmentViewFactory(ImgurAttachmentViewFactory())
49+
// Set a view factory manager for Imgur attachments
50+
val imgurAttachmentViewFactory = ImgurAttachmentFactory()
51+
val attachmentViewFactory = AttachmentFactoryManager(listOf(imgurAttachmentViewFactory))
52+
binding.messageListView.setAttachmentFactoryManager(attachmentViewFactory)
4753

4854
// Step 2 - Bind the view and ViewModels, they are loosely coupled so it's easy to customize
4955
messageListHeaderViewModel.bindView(binding.messageListHeaderView, this)
@@ -87,25 +93,15 @@ class ChannelActivity3 : AppCompatActivity() {
8793
val nobodyTyping = "nobody is typing"
8894
binding.typingHeaderView.text = nobodyTyping
8995

90-
// Obtain a ChannelController
91-
ChatDomain
92-
.instance()
93-
.getChannelController(cid)
94-
.enqueue { channelControllerResult ->
95-
if (channelControllerResult.isSuccess) {
96-
// Observe typing users
97-
channelControllerResult.data().typing.observe(this) { typingState ->
98-
binding.typingHeaderView.text = when {
99-
100-
typingState.users.isNotEmpty() -> {
101-
typingState.users.joinToString(prefix = "typing: ") { user -> user.name }
102-
}
103-
104-
else -> nobodyTyping
105-
}
106-
}
96+
// Observe typing events and update typing header depending on its state.
97+
lifecycleScope.launchWhenStarted {
98+
ChatClient.instance().globalState.typingUpdates.collect {
99+
binding.typingHeaderView.text = when {
100+
it.users.isNotEmpty() -> it.users.joinToString(prefix = "typing: ") { user -> user.name }
101+
else -> nobodyTyping
107102
}
108103
}
104+
}
109105
}
110106

111107
companion object {

samplekotlin/src/main/java/com/example/chattutorial/ChannelActivity4.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import io.getstream.chat.android.client.events.TypingStartEvent
1818
import io.getstream.chat.android.client.events.TypingStopEvent
1919
import io.getstream.chat.android.client.models.Channel
2020
import io.getstream.chat.android.ui.message.input.viewmodel.bindView
21+
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactoryManager
2122
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModel
2223
import io.getstream.chat.android.ui.message.list.header.viewmodel.bindView
2324
import io.getstream.chat.android.ui.message.list.viewmodel.bindView
@@ -45,8 +46,10 @@ class ChannelActivity4 : AppCompatActivity() {
4546
val messageListViewModel: MessageListViewModel by viewModels { factory }
4647
val messageInputViewModel: MessageInputViewModel by viewModels { factory }
4748

48-
// Set view factory for Imgur attachments
49-
binding.messageListView.setAttachmentViewFactory(ImgurAttachmentViewFactory())
49+
// Set view factory manager for Imgur attachments
50+
val imgurAttachmentViewFactory = ImgurAttachmentFactory()
51+
val attachmentViewFactory = AttachmentFactoryManager(listOf(imgurAttachmentViewFactory))
52+
binding.messageListView.setAttachmentFactoryManager(attachmentViewFactory)
5053

5154
// Step 2 - Bind the view and ViewModels, they are loosely coupled so it's easy to customize
5255
messageListHeaderViewModel.bindView(binding.messageListHeaderView, this)
@@ -92,7 +95,7 @@ class ChannelActivity4 : AppCompatActivity() {
9295

9396
val currentlyTyping = mutableSetOf<String>()
9497

95-
// Observe raw events through the low-level client
98+
// Observe typing events and update typing header depending on its state.
9699
ChatClient
97100
.instance()
98101
.channel(cid)
Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,58 @@
11
package com.example.chattutorial
22

33
import android.view.LayoutInflater
4-
import android.view.View
54
import android.view.ViewGroup
65
import coil.load
76
import com.example.chattutorial.databinding.AttachmentImgurBinding
8-
import com.getstream.sdk.chat.adapter.MessageListItem
97
import io.getstream.chat.android.client.models.Attachment
8+
import io.getstream.chat.android.client.models.Message
109
import io.getstream.chat.android.ui.message.list.adapter.MessageListListenerContainer
11-
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentViewFactory
12-
import io.getstream.chat.android.ui.message.list.MessageListItemStyle
10+
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactory
11+
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.InnerAttachmentViewHolder
1312

14-
class ImgurAttachmentViewFactory : AttachmentViewFactory() {
13+
/** A custom attachment factory to show an imgur logo if the attachment URL is an imgur image. */
14+
class ImgurAttachmentFactory : AttachmentFactory {
1515

16-
override fun createAttachmentView(
17-
data: MessageListItem.MessageItem,
18-
listeners: MessageListListenerContainer?,
19-
style: MessageListItemStyle,
20-
parent: ViewGroup,
21-
): View {
22-
val imgurAttachment = data.message.attachments.firstOrNull { it.isImgurAttachment() }
23-
return when {
24-
imgurAttachment != null -> createImgurAttachmentView(imgurAttachment, parent)
25-
else -> super.createAttachmentView(data, listeners, style, parent)
26-
}
16+
override fun canHandle(message: Message): Boolean {
17+
val imgurAttachment = message.attachments.firstOrNull { it.isImgurAttachment() }
18+
return imgurAttachment != null
2719
}
2820

29-
private fun Attachment.isImgurAttachment(): Boolean = imageUrl?.contains("imgur") == true
30-
31-
private fun createImgurAttachmentView(imgurAttachment: Attachment, parent: ViewGroup): View {
21+
override fun createViewHolder(
22+
message: Message,
23+
listeners: MessageListListenerContainer?,
24+
parent: ViewGroup
25+
): InnerAttachmentViewHolder {
26+
val imgurAttachment = message.attachments.firstOrNull { it.isImgurAttachment() }
27+
?: return createViewHolder(message, listeners, parent)
3228
val binding = AttachmentImgurBinding
3329
.inflate(LayoutInflater.from(parent.context), null, false)
30+
return ImgurAttachmentViewHolder(
31+
imgurAttachment = imgurAttachment,
32+
binding = binding
33+
)
34+
}
35+
36+
private fun Attachment.isImgurAttachment(): Boolean = imageUrl?.contains("imgur") == true
3437

35-
binding.ivMediaThumb.apply {
36-
shapeAppearanceModel = shapeAppearanceModel
37-
.toBuilder()
38-
.setAllCornerSizes(resources.getDimension(R.dimen.stream_ui_selected_attachment_corner_radius))
39-
.build()
40-
load(imgurAttachment.imageUrl) {
41-
allowHardware(false)
42-
crossfade(true)
43-
placeholder(R.drawable.stream_ui_picture_placeholder)
38+
private class ImgurAttachmentViewHolder(
39+
binding: AttachmentImgurBinding,
40+
imgurAttachment: Attachment
41+
) :
42+
InnerAttachmentViewHolder(binding.root) {
43+
44+
init {
45+
binding.ivMediaThumb.apply {
46+
shapeAppearanceModel = shapeAppearanceModel
47+
.toBuilder()
48+
.setAllCornerSizes(resources.getDimension(R.dimen.stream_ui_selected_attachment_corner_radius))
49+
.build()
50+
load(imgurAttachment.imageUrl) {
51+
allowHardware(false)
52+
crossfade(true)
53+
placeholder(R.drawable.stream_ui_picture_placeholder)
54+
}
4455
}
4556
}
46-
47-
return binding.root
4857
}
49-
50-
}
58+
}

samplekotlin/src/main/java/com/example/chattutorial/MainActivity.kt

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import io.getstream.chat.android.client.ChatClient
88
import io.getstream.chat.android.client.logger.ChatLogLevel
99
import io.getstream.chat.android.client.models.Filters
1010
import io.getstream.chat.android.client.models.User
11-
import io.getstream.chat.android.livedata.ChatDomain
11+
import io.getstream.chat.android.offline.model.message.attachments.UploadAttachmentsNetworkType
12+
import io.getstream.chat.android.offline.plugin.configuration.Config
13+
import io.getstream.chat.android.offline.plugin.factory.StreamOfflinePluginFactory
1214
import io.getstream.chat.android.ui.channel.list.viewmodel.ChannelListViewModel
1315
import io.getstream.chat.android.ui.channel.list.viewmodel.bindView
1416
import io.getstream.chat.android.ui.channel.list.viewmodel.factory.ChannelListViewModelFactory
@@ -24,33 +26,46 @@ class MainActivity : AppCompatActivity() {
2426
binding = ActivityMainBinding.inflate(layoutInflater)
2527
setContentView(binding.root)
2628

27-
// Step 1 - Set up the client for API calls and the domain for offline storage
29+
// Step 1 - Set up the OfflinePlugin for offline storage
30+
val offlinePluginFactory = StreamOfflinePluginFactory(
31+
config = Config(
32+
backgroundSyncEnabled = true,
33+
userPresence = true,
34+
persistenceEnabled = true,
35+
uploadAttachmentsNetworkType = UploadAttachmentsNetworkType.NOT_ROAMING,
36+
),
37+
appContext = applicationContext,
38+
)
39+
40+
// Step 2 - Set up the client for API calls with the plugin for offline storage
2841
val client = ChatClient.Builder("b67pax5b2wdq", applicationContext)
42+
.withPlugin(offlinePluginFactory)
2943
.logLevel(ChatLogLevel.ALL) // Set to NOTHING in prod
3044
.build()
31-
ChatDomain.Builder(client, applicationContext).build()
3245

33-
// Step 2 - Authenticate and connect the user
34-
val user = User(id = "tutorial-droid").apply {
35-
name = "Tutorial Droid"
46+
// Step 3 - Authenticate and connect the user
47+
val user = User(
48+
id = "tutorial-droid",
49+
name = "Tutorial Droid",
3650
image = "https://bit.ly/2TIt8NR"
37-
}
51+
)
3852
client.connectUser(
3953
user = user,
4054
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidHV0b3JpYWwtZHJvaWQifQ.NhEr0hP9W9nwqV7ZkdShxvi02C5PR7SJE7Cs4y7kyqg"
4155
).enqueue()
4256

43-
// Step 3 - Set the channel list filter and order
57+
// Step 4 - Set the channel list filter and order
4458
// This can be read as requiring only channels whose "type" is "messaging" AND
4559
// whose "members" include our "user.id"
4660
val filter = Filters.and(
4761
Filters.eq("type", "messaging"),
4862
Filters.`in`("members", listOf(user.id))
4963
)
50-
val viewModelFactory = ChannelListViewModelFactory(filter, ChannelListViewModel.DEFAULT_SORT)
64+
val viewModelFactory =
65+
ChannelListViewModelFactory(filter, ChannelListViewModel.DEFAULT_SORT)
5166
val viewModel: ChannelListViewModel by viewModels { viewModelFactory }
5267

53-
// Step 4 - Connect the ChannelListViewModel to the ChannelListView, loose
68+
// Step 5 - Connect the ChannelListViewModel to the ChannelListView, loose
5469
// coupling makes it easy to customize
5570
viewModel.bindView(binding.channelListView, this)
5671
binding.channelListView.setChannelItemClickListener { channel ->

0 commit comments

Comments
 (0)