Skip to content

Commit 7f1049e

Browse files
authored
Merge pull request #65 from GetStream/update/5.2.0
Update Tutorial to SDK 5.2.0
2 parents 9913c5d + 8c9be54 commit 7f1049e

File tree

13 files changed

+212
-127
lines changed

13 files changed

+212
-127
lines changed

samplejava/build.gradle

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ android {
3030

3131
dependencies {
3232
// Add new dependencies
33-
implementation "io.getstream:stream-chat-android-ui-components:4.28.2"
34-
implementation "com.google.android.material:material:1.4.0"
33+
implementation "io.getstream:stream-chat-android-ui-components:5.2.0"
34+
implementation "com.google.android.material:material:1.6.0"
3535
implementation "androidx.activity:activity-ktx:1.4.0"
3636
implementation "io.coil-kt:coil:1.4.0"
37+
38+
// We use the ktx dependency for transforming StateFlow into LiveData
39+
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
3740
}

samplejava/src/main/java/com/example/chattutorial/ChannelActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
6060

6161
// Step 2 - Bind the view and ViewModels, they are loosely coupled so it's easy to customize
6262
MessageListHeaderViewModelBinding.bind(messageListHeaderViewModel, binding.messageListHeaderView, this);
63-
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this);
63+
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this, true);
6464
MessageInputViewModelBinding.bind(messageInputViewModel, binding.messageInputView, this);
6565

6666
// Step 3 - Let both MessageListHeaderView and MessageInputView know when we open a thread

samplejava/src/main/java/com/example/chattutorial/ChannelActivity2.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.Mode.Thread;
1717
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.State.NavigateUp;
1818

19+
import java.util.ArrayList;
20+
import java.util.List;
21+
1922
import io.getstream.chat.android.client.models.Channel;
2023
import io.getstream.chat.android.client.models.Message;
2124
import io.getstream.chat.android.ui.message.input.viewmodel.MessageInputViewModelBinding;
25+
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactoryManager;
2226
import io.getstream.chat.android.ui.message.list.header.MessageListHeaderView;
2327
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModel;
2428
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModelBinding;
@@ -56,12 +60,18 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
5660
MessageListViewModel messageListViewModel = provider.get(MessageListViewModel.class);
5761
MessageInputViewModel messageInputViewModel = provider.get(MessageInputViewModel.class);
5862

59-
// Set view factory for Imgur attachments
60-
binding.messageListView.setAttachmentViewFactory(new ImgurAttachmentViewFactory());
63+
// Set a view factory manager for Imgur attachments
64+
ImgurAttachmentFactory imgurAttachmentFactory = new ImgurAttachmentFactory();
65+
66+
List<ImgurAttachmentFactory> imgurAttachmentViewFactories = new ArrayList<ImgurAttachmentFactory>();
67+
imgurAttachmentViewFactories.add(imgurAttachmentFactory);
68+
69+
AttachmentFactoryManager attachmentFactoryManager = new AttachmentFactoryManager(imgurAttachmentViewFactories);
70+
binding.messageListView.setAttachmentFactoryManager(attachmentFactoryManager);
6171

6272
// Step 2 - Bind the view and ViewModels, they are loosely coupled so it's easy to customize
6373
MessageListHeaderViewModelBinding.bind(messageListHeaderViewModel, binding.messageListHeaderView, this);
64-
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this);
74+
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this, true);
6575
MessageInputViewModelBinding.bind(messageInputViewModel, binding.messageInputView, this);
6676

6777
// Step 3 - Let both MessageListHeaderView and MessageInputView know when we open a thread

samplejava/src/main/java/com/example/chattutorial/ChannelActivity3.java

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
import android.content.Context;
44
import android.content.Intent;
55
import android.os.Bundle;
6-
import android.text.TextUtils;
7-
import android.widget.TextView;
86

97
import androidx.activity.OnBackPressedCallback;
8+
import androidx.annotation.NonNull;
109
import androidx.annotation.Nullable;
1110
import androidx.appcompat.app.AppCompatActivity;
11+
import androidx.lifecycle.FlowLiveDataConversions;
12+
import androidx.lifecycle.LiveData;
13+
import androidx.lifecycle.Transformations;
1214
import androidx.lifecycle.ViewModelProvider;
1315

1416
import com.example.chattutorial.databinding.ActivityChannel3Binding;
@@ -18,20 +20,25 @@
1820
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.Mode.Thread;
1921
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.State.NavigateUp;
2022

21-
import java.util.LinkedList;
23+
import java.util.ArrayList;
2224
import java.util.List;
2325

26+
import io.getstream.chat.android.client.ChatClient;
2427
import io.getstream.chat.android.client.models.Channel;
2528
import io.getstream.chat.android.client.models.Message;
26-
import io.getstream.chat.android.client.models.User;
27-
import io.getstream.chat.android.livedata.ChatDomain;
28-
import io.getstream.chat.android.livedata.controller.ChannelController;
29+
import io.getstream.chat.android.client.models.TypingEvent;
30+
import io.getstream.chat.android.offline.extensions.ChatClientExtensions;
31+
import io.getstream.chat.android.offline.plugin.state.channel.ChannelState;
2932
import io.getstream.chat.android.ui.message.input.viewmodel.MessageInputViewModelBinding;
33+
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactoryManager;
3034
import io.getstream.chat.android.ui.message.list.header.MessageListHeaderView;
3135
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModel;
3236
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModelBinding;
3337
import io.getstream.chat.android.ui.message.list.viewmodel.MessageListViewModelBinding;
3438
import io.getstream.chat.android.ui.message.list.viewmodel.factory.MessageListViewModelFactory;
39+
import kotlinx.coroutines.Dispatchers;
40+
import kotlinx.coroutines.flow.Flow;
41+
import kotlinx.coroutines.flow.FlowKt;
3542

3643
public class ChannelActivity3 extends AppCompatActivity {
3744

@@ -64,12 +71,18 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
6471
MessageListViewModel messageListViewModel = provider.get(MessageListViewModel.class);
6572
MessageInputViewModel messageInputViewModel = provider.get(MessageInputViewModel.class);
6673

67-
// Set view factory for Imgur attachments
68-
binding.messageListView.setAttachmentViewFactory(new ImgurAttachmentViewFactory());
74+
// Set a view factory manager for Imgur attachments
75+
ImgurAttachmentFactory imgurAttachmentFactory = new ImgurAttachmentFactory();
76+
77+
List<ImgurAttachmentFactory> imgurAttachmentViewFactories = new ArrayList<ImgurAttachmentFactory>();
78+
imgurAttachmentViewFactories.add(imgurAttachmentFactory);
79+
80+
AttachmentFactoryManager attachmentFactoryManager = new AttachmentFactoryManager(imgurAttachmentViewFactories);
81+
binding.messageListView.setAttachmentFactoryManager(attachmentFactoryManager);
6982

7083
// Step 2 - Bind the view and ViewModels, they are loosely coupled so it's easy to customize
7184
MessageListHeaderViewModelBinding.bind(messageListHeaderViewModel, binding.messageListHeaderView, this);
72-
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this);
85+
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this, true);
7386
MessageInputViewModelBinding.bind(messageInputViewModel, binding.messageInputView, this);
7487

7588
// Step 3 - Let both MessageListHeaderView and MessageInputView know when we open a thread
@@ -107,29 +120,43 @@ public void handleOnBackPressed() {
107120
});
108121

109122
// Custom typing info header bar
110-
TextView typingHeaderView = findViewById(R.id.typingHeaderView);
111123
String nobodyTyping = "nobody is typing";
112-
typingHeaderView.setText(nobodyTyping);
113-
114-
// Obtain a ChannelController
115-
ChatDomain.instance()
116-
.getChannelController(cid)
117-
.enqueue((result) -> {
118-
ChannelController channelController = result.data();
119-
120-
// Observe typing users
121-
channelController.getTyping().observe(this, typingState -> {
122-
if (typingState.getUsers().isEmpty()) {
123-
typingHeaderView.setText(nobodyTyping);
124-
} else {
125-
List<String> userNames = new LinkedList<>();
126-
for (User user : typingState.getUsers()) {
127-
userNames.add(user.getName());
128-
}
129-
String typing = "typing: " + TextUtils.join(", ", userNames);
130-
typingHeaderView.setText(typing);
131-
}
132-
});
133-
});
124+
binding.typingHeaderView.setText(nobodyTyping);
125+
126+
// Observe typing events and update typing header depending on its state.
127+
Flow<ChannelState> channelStateFlow = ChatClientExtensions.watchChannelAsState(ChatClient.instance(), cid, 30);
128+
LiveData<TypingEvent> typingEventLiveData = Transformations.switchMap(
129+
FlowLiveDataConversions.asLiveData(channelStateFlow),
130+
channelState -> FlowLiveDataConversions.asLiveData(channelState.getTyping())
131+
);
132+
133+
typingEventLiveData.observe(this, typingEvent -> {
134+
String headerText;
135+
136+
if (typingEvent.getUsers().size() != 0) {
137+
headerText = "typing: " + joinTypingUpdatesToUserNames(typingEvent);
138+
} else {
139+
headerText = nobodyTyping;
140+
}
141+
142+
binding.typingHeaderView.setText(headerText);
143+
});
144+
}
145+
146+
// Helper method that transforms typing updates into a string
147+
// containing typing member's names
148+
@NonNull
149+
private String joinTypingUpdatesToUserNames(@NonNull TypingEvent typingEvent) {
150+
StringBuilder joinedString = new StringBuilder();
151+
152+
for (int i = 0; i < typingEvent.getUsers().size(); i++) {
153+
if (i < typingEvent.getUsers().size() - 1) {
154+
joinedString.append(typingEvent.getUsers().get(i).getName()).append(", ");
155+
} else {
156+
joinedString.append(typingEvent.getUsers().get(i).getName());
157+
}
158+
}
159+
160+
return joinedString.toString();
134161
}
135162
}

samplejava/src/main/java/com/example/chattutorial/ChannelActivity4.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.Mode.Thread;
1919
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.State.NavigateUp;
2020

21+
import java.util.ArrayList;
2122
import java.util.HashSet;
23+
import java.util.List;
2224
import java.util.Set;
2325

2426
import io.getstream.chat.android.client.ChatClient;
@@ -28,6 +30,7 @@
2830
import io.getstream.chat.android.client.models.Message;
2931
import io.getstream.chat.android.client.models.User;
3032
import io.getstream.chat.android.ui.message.input.viewmodel.MessageInputViewModelBinding;
33+
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactoryManager;
3134
import io.getstream.chat.android.ui.message.list.header.MessageListHeaderView;
3235
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModel;
3336
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModelBinding;
@@ -65,12 +68,18 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
6568
MessageListViewModel messageListViewModel = provider.get(MessageListViewModel.class);
6669
MessageInputViewModel messageInputViewModel = provider.get(MessageInputViewModel.class);
6770

68-
// Set view factory for Imgur attachments
69-
binding.messageListView.setAttachmentViewFactory(new ImgurAttachmentViewFactory());
71+
// Set a view factory manager for Imgur attachments
72+
ImgurAttachmentFactory imgurAttachmentFactory = new ImgurAttachmentFactory();
73+
74+
List<ImgurAttachmentFactory> imgurAttachmentViewFactories = new ArrayList<ImgurAttachmentFactory>();
75+
imgurAttachmentViewFactories.add(imgurAttachmentFactory);
76+
77+
AttachmentFactoryManager attachmentFactoryManager = new AttachmentFactoryManager(imgurAttachmentViewFactories);
78+
binding.messageListView.setAttachmentFactoryManager(attachmentFactoryManager);
7079

7180
// Step 2 - Bind the view and ViewModels, they are loosely coupled so it's easy to customize
7281
MessageListHeaderViewModelBinding.bind(messageListHeaderViewModel, binding.messageListHeaderView, this);
73-
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this);
82+
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this, true);
7483
MessageInputViewModelBinding.bind(messageInputViewModel, binding.messageInputView, this);
7584

7685
// Step 3 - Let both MessageListHeaderView and MessageInputView know when we open a thread
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.example.chattutorial;
2+
3+
import android.view.LayoutInflater;
4+
import android.view.ViewGroup;
5+
6+
import androidx.annotation.NonNull;
7+
import androidx.annotation.Nullable;
8+
9+
import com.example.chattutorial.databinding.AttachmentImgurBinding;
10+
import com.google.android.material.shape.ShapeAppearanceModel;
11+
12+
import org.jetbrains.annotations.NotNull;
13+
14+
import coil.Coil;
15+
import coil.request.ImageRequest;
16+
import io.getstream.chat.android.client.models.Attachment;
17+
import io.getstream.chat.android.client.models.Message;
18+
import io.getstream.chat.android.ui.message.list.adapter.MessageListListenerContainer;
19+
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactory;
20+
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.InnerAttachmentViewHolder;
21+
22+
/** A custom attachment factory to show an imgur logo if the attachment URL is an imgur image. **/
23+
public class ImgurAttachmentFactory implements AttachmentFactory {
24+
25+
26+
// Step 1 - Check whether the message contains an Imgur attachment
27+
@Override
28+
public boolean canHandle(@NonNull Message message) {
29+
return containsImgurAttachments(message) != null;
30+
}
31+
32+
// Step 2 - Create the ViewHolder that will be used to display the Imgur logo
33+
// over Imgur attachments
34+
@NonNull
35+
@Override
36+
public InnerAttachmentViewHolder createViewHolder(
37+
@NonNull Message message,
38+
@Nullable MessageListListenerContainer listeners,
39+
@NonNull ViewGroup parent
40+
) {
41+
Attachment imgurAttachment = containsImgurAttachments(message);
42+
43+
AttachmentImgurBinding attachmentImgurBinding = AttachmentImgurBinding.inflate(LayoutInflater.from(parent.getContext()), null, false);
44+
45+
return new ImgurAttachmentViewHolder(attachmentImgurBinding, imgurAttachment);
46+
}
47+
48+
private Attachment containsImgurAttachments(@NotNull Message message) {
49+
for (int i = 0; i < message.getAttachments().size(); i++) {
50+
String imageUrl = message.getAttachments().get(i).getImageUrl();
51+
52+
if (imageUrl != null && imageUrl.contains("imgur")) {
53+
return message.getAttachments().get(i);
54+
}
55+
}
56+
57+
return null;
58+
}
59+
60+
private static class ImgurAttachmentViewHolder extends InnerAttachmentViewHolder {
61+
62+
public ImgurAttachmentViewHolder(AttachmentImgurBinding binding,
63+
@Nullable Attachment imgurAttachment) {
64+
super(binding.getRoot());
65+
66+
ShapeAppearanceModel shapeAppearanceModel = binding.ivMediaThumb.getShapeAppearanceModel()
67+
.toBuilder()
68+
.setAllCornerSizes(binding.ivMediaThumb.getResources().getDimension(io.getstream.chat.android.ui.R.dimen.stream_ui_selected_attachment_corner_radius))
69+
.build();
70+
71+
binding.ivMediaThumb.setShapeAppearanceModel(shapeAppearanceModel);
72+
73+
if (imgurAttachment != null) {
74+
ImageRequest imageRequest = new ImageRequest.Builder(binding.getRoot().getContext())
75+
.data(imgurAttachment.getImageUrl())
76+
.allowHardware(false)
77+
.crossfade(true)
78+
.placeholder(io.getstream.chat.android.ui.R.drawable.stream_ui_picture_placeholder)
79+
.target(binding.ivMediaThumb)
80+
.build();
81+
Coil.imageLoader(binding.getRoot().getContext()).enqueue(imageRequest);
82+
}
83+
}
84+
}
85+
}

samplejava/src/main/java/com/example/chattutorial/ImgurAttachmentViewFactory.java

Lines changed: 0 additions & 68 deletions
This file was deleted.

0 commit comments

Comments
 (0)