made it so we can put attachments to chat

- Sending not implemented until backend is complete
This commit is contained in:
Alex
2026-04-02 18:23:49 -06:00
parent 3d6b87a7d2
commit 877e0cf0de
6 changed files with 257 additions and 13 deletions

View File

@@ -22,6 +22,15 @@ public class MessageDTO {
@SerializedName("isRead") @SerializedName("isRead")
private Boolean isRead; private Boolean isRead;
@SerializedName("attachmentUrl")
private String attachmentUrl;
@SerializedName("attachmentName")
private String attachmentName;
@SerializedName("attachmentType")
private String attachmentType;
public MessageDTO() {} public MessageDTO() {}
public Long getId() { return id; } public Long getId() { return id; }
@@ -41,4 +50,13 @@ public class MessageDTO {
public Boolean getIsRead() { return isRead; } public Boolean getIsRead() { return isRead; }
public void setIsRead(Boolean isRead) { this.isRead = isRead; } public void setIsRead(Boolean isRead) { this.isRead = isRead; }
public String getAttachmentUrl() { return attachmentUrl; }
public void setAttachmentUrl(String attachmentUrl) { this.attachmentUrl = attachmentUrl; }
public String getAttachmentName() { return attachmentName; }
public void setAttachmentName(String attachmentName) { this.attachmentName = attachmentName; }
public String getAttachmentType() { return attachmentType; }
public void setAttachmentType(String attachmentType) { this.attachmentType = attachmentType; }
} }

View File

@@ -1,15 +1,23 @@
package com.example.petstoremobile.fragments; package com.example.petstoremobile.fragments;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.OpenableColumns;
import android.util.Log; import android.util.Log;
import android.view.*; import android.view.*;
import android.widget.*; import android.widget.*;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.view.GravityCompat; import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout; import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.example.petstoremobile.R; import com.example.petstoremobile.R;
import com.example.petstoremobile.adapters.ChatAdapter; import com.example.petstoremobile.adapters.ChatAdapter;
import com.example.petstoremobile.adapters.MessageAdapter; import com.example.petstoremobile.adapters.MessageAdapter;
@@ -41,8 +49,15 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
private RecyclerView rvChatList, rvMessages; private RecyclerView rvChatList, rvMessages;
private EditText etMessage; private EditText etMessage;
private Button btnSend; private Button btnSend;
private ImageButton btnAttach;
private TextView tvChatTitle; private TextView tvChatTitle;
// Preview views
private View layoutAttachmentPreview;
private ImageView ivPreview;
private TextView tvPreviewName;
private ImageButton btnRemoveAttachment;
// Adapters // Adapters
private ChatAdapter chatAdapter; private ChatAdapter chatAdapter;
private MessageAdapter messageAdapter; private MessageAdapter messageAdapter;
@@ -51,6 +66,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
private final List<Chat> chatList = new ArrayList<>(); private final List<Chat> chatList = new ArrayList<>();
private final List<Message> messageList = new ArrayList<>(); private final List<Message> messageList = new ArrayList<>();
private final Map<Long, String> customerNames = new HashMap<>(); private final Map<Long, String> customerNames = new HashMap<>();
private Uri pendingAttachmentUri;
// APIs // APIs
private ChatApi chatApi; private ChatApi chatApi;
@@ -61,6 +77,24 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
private Long currentUserId; private Long currentUserId;
private Long activeConversationId; private Long activeConversationId;
private StompChatManager stompChatManager; private StompChatManager stompChatManager;
private ActivityResultLauncher<Intent> attachmentLauncher;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
attachmentLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
Uri uri = result.getData().getData();
if (uri != null) {
showAttachmentPreview(uri);
}
}
}
);
}
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
@@ -77,11 +111,28 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
rvMessages = view.findViewById(R.id.rvMessages); rvMessages = view.findViewById(R.id.rvMessages);
etMessage = view.findViewById(R.id.etMessage); etMessage = view.findViewById(R.id.etMessage);
btnSend = view.findViewById(R.id.btnSend); btnSend = view.findViewById(R.id.btnSend);
btnAttach = view.findViewById(R.id.btnAttach);
tvChatTitle = view.findViewById(R.id.tvChatTitle); tvChatTitle = view.findViewById(R.id.tvChatTitle);
layoutAttachmentPreview = view.findViewById(R.id.layoutAttachmentPreview);
ivPreview = view.findViewById(R.id.ivPreview);
tvPreviewName = view.findViewById(R.id.tvPreviewName);
btnRemoveAttachment = view.findViewById(R.id.btnRemoveAttachment);
ImageButton hamburger = view.findViewById(R.id.btnHamburger); ImageButton hamburger = view.findViewById(R.id.btnHamburger);
hamburger.setOnClickListener(v -> drawerLayout.openDrawer(GravityCompat.START)); hamburger.setOnClickListener(v -> drawerLayout.openDrawer(GravityCompat.START));
btnSend.setOnClickListener(v -> sendMessage()); //When the send button is clicked check if there is an attachment and send using the correct helper function
btnSend.setOnClickListener(v -> {
if (pendingAttachmentUri != null) {
sendWithAttachment(pendingAttachmentUri);
} else {
sendMessage();
}
});
//When the attachment button is clicked open the file picker
btnAttach.setOnClickListener(v -> selectAttachment());
btnRemoveAttachment.setOnClickListener(v -> removeAttachment());
setupRecyclerViews(); setupRecyclerViews();
loadInitialData(); loadInitialData();
@@ -89,6 +140,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
return view; return view;
} }
// Helper function to setup recycler views for chat and messages
private void setupRecyclerViews() { private void setupRecyclerViews() {
// Set up Drawer menu to select conversation // Set up Drawer menu to select conversation
chatAdapter = new ChatAdapter(chatList, this); chatAdapter = new ChatAdapter(chatList, this);
@@ -273,6 +325,68 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
}); });
} }
//Helper function to open file picker when the attachment button is clicked
private void selectAttachment() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
attachmentLauncher.launch(intent);
}
//Helper function to show the attachment preview
private void showAttachmentPreview(Uri uri) {
pendingAttachmentUri = uri;
layoutAttachmentPreview.setVisibility(View.VISIBLE);
String mimeType = requireContext().getContentResolver().getType(uri);
String fileName = getFileName(uri);
tvPreviewName.setText(fileName);
// If the file is an image, display a thumbnail of the image as well
if (mimeType != null && mimeType.startsWith("image/")) {
ivPreview.setVisibility(View.VISIBLE);
Glide.with(this).load(uri).into(ivPreview);
} else {
ivPreview.setVisibility(View.GONE);
}
}
//Helper function to remove the attachment
private void removeAttachment() {
pendingAttachmentUri = null;
layoutAttachmentPreview.setVisibility(View.GONE);
}
//Helper function to get the file name from the uri to display in attachment preview
private String getFileName(Uri uri) {
String result = null;
if (uri.getScheme().equals("content")) {
try (Cursor cursor = requireContext().getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (index != -1) {
result = cursor.getString(index);
}
}
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}
//Helper function to send the message with attachment
private void sendWithAttachment(Uri uri) {
if (activeConversationId == null) return;
//TODO: send the message with attachment when backend is done
Log.d(TAG, "Send with attachment happening");
}
// When a message is received updates the chat preview // When a message is received updates the chat preview
@Override @Override
public void onMessageReceived(MessageDTO dto) { public void onMessageReceived(MessageDTO dto) {
@@ -370,6 +484,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
m.setContent(dto.getContent()); m.setContent(dto.getContent());
m.setTimestamp(dto.getTimestamp()); m.setTimestamp(dto.getTimestamp());
m.setIsRead(dto.getIsRead()); m.setIsRead(dto.getIsRead());
m.setAttachmentUrl(dto.getAttachmentUrl());
m.setAttachmentName(dto.getAttachmentName());
m.setAttachmentType(dto.getAttachmentType());
return m; return m;
} }
@@ -407,9 +524,11 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
private void setConversationActive(boolean active) { private void setConversationActive(boolean active) {
btnSend.setEnabled(active); btnSend.setEnabled(active);
etMessage.setEnabled(active); etMessage.setEnabled(active);
btnAttach.setEnabled(active);
if (!active) { if (!active) {
activeConversationId = null; activeConversationId = null;
ChatNotificationService.activeConversationIdInUi = null; ChatNotificationService.activeConversationIdInUi = null;
removeAttachment();
if (tvChatTitle != null) tvChatTitle.setText("Customer Chat"); if (tvChatTitle != null) tvChatTitle.setText("Customer Chat");
if (stompChatManager != null) { if (stompChatManager != null) {
stompChatManager.clearConversationSubscription(); stompChatManager.clearConversationSubscription();

View File

@@ -7,6 +7,9 @@ public class Message {
private String content; private String content;
private String timestamp; private String timestamp;
private Boolean isRead; private Boolean isRead;
private String attachmentUrl;
private String attachmentName;
private String attachmentType;
public Message() {} public Message() {}
@@ -33,4 +36,13 @@ public class Message {
public Boolean getIsRead() { return isRead; } public Boolean getIsRead() { return isRead; }
public void setIsRead(Boolean isRead) { this.isRead = isRead; } public void setIsRead(Boolean isRead) { this.isRead = isRead; }
public String getAttachmentUrl() { return attachmentUrl; }
public void setAttachmentUrl(String attachmentUrl) { this.attachmentUrl = attachmentUrl; }
public String getAttachmentName() { return attachmentName; }
public void setAttachmentName(String attachmentName) { this.attachmentName = attachmentName; }
public String getAttachmentType() { return attachmentType; }
public void setAttachmentType(String attachmentType) { this.attachmentType = attachmentType; }
} }

View File

@@ -49,12 +49,59 @@
android:clipToPadding="false" /> android:clipToPadding="false" />
<LinearLayout <LinearLayout
android:id="@+id/layoutAttachmentPreview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="8dp" android:padding="8dp"
android:background="#E0E0E0"
android:gravity="center_vertical"
android:visibility="gone">
<ImageView
android:id="@+id/ivPreview"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="centerCrop"
android:layout_marginEnd="8dp"
android:visibility="gone"/>
<TextView
android:id="@+id/tvPreviewName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="middle"
android:singleLine="true"
android:textColor="@color/text_dark"/>
<ImageButton
android:id="@+id/btnRemoveAttachment"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@android:drawable/ic_menu_close_clear_cancel"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Remove attachment"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
android:gravity="center_vertical"
android:background="@color/white"> android:background="@color/white">
<ImageButton
android:id="@+id/btnAttach"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@android:drawable/ic_menu_add"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Attach file"
android:layout_marginEnd="4dp"/>
<EditText <EditText
android:id="@+id/etMessage" android:id="@+id/etMessage"
android:layout_width="0dp" android:layout_width="0dp"

View File

@@ -6,14 +6,38 @@
android:padding="8dp" android:padding="8dp"
android:gravity="start"> android:gravity="start">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/bg_message_received"
android:padding="8dp"
android:maxWidth="300dp">
<ImageView
android:id="@+id/ivAttachment"
android:layout_width="200dp"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:visibility="gone"
android:layout_marginBottom="4dp"/>
<TextView
android:id="@+id/tvAttachmentName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/text_dark"
android:textStyle="italic"
android:textSize="12sp"
android:visibility="gone"
android:layout_marginBottom="4dp"/>
<TextView <TextView
android:id="@+id/tvMessageContent" android:id="@+id/tvMessageContent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/bg_message_received"
android:padding="12dp"
android:text="Received message" android:text="Received message"
android:textColor="@color/text_dark" android:textColor="@color/text_dark" />
android:maxWidth="300dp" /> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -6,14 +6,38 @@
android:padding="8dp" android:padding="8dp"
android:gravity="end"> android:gravity="end">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/bg_message_sent"
android:padding="8dp"
android:maxWidth="300dp">
<ImageView
android:id="@+id/ivAttachment"
android:layout_width="200dp"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:visibility="gone"
android:layout_marginBottom="4dp"/>
<TextView
android:id="@+id/tvAttachmentName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textStyle="italic"
android:textSize="12sp"
android:visibility="gone"
android:layout_marginBottom="4dp"/>
<TextView <TextView
android:id="@+id/tvMessageContent" android:id="@+id/tvMessageContent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/bg_message_sent"
android:padding="12dp"
android:text="Sent message" android:text="Sent message"
android:textColor="@color/white" android:textColor="@color/white" />
android:maxWidth="300dp" /> </LinearLayout>
</LinearLayout> </LinearLayout>