Added push notifications when reciving any message and added filter status on pets in Andriod
This commit is contained in:
@@ -88,7 +88,13 @@ public class HomeActivity extends AppCompatActivity {
|
|||||||
// like clicking a notification or just launching the app from a fresh start
|
// like clicking a notification or just launching the app from a fresh start
|
||||||
private void handleIntent(Intent intent) {
|
private void handleIntent(Intent intent) {
|
||||||
if (intent != null && "chat".equals(intent.getStringExtra("navigate_to"))) {
|
if (intent != null && "chat".equals(intent.getStringExtra("navigate_to"))) {
|
||||||
loadFragment(new ChatFragment());
|
ChatFragment chatFragment = new ChatFragment();
|
||||||
|
if (intent.hasExtra("conversation_id")) {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putLong("conversation_id", intent.getLongExtra("conversation_id", -1));
|
||||||
|
chatFragment.setArguments(args);
|
||||||
|
}
|
||||||
|
loadFragment(chatFragment);
|
||||||
bottomNav.setSelectedItemId(R.id.nav_chat);
|
bottomNav.setSelectedItemId(R.id.nav_chat);
|
||||||
} else {
|
} else {
|
||||||
loadFragment(new ListFragment());
|
loadFragment(new ListFragment());
|
||||||
|
|||||||
@@ -121,6 +121,10 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
|||||||
Log.e(TAG, "No token found");
|
Log.e(TAG, "No token found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getArguments() != null && getArguments().containsKey("conversation_id")) {
|
||||||
|
activeConversationId = getArguments().getLong("conversation_id");
|
||||||
|
}
|
||||||
|
|
||||||
loadCustomers();
|
loadCustomers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +169,14 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
chatList.addAll(loaded);
|
chatList.addAll(loaded);
|
||||||
chatAdapter.notifyDataSetChanged();
|
chatAdapter.notifyDataSetChanged();
|
||||||
if (activeConversationId == null) {
|
|
||||||
|
if (activeConversationId != null) {
|
||||||
|
setConversationActive(true);
|
||||||
|
if (stompChatManager != null) {
|
||||||
|
stompChatManager.subscribeToConversation(activeConversationId);
|
||||||
|
}
|
||||||
|
loadMessageHistory(activeConversationId);
|
||||||
|
} else {
|
||||||
messageList.clear();
|
messageList.clear();
|
||||||
messageAdapter.notifyDataSetChanged();
|
messageAdapter.notifyDataSetChanged();
|
||||||
setConversationActive(false);
|
setConversationActive(false);
|
||||||
|
|||||||
@@ -13,8 +13,11 @@ import android.util.Log;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.Spinner;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.example.petstoremobile.R;
|
import com.example.petstoremobile.R;
|
||||||
@@ -43,6 +46,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
|||||||
private PetApi api;
|
private PetApi api;
|
||||||
private SwipeRefreshLayout swipeRefreshLayout;
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
private EditText etSearch;
|
private EditText etSearch;
|
||||||
|
private Spinner spinnerStatus;
|
||||||
|
|
||||||
//load pet view
|
//load pet view
|
||||||
@Override
|
@Override
|
||||||
@@ -57,6 +61,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
|||||||
|
|
||||||
setupRecyclerView(view);
|
setupRecyclerView(view);
|
||||||
setupSearch(view);
|
setupSearch(view);
|
||||||
|
setupStatusFilter(view);
|
||||||
setupSwipeRefresh(view);
|
setupSwipeRefresh(view);
|
||||||
loadPetData();
|
loadPetData();
|
||||||
|
|
||||||
@@ -82,24 +87,48 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
|||||||
etSearch.addTextChangedListener(new TextWatcher() {
|
etSearch.addTextChangedListener(new TextWatcher() {
|
||||||
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
filterPets(s.toString());
|
filterPets();
|
||||||
}
|
}
|
||||||
@Override public void afterTextChanged(Editable s) {}
|
@Override public void afterTextChanged(Editable s) {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void filterPets(String query) {
|
//Setup the status filter spinner
|
||||||
|
private void setupStatusFilter(View view) {
|
||||||
|
spinnerStatus = view.findViewById(R.id.spinnerStatus);
|
||||||
|
String[] statuses = {"All Statuses", "Available", "Adopted"};
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item, statuses);
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinnerStatus.setAdapter(adapter);
|
||||||
|
|
||||||
|
spinnerStatus.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
filterPets();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to filter pets based on search and status filter
|
||||||
|
private void filterPets() {
|
||||||
|
String query = etSearch.getText().toString().toLowerCase();
|
||||||
|
String selectedStatus = spinnerStatus.getSelectedItem().toString();
|
||||||
|
|
||||||
filteredList.clear();
|
filteredList.clear();
|
||||||
if (query.isEmpty()) {
|
for (PetDTO p : petList) {
|
||||||
filteredList.addAll(petList);
|
boolean matchesSearch = query.isEmpty() ||
|
||||||
} else {
|
p.getPetName().toLowerCase().contains(query) ||
|
||||||
String lower = query.toLowerCase();
|
p.getPetSpecies().toLowerCase().contains(query) ||
|
||||||
for (PetDTO p : petList) {
|
p.getPetBreed().toLowerCase().contains(query);
|
||||||
if (p.getPetName().toLowerCase().contains(lower)
|
|
||||||
|| p.getPetSpecies().toLowerCase().contains(lower)
|
boolean matchesStatus = selectedStatus.equals("All Statuses") ||
|
||||||
|| p.getPetBreed().toLowerCase().contains(lower)) {
|
p.getPetStatus().equalsIgnoreCase(selectedStatus);
|
||||||
filteredList.add(p);
|
|
||||||
}
|
if (matchesSearch && matchesStatus) {
|
||||||
|
filteredList.add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adapter.notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
@@ -173,7 +202,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
|||||||
if (response.isSuccessful() && response.body() != null) {
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
petList.clear();
|
petList.clear();
|
||||||
petList.addAll(response.body().getContent());
|
petList.addAll(response.body().getContent());
|
||||||
filterPets(etSearch.getText().toString());
|
filterPets();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.e("onResponse: ", response.message());
|
Log.e("onResponse: ", response.message());
|
||||||
|
|||||||
@@ -5,18 +5,26 @@ import android.content.Intent;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import com.example.petstoremobile.api.ChatApi;
|
||||||
|
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.MessageDTO;
|
||||||
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.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
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";
|
||||||
private StompChatManager stompChatManager;
|
private StompChatManager stompChatManager;
|
||||||
private final Set<Long> knownConversationIds = new HashSet<>();
|
private final Set<Long> knownConversationIds = new HashSet<>();
|
||||||
|
private Long currentUserId;
|
||||||
|
|
||||||
//When the service starts, connect to the websocket
|
//When the service starts, connect to the websocket
|
||||||
@Override
|
@Override
|
||||||
@@ -32,45 +40,98 @@ public class ChatNotificationService extends Service {
|
|||||||
TokenManager tm = TokenManager.getInstance(this);
|
TokenManager tm = TokenManager.getInstance(this);
|
||||||
String token = tm.getToken();
|
String token = tm.getToken();
|
||||||
String role = tm.getRole();
|
String role = tm.getRole();
|
||||||
|
currentUserId = tm.getUserId();
|
||||||
|
|
||||||
if (token != null && stompChatManager == null) {
|
if (token != null && stompChatManager == null) {
|
||||||
stompChatManager = new StompChatManager(token, role);
|
// Fetch existing conversations
|
||||||
|
ChatApi chatApi = RetrofitClient.getChatApi(this);
|
||||||
//When a conversation gets created, show a notification
|
chatApi.getAllConversations().enqueue(new Callback<List<ConversationDTO>>() {
|
||||||
stompChatManager.setConversationListener(conversation -> {
|
@Override
|
||||||
//check if the conversation exists
|
public void onResponse(Call<List<ConversationDTO>> call, Response<List<ConversationDTO>> response) {
|
||||||
if (conversation != null && conversation.getId() != null) {
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
//check if the conversation is new
|
for (ConversationDTO conversation : response.body()) {
|
||||||
if (!knownConversationIds.contains(conversation.getId())) {
|
if (conversation.getId() != null) {
|
||||||
//add the conversation to the set of known conversations
|
knownConversationIds.add(conversation.getId());
|
||||||
knownConversationIds.add(conversation.getId());
|
// subscribe to existing conversations to get message notifications
|
||||||
NotificationHelper.showNotification(
|
if (stompChatManager != null) {
|
||||||
getApplicationContext(),
|
stompChatManager.subscribeToConversation(conversation.getId());
|
||||||
"Customer Support",
|
}
|
||||||
"A customer request for assistance"
|
}
|
||||||
);
|
}
|
||||||
|
Log.d(TAG, "Loaded " + knownConversationIds.size() + " existing conversations");
|
||||||
}
|
}
|
||||||
}
|
startStomp(token, role);
|
||||||
});
|
|
||||||
stompChatManager.setConnectionListener(new StompChatManager.ConnectionListener() {
|
|
||||||
// when the websocket is connected, set isFirstLoad to false after a delay
|
|
||||||
@Override
|
|
||||||
public void onSocketOpened() {
|
|
||||||
Log.d(TAG, "WebSocket connected in service");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSocketClosed() { Log.d(TAG, "WebSocket closed in service"); }
|
public void onFailure(Call<List<ConversationDTO>> call, Throwable t) {
|
||||||
|
Log.e(TAG, "Failed to load existing conversations", t);
|
||||||
@Override
|
//tries to connect if loading fails
|
||||||
public void onSocketError() { Log.e(TAG, "WebSocket error in service"); }
|
startStomp(token, role);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
stompChatManager.connect();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startStomp(String token, String role) {
|
||||||
|
if (stompChatManager != null) return;
|
||||||
|
|
||||||
|
stompChatManager = new StompChatManager(token, role);
|
||||||
|
|
||||||
|
// Listen for messages in existing conversations
|
||||||
|
stompChatManager.setMessageListener(message -> {
|
||||||
|
if (message != null && !message.getSenderId().equals(currentUserId)) {
|
||||||
|
NotificationHelper.showNotification(
|
||||||
|
getApplicationContext(),
|
||||||
|
"New Message",
|
||||||
|
message.getContent(),
|
||||||
|
message.getConversationId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//When a conversation gets created, show a notification
|
||||||
|
stompChatManager.setConversationListener(conversation -> {
|
||||||
|
//check if the conversation exists
|
||||||
|
if (conversation != null && conversation.getId() != null) {
|
||||||
|
//check if the conversation is new
|
||||||
|
if (!knownConversationIds.contains(conversation.getId())) {
|
||||||
|
//add the conversation to the set of known conversations
|
||||||
|
knownConversationIds.add(conversation.getId());
|
||||||
|
|
||||||
|
// Subscribe to the new conversation's messages
|
||||||
|
stompChatManager.subscribeToConversation(conversation.getId());
|
||||||
|
|
||||||
|
NotificationHelper.showNotification(
|
||||||
|
getApplicationContext(),
|
||||||
|
"New Support Request",
|
||||||
|
"A customer is requesting assistance",
|
||||||
|
conversation.getId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subscribe to existing conversations if they were already loaded
|
||||||
|
for (Long id : knownConversationIds) {
|
||||||
|
stompChatManager.subscribeToConversation(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
stompChatManager.setConnectionListener(new StompChatManager.ConnectionListener() {
|
||||||
|
@Override
|
||||||
|
public void onSocketOpened() {
|
||||||
|
Log.d(TAG, "WebSocket connected in service");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSocketClosed() { Log.d(TAG, "WebSocket closed in service"); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSocketError() { Log.e(TAG, "WebSocket error in service"); }
|
||||||
|
});
|
||||||
|
stompChatManager.connect();
|
||||||
|
}
|
||||||
|
|
||||||
//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() {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class NotificationHelper {
|
|||||||
private static final int NOTIFICATION_ID = 1;
|
private static final int NOTIFICATION_ID = 1;
|
||||||
|
|
||||||
// a function to show a notification
|
// a function to show a notification
|
||||||
public static void showNotification(Context context, String title, String message) {
|
public static void showNotification(Context context, String title, String message, Long conversationId) {
|
||||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
|
||||||
//check if the device is running on Oreo or higher so we can set up a notification channel
|
//check if the device is running on Oreo or higher so we can set up a notification channel
|
||||||
@@ -34,6 +34,9 @@ public class NotificationHelper {
|
|||||||
Intent intent = new Intent(context, HomeActivity.class);
|
Intent intent = new Intent(context, HomeActivity.class);
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
intent.putExtra("navigate_to", "chat");
|
intent.putExtra("navigate_to", "chat");
|
||||||
|
if (conversationId != null) {
|
||||||
|
intent.putExtra("conversation_id", conversationId);
|
||||||
|
}
|
||||||
|
|
||||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
|
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||||
|
|||||||
@@ -38,18 +38,34 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<EditText
|
<LinearLayout
|
||||||
android:id="@+id/etSearchPet"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:orientation="horizontal"
|
||||||
android:hint="Search by name, species or breed..."
|
android:padding="8dp"
|
||||||
android:inputType="text"
|
android:gravity="center_vertical">
|
||||||
android:drawableStart="@android:drawable/ic_menu_search"
|
|
||||||
android:drawablePadding="8dp"
|
<EditText
|
||||||
android:background="@android:color/white"
|
android:id="@+id/etSearchPet"
|
||||||
android:padding="12dp"
|
android:layout_width="0dp"
|
||||||
android:textColor="@color/text_dark"/>
|
android:layout_height="48dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="Search..."
|
||||||
|
android:inputType="text"
|
||||||
|
android:drawableStart="@android:drawable/ic_menu_search"
|
||||||
|
android:drawablePadding="8dp"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinnerStatus"
|
||||||
|
android:layout_width="140dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:padding="10dp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/swipeRefreshPet"
|
android:id="@+id/swipeRefreshPet"
|
||||||
|
|||||||
Reference in New Issue
Block a user