ChatKit icon indicating copy to clipboard operation
ChatKit copied to clipboard

Setting image by byte stream array

Open pingpingsim opened this issue 5 years ago • 4 comments

How do I set image message by byte stream array instead of image url? I can't seem to find an example that does that. Please help.

pingpingsim avatar Feb 18 '20 06:02 pingpingsim

@pingpingsim https://github.com/stfalcon-studio/ChatKit/blob/master/docs/IMAGE_LOADER.md It looks like you can override it and use something called Picasso to load a byte stream array instead of the url. This could help: https://stackoverflow.com/questions/34629424/how-to-load-bitmap-directly-with-picasso-library-like-following or https://stackoverflow.com/questions/33959599/android-load-byte-into-imageview-with-picasso

COULD is the keyword. Haven't developed Android in awhile (getting back to it). There's probably another way of loading your byte stream into the image view though.

Andrew-Chen-Wang avatar Feb 21 '20 21:02 Andrew-Chen-Wang

I think override and loading the image with Picasso or Glide is the way to go.

The given ImageMessageViewHolder's onBind method expect to retrieve a url from the message to be used in loadImage

https://github.com/stfalcon-studio/ChatKit/blob/master/chatkit/src/main/java/com/stfalcon/chatkit/messages/MessagesListAdapter.java
image

Then at the ImageLoader you are given only the url
https://github.com/stfalcon-studio/ChatKit/blob/master/chatkit/src/main/java/com/stfalcon/chatkit/commons/ImageLoader.java image

Furthermore, this library check if a message has null url to determine if it is a image type
image

This library does not consider the use case of loading image other than using url

yatw avatar Feb 22 '21 21:02 yatw

@pingpingsim
Instead of using the given class in the library, create your own class that extend from the library class

Create your own ImageLoader class that implement ImageLoader,
for example

public class UidlImageLoader implements ImageLoader {
    Context context;
    public UidlImageLoader(Context context){
        this.context = context;
    }
    public void loadImage(ImageView imageView, byte[] data, @Nullable Object payload) { 
                        Glide.with(context)
                                .asBitmap()
                                .load(data)
                                .into(imageView);
    }

Create your own ImageMessage class

public class ImageMessage implements IMessage,
        MessageContentType.Image {

    private final Author author;
    private final byte[] imageData;
    private final Date createdTime;

    public ImageMessage(Author author, byte[] imageData, Date createdTime){
        this.author = author;
        this.imageData= imageData;
        this.createdTime = createdTime;
    }

Here is the tricky part, create your own CustomOutcomingImageMessageViewHolder and override its onBind method,
it will get the byte data from your own ImageMessage class, and use your own ImageLoader to do the loading

    /**
     * Default view holder implementation for outcoming image message
     */
    public static class CustomOutcomingImageMessageViewHolder<MESSAGE extends MessageContentType.Image>
            extends CustomBaseOutcomingMessageViewHolder<MESSAGE> {

        protected ImageView imageView;
        protected View imageOverlay;

        public CustomOutcomingImageMessageViewHolder(View itemView, Object payload, CustomMessagesListStyle style) {
            super(itemView, payload, style);
            init(itemView);
        }

        @Override
        public void onBind(MESSAGE message) {
            super.onBind(message);
            if (imageView != null && imageLoader instanceof UidlImageLoader
                    && message instanceof ImageMessage) {
                ImageMessage imageMessage = (ImageMessage)message;
                byte[] imageData = imageMessage.getImageByte();

                UidlImageLoader uidlImageLoader = (UidlImageLoader)imageLoader;
                uidlImageLoader.loadImage(imageView, imageData , getPayloadForImageLoader(message));
            }
            if (imageOverlay != null) {
                imageOverlay.setSelected(isSelected());
            }
        }

You need to make a wrapper class because the generic constraint

    public static class UidlOutComingImageVH extends CustomOutcomingImageMessageViewHolder<MessageContentType.Image> {
        public UidlOutComingImageVH(View itemView) {
            super(itemView, null, style);
        }
    }

finally tell the library to use your own class for ImageHolder

        // set the layout of the incoming dialogue bubbles
        MessageHolders messageHolders = new MessageHolders();
        messageHolders.setOutcomingImageHolder(UidlOutComingImageVH.class);

yatw avatar Feb 22 '21 22:02 yatw

Just override getPayloadForImageLoader in custom VH and implement own logic in ImageLoader, in your case replace string with byte stream array and customize logic in ImageLoader.

class CustomOutcomingImageMessageViewHolder(private val itemView: View, payload: Any?) :
        MessageHolders.IncomingImageMessageViewHolder<Message>(itemView, payload) {

    override fun onBind(message: Message) {
        super.onBind(message)
        time?.text = "${DateFormatter.format(message.createdAt, DateFormatter.Template.TIME)} Status"

    }

    override fun getPayloadForImageLoader(message: Message?): Any? {
        return  message?.image?.localImagePath
    }
}
  // Create image loader for adapter
  this.imageLoader = ImageLoader { imageView: ImageView, url: String?, payload: Any? ->
      if (url.isNullOrEmpty()) {
          imageView.load(File(payload as (String)))
      } else {
          imageView.load(url)
      }
  }

KamikX avatar Mar 18 '21 10:03 KamikX