Make chat notification display messengers name and disable notifying if already in chat view
This commit is contained in:
@@ -25,6 +25,7 @@ import com.example.petstoremobile.dtos.PageResponse;
|
|||||||
import com.example.petstoremobile.dtos.SendMessageRequest;
|
import com.example.petstoremobile.dtos.SendMessageRequest;
|
||||||
import com.example.petstoremobile.models.Chat;
|
import com.example.petstoremobile.models.Chat;
|
||||||
import com.example.petstoremobile.models.Message;
|
import com.example.petstoremobile.models.Message;
|
||||||
|
import com.example.petstoremobile.services.ChatNotificationService;
|
||||||
import com.example.petstoremobile.websocket.StompChatManager;
|
import com.example.petstoremobile.websocket.StompChatManager;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -40,6 +41,7 @@ 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 TextView tvChatTitle;
|
||||||
|
|
||||||
// Adapters
|
// Adapters
|
||||||
private ChatAdapter chatAdapter;
|
private ChatAdapter chatAdapter;
|
||||||
@@ -75,6 +77,7 @@ 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);
|
||||||
|
tvChatTitle = view.findViewById(R.id.tvChatTitle);
|
||||||
|
|
||||||
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));
|
||||||
@@ -172,6 +175,13 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
|||||||
|
|
||||||
if (activeConversationId != null) {
|
if (activeConversationId != null) {
|
||||||
setConversationActive(true);
|
setConversationActive(true);
|
||||||
|
// Update title to customer name of active conversation
|
||||||
|
for (Chat chat : chatList) {
|
||||||
|
if (chat.getChatId().equals(String.valueOf(activeConversationId))) {
|
||||||
|
tvChatTitle.setText(chat.getCustomerName());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (stompChatManager != null) {
|
if (stompChatManager != null) {
|
||||||
stompChatManager.subscribeToConversation(activeConversationId);
|
stompChatManager.subscribeToConversation(activeConversationId);
|
||||||
}
|
}
|
||||||
@@ -197,6 +207,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
|||||||
public void onChatClick(Chat chat) {
|
public void onChatClick(Chat chat) {
|
||||||
activeConversationId = Long.parseLong(chat.getChatId());
|
activeConversationId = Long.parseLong(chat.getChatId());
|
||||||
setConversationActive(true);
|
setConversationActive(true);
|
||||||
|
tvChatTitle.setText(chat.getCustomerName());
|
||||||
drawerLayout.closeDrawer(GravityCompat.START);
|
drawerLayout.closeDrawer(GravityCompat.START);
|
||||||
|
|
||||||
if (stompChatManager != null) {
|
if (stompChatManager != null) {
|
||||||
@@ -316,6 +327,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
|||||||
|
|
||||||
if (activeConversationId != null && activeConversationId.equals(dto.getId())) {
|
if (activeConversationId != null && activeConversationId.equals(dto.getId())) {
|
||||||
setConversationActive(true);
|
setConversationActive(true);
|
||||||
|
tvChatTitle.setText(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,6 +409,8 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
|||||||
etMessage.setEnabled(active);
|
etMessage.setEnabled(active);
|
||||||
if (!active) {
|
if (!active) {
|
||||||
activeConversationId = null;
|
activeConversationId = null;
|
||||||
|
ChatNotificationService.activeConversationIdInUi = null;
|
||||||
|
if (tvChatTitle != null) tvChatTitle.setText("Customer Chat");
|
||||||
if (stompChatManager != null) {
|
if (stompChatManager != null) {
|
||||||
stompChatManager.clearConversationSubscription();
|
stompChatManager.clearConversationSubscription();
|
||||||
}
|
}
|
||||||
@@ -406,6 +420,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
|||||||
etMessage.setHint("Select a chat to start messaging");
|
etMessage.setHint("Select a chat to start messaging");
|
||||||
} else {
|
} else {
|
||||||
etMessage.setHint("Type a message...");
|
etMessage.setHint("Type a message...");
|
||||||
|
ChatNotificationService.activeConversationIdInUi = activeConversationId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,6 +428,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
|||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
|
ChatNotificationService.activeConversationIdInUi = null;
|
||||||
if (stompChatManager != null) stompChatManager.disconnect();
|
if (stompChatManager != null) stompChatManager.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,22 @@ import android.app.Service;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.example.petstoremobile.api.ChatApi;
|
import com.example.petstoremobile.api.ChatApi;
|
||||||
|
import com.example.petstoremobile.api.CustomerApi;
|
||||||
import com.example.petstoremobile.api.RetrofitClient;
|
import com.example.petstoremobile.api.RetrofitClient;
|
||||||
import com.example.petstoremobile.api.auth.TokenManager;
|
import com.example.petstoremobile.api.auth.TokenManager;
|
||||||
import com.example.petstoremobile.dtos.ConversationDTO;
|
import com.example.petstoremobile.dtos.ConversationDTO;
|
||||||
|
import com.example.petstoremobile.dtos.CustomerDTO;
|
||||||
import com.example.petstoremobile.dtos.MessageDTO;
|
import com.example.petstoremobile.dtos.MessageDTO;
|
||||||
|
import com.example.petstoremobile.dtos.PageResponse;
|
||||||
import com.example.petstoremobile.utils.NotificationHelper;
|
import com.example.petstoremobile.utils.NotificationHelper;
|
||||||
import com.example.petstoremobile.websocket.StompChatManager;
|
import com.example.petstoremobile.websocket.StompChatManager;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
@@ -22,8 +28,13 @@ import retrofit2.Response;
|
|||||||
// Service to receive notifications when a new conversation is created
|
// Service to receive notifications when a new conversation is created
|
||||||
public class ChatNotificationService extends Service {
|
public class ChatNotificationService extends Service {
|
||||||
private static final String TAG = "ChatNotificationService";
|
private static final String TAG = "ChatNotificationService";
|
||||||
|
|
||||||
|
public static Long activeConversationIdInUi = null;
|
||||||
|
|
||||||
private StompChatManager stompChatManager;
|
private StompChatManager stompChatManager;
|
||||||
private final Set<Long> knownConversationIds = new HashSet<>();
|
private final Set<Long> knownConversationIds = new HashSet<>();
|
||||||
|
private final Map<Long, Long> conversationToCustomerId = new HashMap<>();
|
||||||
|
private final Map<Long, String> customerIdToName = new HashMap<>();
|
||||||
private Long currentUserId;
|
private Long currentUserId;
|
||||||
|
|
||||||
//When the service starts, connect to the websocket
|
//When the service starts, connect to the websocket
|
||||||
@@ -43,36 +54,59 @@ public class ChatNotificationService extends Service {
|
|||||||
currentUserId = tm.getUserId();
|
currentUserId = tm.getUserId();
|
||||||
|
|
||||||
if (token != null && stompChatManager == null) {
|
if (token != null && stompChatManager == null) {
|
||||||
// Fetch existing conversations
|
//load customers to have names associated with customer ids
|
||||||
ChatApi chatApi = RetrofitClient.getChatApi(this);
|
CustomerApi customerApi = RetrofitClient.getCustomerApi(this);
|
||||||
chatApi.getAllConversations().enqueue(new Callback<List<ConversationDTO>>() {
|
customerApi.getAllCustomers(0, 1000).enqueue(new Callback<PageResponse<CustomerDTO>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call<List<ConversationDTO>> call, Response<List<ConversationDTO>> response) {
|
public void onResponse(@NonNull Call<PageResponse<CustomerDTO>> call, @NonNull Response<PageResponse<CustomerDTO>> response) {
|
||||||
if (response.isSuccessful() && response.body() != null) {
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
for (ConversationDTO conversation : response.body()) {
|
for (CustomerDTO customer : response.body().getContent()) {
|
||||||
if (conversation.getId() != null) {
|
customerIdToName.put(customer.getCustomerId(), customer.getFullName());
|
||||||
knownConversationIds.add(conversation.getId());
|
|
||||||
// subscribe to existing conversations to get message notifications
|
|
||||||
if (stompChatManager != null) {
|
|
||||||
stompChatManager.subscribeToConversation(conversation.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Loaded " + knownConversationIds.size() + " existing conversations");
|
|
||||||
}
|
}
|
||||||
startStomp(token, role);
|
loadConversationsAndStartStomp(token, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call<List<ConversationDTO>> call, Throwable t) {
|
public void onFailure(@NonNull Call<PageResponse<CustomerDTO>> call, @NonNull Throwable t) {
|
||||||
Log.e(TAG, "Failed to load existing conversations", t);
|
Log.e(TAG, "Failed to load customers", t);
|
||||||
//tries to connect if loading fails
|
loadConversationsAndStartStomp(token, role);
|
||||||
startStomp(token, role);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadConversationsAndStartStomp(String token, String role) {
|
||||||
|
// Fetch existing conversations
|
||||||
|
ChatApi chatApi = RetrofitClient.getChatApi(this);
|
||||||
|
chatApi.getAllConversations().enqueue(new Callback<List<ConversationDTO>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<List<ConversationDTO>> call, @NonNull Response<List<ConversationDTO>> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
for (ConversationDTO conversation : response.body()) {
|
||||||
|
if (conversation.getId() != null) {
|
||||||
|
knownConversationIds.add(conversation.getId());
|
||||||
|
conversationToCustomerId.put(conversation.getId(), conversation.getCustomerId());
|
||||||
|
// subscribe to existing conversations to get message notifications
|
||||||
|
if (stompChatManager != null) {
|
||||||
|
stompChatManager.subscribeToConversation(conversation.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Loaded " + knownConversationIds.size() + " existing conversations");
|
||||||
|
}
|
||||||
|
startStomp(token, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<List<ConversationDTO>> call, @NonNull Throwable t) {
|
||||||
|
Log.e(TAG, "Failed to load existing conversations", t);
|
||||||
|
//tries to connect if loading fails
|
||||||
|
startStomp(token, role);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void startStomp(String token, String role) {
|
private void startStomp(String token, String role) {
|
||||||
if (stompChatManager != null) return;
|
if (stompChatManager != null) return;
|
||||||
|
|
||||||
@@ -81,9 +115,23 @@ public class ChatNotificationService extends Service {
|
|||||||
// Listen for messages in existing conversations
|
// Listen for messages in existing conversations
|
||||||
stompChatManager.setMessageListener(message -> {
|
stompChatManager.setMessageListener(message -> {
|
||||||
if (message != null && !message.getSenderId().equals(currentUserId)) {
|
if (message != null && !message.getSenderId().equals(currentUserId)) {
|
||||||
|
// Check if this conversation is already active in the view
|
||||||
|
//if it is then don't make a notification for this chat
|
||||||
|
if (activeConversationIdInUi != null && activeConversationIdInUi.equals(message.getConversationId())) {
|
||||||
|
Log.d(TAG, "Disable notification for active conversation: " + message.getConversationId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String title = "New Message";
|
||||||
|
Long customerId = conversationToCustomerId.get(message.getConversationId());
|
||||||
|
if (customerId != null && customerIdToName.containsKey(customerId)) {
|
||||||
|
//append the customer name to the title of the notification
|
||||||
|
title = "New message from " + customerIdToName.get(customerId);
|
||||||
|
}
|
||||||
|
|
||||||
NotificationHelper.showNotification(
|
NotificationHelper.showNotification(
|
||||||
getApplicationContext(),
|
getApplicationContext(),
|
||||||
"New Message",
|
title,
|
||||||
message.getContent(),
|
message.getContent(),
|
||||||
message.getConversationId()
|
message.getConversationId()
|
||||||
);
|
);
|
||||||
@@ -98,13 +146,24 @@ public class ChatNotificationService extends Service {
|
|||||||
if (!knownConversationIds.contains(conversation.getId())) {
|
if (!knownConversationIds.contains(conversation.getId())) {
|
||||||
//add the conversation to the set of known conversations
|
//add the conversation to the set of known conversations
|
||||||
knownConversationIds.add(conversation.getId());
|
knownConversationIds.add(conversation.getId());
|
||||||
|
conversationToCustomerId.put(conversation.getId(), conversation.getCustomerId());
|
||||||
|
|
||||||
// Subscribe to the new conversation's messages
|
// Subscribe to the new conversation's messages
|
||||||
stompChatManager.subscribeToConversation(conversation.getId());
|
stompChatManager.subscribeToConversation(conversation.getId());
|
||||||
|
|
||||||
|
String title = "New Support Request";
|
||||||
|
if (customerIdToName.containsKey(conversation.getCustomerId())) {
|
||||||
|
//append the customer name to the title of the notification
|
||||||
|
title = "New Support Request from " + customerIdToName.get(conversation.getCustomerId());
|
||||||
|
} else {
|
||||||
|
// Try to fetch customer name for the new request
|
||||||
|
fetchCustomerName(conversation.getCustomerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Display a notification
|
||||||
NotificationHelper.showNotification(
|
NotificationHelper.showNotification(
|
||||||
getApplicationContext(),
|
getApplicationContext(),
|
||||||
"New Support Request",
|
title,
|
||||||
"A customer is requesting assistance",
|
"A customer is requesting assistance",
|
||||||
conversation.getId()
|
conversation.getId()
|
||||||
);
|
);
|
||||||
@@ -132,6 +191,24 @@ public class ChatNotificationService extends Service {
|
|||||||
stompChatManager.connect();
|
stompChatManager.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to fetch customer name for a conversation
|
||||||
|
private void fetchCustomerName(Long customerId) {
|
||||||
|
CustomerApi customerApi = RetrofitClient.getCustomerApi(this);
|
||||||
|
customerApi.getCustomerById(customerId).enqueue(new Callback<CustomerDTO>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<CustomerDTO> call, @NonNull Response<CustomerDTO> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
customerIdToName.put(customerId, response.body().getFullName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<CustomerDTO> call, @NonNull Throwable t) {
|
||||||
|
Log.e(TAG, "Failed to fetch customer name", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//When the service is destroyed, disconnect from the websocket
|
//When the service is destroyed, disconnect from the websocket
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
|
|||||||
@@ -28,12 +28,15 @@
|
|||||||
android:contentDescription="Open menu"/>
|
android:contentDescription="Open menu"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/tvChatTitle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Customer Chat"
|
android:text="Customer Chat"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold"/>
|
android:textStyle="bold"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user