-
Notifications
You must be signed in to change notification settings - Fork 11
Update Tutorial to SDK 5.2.0 #65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d4c6f40
f9b0c6c
4c316c2
c687504
b7df9c0
721b418
b306a5c
8c9be54
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,12 +3,14 @@ | |
import android.content.Context; | ||
import android.content.Intent; | ||
import android.os.Bundle; | ||
import android.text.TextUtils; | ||
import android.widget.TextView; | ||
|
||
import androidx.activity.OnBackPressedCallback; | ||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
import androidx.appcompat.app.AppCompatActivity; | ||
import androidx.lifecycle.FlowLiveDataConversions; | ||
import androidx.lifecycle.LiveData; | ||
import androidx.lifecycle.Transformations; | ||
import androidx.lifecycle.ViewModelProvider; | ||
|
||
import com.example.chattutorial.databinding.ActivityChannel3Binding; | ||
|
@@ -18,20 +20,25 @@ | |
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.Mode.Thread; | ||
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.State.NavigateUp; | ||
|
||
import java.util.LinkedList; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import io.getstream.chat.android.client.ChatClient; | ||
import io.getstream.chat.android.client.models.Channel; | ||
import io.getstream.chat.android.client.models.Message; | ||
import io.getstream.chat.android.client.models.User; | ||
import io.getstream.chat.android.livedata.ChatDomain; | ||
import io.getstream.chat.android.livedata.controller.ChannelController; | ||
import io.getstream.chat.android.client.models.TypingEvent; | ||
import io.getstream.chat.android.offline.extensions.ChatClientExtensions; | ||
import io.getstream.chat.android.offline.plugin.state.channel.ChannelState; | ||
import io.getstream.chat.android.ui.message.input.viewmodel.MessageInputViewModelBinding; | ||
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactoryManager; | ||
import io.getstream.chat.android.ui.message.list.header.MessageListHeaderView; | ||
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModel; | ||
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModelBinding; | ||
import io.getstream.chat.android.ui.message.list.viewmodel.MessageListViewModelBinding; | ||
import io.getstream.chat.android.ui.message.list.viewmodel.factory.MessageListViewModelFactory; | ||
import kotlinx.coroutines.Dispatchers; | ||
import kotlinx.coroutines.flow.Flow; | ||
import kotlinx.coroutines.flow.FlowKt; | ||
|
||
public class ChannelActivity3 extends AppCompatActivity { | ||
|
||
|
@@ -64,12 +71,18 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { | |
MessageListViewModel messageListViewModel = provider.get(MessageListViewModel.class); | ||
MessageInputViewModel messageInputViewModel = provider.get(MessageInputViewModel.class); | ||
|
||
// Set view factory for Imgur attachments | ||
binding.messageListView.setAttachmentViewFactory(new ImgurAttachmentViewFactory()); | ||
// Set a view factory manager for Imgur attachments | ||
ImgurAttachmentFactory imgurAttachmentFactory = new ImgurAttachmentFactory(); | ||
|
||
List<ImgurAttachmentFactory> imgurAttachmentViewFactories = new ArrayList<ImgurAttachmentFactory>(); | ||
imgurAttachmentViewFactories.add(imgurAttachmentFactory); | ||
|
||
AttachmentFactoryManager attachmentFactoryManager = new AttachmentFactoryManager(imgurAttachmentViewFactories); | ||
binding.messageListView.setAttachmentFactoryManager(attachmentFactoryManager); | ||
|
||
// Step 2 - Bind the view and ViewModels, they are loosely coupled so it's easy to customize | ||
MessageListHeaderViewModelBinding.bind(messageListHeaderViewModel, binding.messageListHeaderView, this); | ||
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this); | ||
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this, true); | ||
MessageInputViewModelBinding.bind(messageInputViewModel, binding.messageInputView, this); | ||
|
||
// Step 3 - Let both MessageListHeaderView and MessageInputView know when we open a thread | ||
|
@@ -107,29 +120,43 @@ public void handleOnBackPressed() { | |
}); | ||
|
||
// Custom typing info header bar | ||
TextView typingHeaderView = findViewById(R.id.typingHeaderView); | ||
String nobodyTyping = "nobody is typing"; | ||
typingHeaderView.setText(nobodyTyping); | ||
|
||
// Obtain a ChannelController | ||
ChatDomain.instance() | ||
.getChannelController(cid) | ||
.enqueue((result) -> { | ||
ChannelController channelController = result.data(); | ||
|
||
// Observe typing users | ||
channelController.getTyping().observe(this, typingState -> { | ||
if (typingState.getUsers().isEmpty()) { | ||
typingHeaderView.setText(nobodyTyping); | ||
} else { | ||
List<String> userNames = new LinkedList<>(); | ||
for (User user : typingState.getUsers()) { | ||
userNames.add(user.getName()); | ||
} | ||
String typing = "typing: " + TextUtils.join(", ", userNames); | ||
typingHeaderView.setText(typing); | ||
} | ||
}); | ||
}); | ||
binding.typingHeaderView.setText(nobodyTyping); | ||
|
||
// Observe typing events and update typing header depending on its state. | ||
Flow<ChannelState> channelStateFlow = ChatClientExtensions.watchChannelAsState(ChatClient.instance(), cid, 30); | ||
LiveData<TypingEvent> typingEventLiveData = Transformations.switchMap( | ||
FlowLiveDataConversions.asLiveData(channelStateFlow), | ||
channelState -> FlowLiveDataConversions.asLiveData(channelState.getTyping()) | ||
); | ||
|
||
typingEventLiveData.observe(this, typingEvent -> { | ||
String headerText; | ||
|
||
if (typingEvent.getUsers().size() != 0) { | ||
headerText = "typing: " + joinTypingUpdatesToUserNames(typingEvent); | ||
} else { | ||
headerText = nobodyTyping; | ||
} | ||
|
||
binding.typingHeaderView.setText(headerText); | ||
}); | ||
} | ||
|
||
// Helper method that transforms typing updates into a string | ||
// containing typing member's names | ||
@NonNull | ||
private String joinTypingUpdatesToUserNames(@NonNull TypingEvent typingEvent) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have to do this manually because |
||
StringBuilder joinedString = new StringBuilder(); | ||
|
||
for (int i = 0; i < typingEvent.getUsers().size(); i++) { | ||
if (i < typingEvent.getUsers().size() - 1) { | ||
joinedString.append(typingEvent.getUsers().get(i).getName()).append(", "); | ||
} else { | ||
joinedString.append(typingEvent.getUsers().get(i).getName()); | ||
} | ||
} | ||
|
||
return joinedString.toString(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,9 @@ | |
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.Mode.Thread; | ||
import com.getstream.sdk.chat.viewmodel.messages.MessageListViewModel.State.NavigateUp; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
import io.getstream.chat.android.client.ChatClient; | ||
|
@@ -28,6 +30,7 @@ | |
import io.getstream.chat.android.client.models.Message; | ||
import io.getstream.chat.android.client.models.User; | ||
import io.getstream.chat.android.ui.message.input.viewmodel.MessageInputViewModelBinding; | ||
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactoryManager; | ||
import io.getstream.chat.android.ui.message.list.header.MessageListHeaderView; | ||
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModel; | ||
import io.getstream.chat.android.ui.message.list.header.viewmodel.MessageListHeaderViewModelBinding; | ||
|
@@ -65,12 +68,18 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { | |
MessageListViewModel messageListViewModel = provider.get(MessageListViewModel.class); | ||
MessageInputViewModel messageInputViewModel = provider.get(MessageInputViewModel.class); | ||
|
||
// Set view factory for Imgur attachments | ||
binding.messageListView.setAttachmentViewFactory(new ImgurAttachmentViewFactory()); | ||
// Set a view factory manager for Imgur attachments | ||
ImgurAttachmentFactory imgurAttachmentFactory = new ImgurAttachmentFactory(); | ||
|
||
List<ImgurAttachmentFactory> imgurAttachmentViewFactories = new ArrayList<ImgurAttachmentFactory>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, |
||
imgurAttachmentViewFactories.add(imgurAttachmentFactory); | ||
|
||
AttachmentFactoryManager attachmentFactoryManager = new AttachmentFactoryManager(imgurAttachmentViewFactories); | ||
binding.messageListView.setAttachmentFactoryManager(attachmentFactoryManager); | ||
|
||
// Step 2 - Bind the view and ViewModels, they are loosely coupled so it's easy to customize | ||
MessageListHeaderViewModelBinding.bind(messageListHeaderViewModel, binding.messageListHeaderView, this); | ||
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this); | ||
MessageListViewModelBinding.bind(messageListViewModel, binding.messageListView, this, true); | ||
MessageInputViewModelBinding.bind(messageInputViewModel, binding.messageInputView, this); | ||
|
||
// Step 3 - Let both MessageListHeaderView and MessageInputView know when we open a thread | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package com.example.chattutorial; | ||
|
||
import android.view.LayoutInflater; | ||
import android.view.ViewGroup; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
|
||
import com.example.chattutorial.databinding.AttachmentImgurBinding; | ||
import com.google.android.material.shape.ShapeAppearanceModel; | ||
|
||
import org.jetbrains.annotations.NotNull; | ||
|
||
import coil.Coil; | ||
import coil.request.ImageRequest; | ||
import io.getstream.chat.android.client.models.Attachment; | ||
import io.getstream.chat.android.client.models.Message; | ||
import io.getstream.chat.android.ui.message.list.adapter.MessageListListenerContainer; | ||
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactory; | ||
import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.InnerAttachmentViewHolder; | ||
|
||
/** A custom attachment factory to show an imgur logo if the attachment URL is an imgur image. **/ | ||
public class ImgurAttachmentFactory implements AttachmentFactory { | ||
|
||
|
||
// Step 1 - Check whether the message contains an Imgur attachment | ||
@Override | ||
public boolean canHandle(@NonNull Message message) { | ||
return containsImgurAttachments(message) != null; | ||
} | ||
|
||
// Step 2 - Create the ViewHolder that will be used to display the Imgur logo | ||
// over Imgur attachments | ||
@NonNull | ||
@Override | ||
public InnerAttachmentViewHolder createViewHolder( | ||
@NonNull Message message, | ||
@Nullable MessageListListenerContainer listeners, | ||
@NonNull ViewGroup parent | ||
) { | ||
Attachment imgurAttachment = containsImgurAttachments(message); | ||
|
||
AttachmentImgurBinding attachmentImgurBinding = AttachmentImgurBinding.inflate(LayoutInflater.from(parent.getContext()), null, false); | ||
|
||
return new ImgurAttachmentViewHolder(attachmentImgurBinding, imgurAttachment); | ||
} | ||
|
||
private Attachment containsImgurAttachments(@NotNull Message message) { | ||
for (int i = 0; i < message.getAttachments().size(); i++) { | ||
String imageUrl = message.getAttachments().get(i).getImageUrl(); | ||
|
||
if (imageUrl != null && imageUrl.contains("imgur")) { | ||
return message.getAttachments().get(i); | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private static class ImgurAttachmentViewHolder extends InnerAttachmentViewHolder { | ||
|
||
public ImgurAttachmentViewHolder(AttachmentImgurBinding binding, | ||
@Nullable Attachment imgurAttachment) { | ||
super(binding.getRoot()); | ||
|
||
ShapeAppearanceModel shapeAppearanceModel = binding.ivMediaThumb.getShapeAppearanceModel() | ||
.toBuilder() | ||
.setAllCornerSizes(binding.ivMediaThumb.getResources().getDimension(io.getstream.chat.android.ui.R.dimen.stream_ui_selected_attachment_corner_radius)) | ||
.build(); | ||
|
||
binding.ivMediaThumb.setShapeAppearanceModel(shapeAppearanceModel); | ||
|
||
if (imgurAttachment != null) { | ||
ImageRequest imageRequest = new ImageRequest.Builder(binding.getRoot().getContext()) | ||
.data(imgurAttachment.getImageUrl()) | ||
.allowHardware(false) | ||
.crossfade(true) | ||
.placeholder(io.getstream.chat.android.ui.R.drawable.stream_ui_picture_placeholder) | ||
.target(binding.ivMediaThumb) | ||
.build(); | ||
Coil.imageLoader(binding.getRoot().getContext()).enqueue(imageRequest); | ||
} | ||
} | ||
} | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very clunky, but we currently don't expose any of our state classes (
ChannelState
,GlobalState
, ...) asLiveData
.It was either this or suggesting that Java users use Coroutines, which I feel is a bigger unknown in the Java world + the syntax would still be clunky since
Flow
operators are written as extensions.