I have problem, because I can't send images for other side, it duplicates or sends empty message, but normal messages work fine. I've tried many tutorials and changing some classes, but nothing really worked
here's my recycler view class:
public final class ChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<ChatMessage> messages;
private Bitmap profileImage;
private final String senderId;
private static final int VIEW_TYPE_SENT = 1;
private static final int VIEW_TYPE_RECEIVED = 2;
public void setReceiverProfileImage(Bitmap bitmap) {
this.profileImage = bitmap;
}
public ChatAdapter(List<ChatMessage> messages, Bitmap profileImage, String senderId) {
this.messages = messages;
this.profileImage = profileImage;
this.senderId = senderId;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return (viewType == VIEW_TYPE_SENT ? new SentMessageViewHolder(
ItemContainerSentMessageBinding.inflate(
LayoutInflater.from(parent.getContext()),
parent,
false)
) : new ReceivedMessageViewHolder(
ItemContainerReceivedMessageBinding.inflate(
LayoutInflater.from(parent.getContext()),
parent,
false)
));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == VIEW_TYPE_SENT) {
((SentMessageViewHolder) holder).setData(messages.get(position));
} else {
((ReceivedMessageViewHolder) holder).setData(messages.get(position), profileImage);
}
}
@Override
public int getItemCount() {
return messages.size();
}
@Override
public int getItemViewType(int position) {
return (messages.get(position).getSenderId().equals(senderId) ? VIEW_TYPE_SENT : VIEW_TYPE_RECEIVED);
}
public static class SentMessageViewHolder extends RecyclerView.ViewHolder {
private final ItemContainerSentMessageBinding binding;
public SentMessageViewHolder(ItemContainerSentMessageBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
void setData(ChatMessage message) {
binding.textMessage.setText(message.getMessage());
binding.textDateTime.setText(message.getDateTime());
binding.image.setImageBitmap(message.getImage());
}
}
public static class ReceivedMessageViewHolder extends RecyclerView.ViewHolder {
private final ItemContainerReceivedMessageBinding binding;
public ReceivedMessageViewHolder(ItemContainerReceivedMessageBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
void setData(ChatMessage message, Bitmap bitmap) {
binding.textMessage.setText(message.getMessage());
binding.textDateTime.setText(message.getDateTime());
if (bitmap != null) {
binding.imageProfile.setImageBitmap(bitmap);
}
binding.image.setImageBitmap(message.getImage());
}
}
}
Here's received message:
<com.makeramen.roundedimageview.RoundedImageView
android:id="@ id/imageProfile"
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/background_image"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="@id/textMessage"
app:layout_constraintStart_toStartOf="parent"
app:riv_oval="true"/>
<TextView
android:id="@ id/textMessage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:background="@drawable/background_received_message"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
app:layout_constraintWidth_max="wrap"
android:textColor="@color/white"
android:textSize="13sp"
app:layout_constraintStart_toEndOf="@id/imageProfile"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.75"/>
<ImageView
android:id="@ id/image"
android:background="@drawable/background_sent_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_marginStart="4dp"
android:maxWidth="250dp"
android:maxHeight="250dp"
app:layout_constraintStart_toEndOf="@id/imageProfile"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@ id/textDateTime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textColor="@color/secondary_text"
android:textSize="8sp"
app:layout_constraintStart_toStartOf="@id/textMessage"
app:layout_constraintTop_toBottomOf="@id/textMessage"/>
and picking image activity:
private final ActivityResultLauncher<Intent> pickImage = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() != RESULT_OK) return;
if (result.getData() == null) return;
Uri imageUri = result.getData().getData();
try {
InputStream inputStream = getContentResolver().openInputStream(imageUri);
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
this.encodingImage = encodeImage(bitmap);
sendMessage();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
});
and here create ChatMessage object (eventListener):
private final EventListener<QuerySnapshot> eventListener = (value, error) -> {
if (error != null) {
return;
}
if (value != null) {
int count = messages.size();
for (DocumentChange documentChange : value.getDocumentChanges()) {
if (documentChange.getType() == DocumentChange.Type.ADDED) {
ChatMessage chatMessage = new ChatMessage(
documentChange.getDocument().getString(Constants.KEY_SENDER_ID),
documentChange.getDocument().getString(Constants.KEY_RECEIVER_ID),
documentChange.getDocument().getString(Constants.KEY_MESSAGE),
getReadableDateTime(documentChange.getDocument().getDate(Constants.KEY_TIMESTAMP)),
documentChange.getDocument().getDate(Constants.KEY_TIMESTAMP)
);
if (!encodingImage.isEmpty()) {
byte[] decodedString = Base64.decode(encodingImage, Base64.DEFAULT);
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
chatMessage.setImage(decodedByte);
}
messages.add(chatMessage);
}
}
Collections.sort(messages, Comparator.comparing(ChatMessage::getDate));
if (count == 0) {
chatAdapter.notifyDataSetChanged();
} else {
chatAdapter.notifyItemRangeInserted(messages.size(), messages.size());
binding.chatRecyclerView.smoothScrollToPosition(messages.size() - 1);
}
binding.chatRecyclerView.setVisibility(View.VISIBLE);
}
binding.progressBar.setVisibility(View.GONE);
if (conversionId == null) checkForConversion();
};
sendMessage method:
private void sendMessage() {
Map<String, Object> message = new HashMap<>();
message.put(Constants.KEY_SENDER_ID, preferenceManager.getString(Constants.KEY_USER_ID));
message.put(Constants.KEY_RECEIVER_ID, user.getId());
message.put(Constants.KEY_MESSAGE, binding.inputMessage.getText().toString());
if (!encodingImage.isEmpty()) {
message.put(Constants.KEY_IMAGE_MESSAGE, encodingImage);
}
message.put(Constants.KEY_TIMESTAMP, new Date());
db.collection(Constants.KEY_COLLECTION_CHAT).add(message);
if (conversionId != null) {
updateConversion(binding.inputMessage.getText().toString());
} else {
Map<String, Object> conversion = new HashMap<>();
conversion.put(Constants.KEY_SENDER_ID, preferenceManager.getString(Constants.KEY_USER_ID));
conversion.put(Constants.KEY_SENDER_NAME, preferenceManager.getString(Constants.KEY_NAME));
conversion.put(Constants.KEY_SENDER_IMAGE, preferenceManager.getString(Constants.KEY_IMAGE));
conversion.put(Constants.KEY_RECEIVER_ID, user.getId());
conversion.put(Constants.KEY_RECEIVER_NAME, user.getName());
conversion.put(Constants.KEY_RECEIVER_IMAGE, user.getImage());
conversion.put(Constants.KEY_LAST_MESSAGE, binding.inputMessage.getText().toString());
conversion.put(Constants.KEY_TIMESTAMP, new Date());
addConversion(conversion);
}
if (!isReceiverAvaible) {
try {
JSONArray tokens = new JSONArray();
tokens.put(user.getToken());
JSONObject data = new JSONObject();
data.put(Constants.KEY_USER_ID, preferenceManager.getString(Constants.KEY_USER_ID));
data.put(Constants.KEY_NAME, preferenceManager.getString(Constants.KEY_NAME));
data.put(Constants.KEY_FCM_TOKEN, preferenceManager.getString(Constants.KEY_FCM_TOKEN));
data.put(Constants.KEY_MESSAGE, binding.inputMessage.getText().toString());
JSONObject body = new JSONObject();
body.put(Constants.REMOTE_MSG_DATA, data);
body.put(Constants.REMOTE_MSG_REGISTRATION_IDS, tokens);
sendNotification(body.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
binding.inputMessage.setText(null);
}
listenMessages method:
private void listenMessages() {
db.collection(Constants.KEY_COLLECTION_CHAT)
.whereEqualTo(Constants.KEY_SENDER_ID, preferenceManager.getString(Constants.KEY_USER_ID))
.whereEqualTo(Constants.KEY_RECEIVER_ID, user.getId())
.addSnapshotListener(eventListener);
db.collection(Constants.KEY_COLLECTION_CHAT)
.whereEqualTo(Constants.KEY_SENDER_ID, user.getId())
.whereEqualTo(Constants.KEY_RECEIVER_ID, preferenceManager.getString(Constants.KEY_USER_ID))
.addSnapshotListener(eventListener);
}
private String encodeImage(Bitmap bitmap) {
int previewWidth = 300;
int previewHeight = bitmap.getHeight() * previewWidth / bitmap.getWidth();
Bitmap previewBitmap = Bitmap.createScaledBitmap(bitmap, previewWidth, previewHeight, false);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
previewBitmap.compress(Bitmap.CompressFormat.JPEG, 50, byteArrayOutputStream);
return Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.DEFAULT);
}
Please help and thanks!
CodePudding user response:
The issue
You are actually providing the adapter the encodingImage
which was just created and that is why you get repeated or no image at all.
The solution
There are 2 parts in this solution:
- Reformat the methods and their parameters
- Change the code to pick image for sending image
Let's start !
1. Reformat the methods and their parameters
First of all, you need to add 1 parameter to the sendMessage()
method. It will look something like this:
private void sendMessage(String encodedImage) {
Map<String, Object> message = new HashMap<>();
message.put(Constants.KEY_SENDER_ID, preferenceManager.getString(Constants.KEY_USER_ID));
message.put(Constants.KEY_RECEIVER_ID, user.getId());
message.put(Constants.KEY_MESSAGE, binding.inputMessage.getText().toString());
message.put(Constants.KEY_IMAGE_MESSAGE, encodedImage);
message.put(Constants.KEY_TIMESTAMP, new Date());
db.collection(Constants.KEY_COLLECTION_CHAT).add(message);
if (conversionId != null) {
updateConversion(binding.inputMessage.getText().toString());
} else {
Map<String, Object> conversion = new HashMap<>();
conversion.put(Constants.KEY_SENDER_ID, preferenceManager.getString(Constants.KEY_USER_ID));
conversion.put(Constants.KEY_SENDER_NAME, preferenceManager.getString(Constants.KEY_NAME));
conversion.put(Constants.KEY_SENDER_IMAGE, preferenceManager.getString(Constants.KEY_IMAGE));
conversion.put(Constants.KEY_RECEIVER_ID, user.getId());
conversion.put(Constants.KEY_RECEIVER_NAME, user.getName());
conversion.put(Constants.KEY_RECEIVER_IMAGE, user.getImage());
conversion.put(Constants.KEY_LAST_MESSAGE, binding.inputMessage.getText().toString());
conversion.put(Constants.KEY_TIMESTAMP, new Date());
addConversion(conversion);
}
if (!isReceiverAvaible) {
try {
JSONArray tokens = new JSONArray();
tokens.put(user.getToken());
JSONObject data = new JSONObject();
data.put(Constants.KEY_USER_ID, preferenceManager.getString(Constants.KEY_USER_ID));
data.put(Constants.KEY_NAME, preferenceManager.getString(Constants.KEY_NAME));
data.put(Constants.KEY_FCM_TOKEN, preferenceManager.getString(Constants.KEY_FCM_TOKEN));
data.put(Constants.KEY_MESSAGE, binding.inputMessage.getText().toString());
JSONObject body = new JSONObject();
body.put(Constants.REMOTE_MSG_DATA, data);
body.put(Constants.REMOTE_MSG_REGISTRATION_IDS, tokens);
sendNotification(body.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
binding.inputMessage.setText(null);
}
Now after this, you need to change the eventListener
object. It will look like this:
private final EventListener<QuerySnapshot> eventListener = (value, error) -> {
if (error != null) {
return;
}
if (value != null) {
int count = messages.size();
for (DocumentChange documentChange : value.getDocumentChanges()) {
if (documentChange.getType() == DocumentChange.Type.ADDED) {
ChatMessage chatMessage = new ChatMessage(
documentChange.getDocument().getString(Constants.KEY_SENDER_ID),
documentChange.getDocument().getString(Constants.KEY_RECEIVER_ID),
documentChange.getDocument().getString(Constants.KEY_MESSAGE),
getReadableDateTime(documentChange.getDocument().getDate(Constants.KEY_TIMESTAMP)),
documentChange.getDocument().getDate(Constants.KEY_TIMESTAMP)
);
if (documentChange.getDocument().getString(Constants.KEY_IMAGE_MESSAGE) != null && !documentChange.getDocument().getString(Constants.KEY_IMAGE_MESSAGE).isEmpty()) {
byte[] decodedString = Base64.decode((documentChange.getDocument().getString(Constants.KEY_IMAGE_MESSAGE, Base64.DEFAULT);
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
chatMessage.setImage(decodedByte);
}
messages.add(chatMessage);
}
}
Collections.sort(messages, Comparator.comparing(ChatMessage::getDate));
if (count == 0) {
chatAdapter.notifyDataSetChanged();
} else {
chatAdapter.notifyItemRangeInserted(messages.size(), messages.size());
binding.chatRecyclerView.smoothScrollToPosition(messages.size() - 1);
}
binding.chatRecyclerView.setVisibility(View.VISIBLE);
}
binding.progressBar.setVisibility(View.GONE);
if (conversionId == null) checkForConversion();
};
2. Change the code to pick image for sending image
Now you are doing a very very wrong thing while trying to send an image method. This issue start's from the place where you are picking the image. To solve it, replace that with this code:
private final ActivityResultLauncher<Intent> pickImage = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() != RESULT_OK) return;
if (result.getData() == null) return;
Uri imageUri = result.getData().getData();
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), imageUri);
sendMessage(encodeImage(bitmap));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
});
Also you need to change the encodeImage
method now!:
private String encodeImage(Bitmap bitmap) {
int previewWidth = 300;
int previewHeight = bitmap.getHeight() * previewWidth / bitmap.getWidth();
Bitmap previewBitmap = Bitmap.createScaledBitmap(bitmap, previewWidth, previewHeight, false);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
previewBitmap.compress(Bitmap.CompressFormat.JPEG, 50, byteArrayOutputStream);
String str = Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.DEFAULT);
return str == null ? "" : str;
}
Tada! This must work now. If not, pls inform me!