added push notification when a new conversation is made
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
@@ -24,6 +25,11 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.PetStoreMobile">
|
||||
|
||||
<service
|
||||
android:name=".services.ChatNotificationService"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.HomeActivity"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
|
||||
@@ -7,7 +7,5 @@ public class PetStoreApplication extends Application {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// Clear login data on app so when the application closes, the user is logged out and have to re-login
|
||||
TokenManager.getInstance(this).clearLoginData();
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,40 @@
|
||||
package com.example.petstoremobile.activities;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.fragments.ChatFragment;
|
||||
import com.example.petstoremobile.fragments.ListFragment;
|
||||
import com.example.petstoremobile.fragments.ProfileFragment;
|
||||
import com.example.petstoremobile.services.ChatNotificationService;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
public class HomeActivity extends AppCompatActivity {
|
||||
private BottomNavigationView bottomNav;
|
||||
|
||||
// Launcher to ask for notification permission
|
||||
private final ActivityResultLauncher<String> requestPermissionLauncher =
|
||||
registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
|
||||
if (!isGranted) {
|
||||
Log.w("HomeActivity", "Notification permission denied");
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -34,17 +49,15 @@ public class HomeActivity extends AppCompatActivity {
|
||||
});
|
||||
|
||||
//get the bottom navbar from the layout
|
||||
BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation);
|
||||
bottomNav = findViewById(R.id.bottom_navigation);
|
||||
|
||||
// Load ListFragment by default only if this is a fresh start
|
||||
//load the list fragment by default if it's a fresh start
|
||||
if (savedInstanceState == null) {
|
||||
loadFragment(new ListFragment());
|
||||
bottomNav.setSelectedItemId(R.id.nav_list);
|
||||
handleIntent(getIntent());
|
||||
}
|
||||
|
||||
//when an item in the bar is selected, load the corresponding fragment
|
||||
//when an item in the bottom bar is selected, load the corresponding fragment
|
||||
bottomNav.setOnItemSelectedListener(item -> {
|
||||
|
||||
if (item.getItemId() == R.id.nav_list) {
|
||||
loadFragment(new ListFragment());
|
||||
return true;
|
||||
@@ -57,9 +70,49 @@ public class HomeActivity extends AppCompatActivity {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Start the notification service and request for notification permission
|
||||
startNotificationService();
|
||||
requestNotificationPermission();
|
||||
}
|
||||
|
||||
//helper function to load a fragment
|
||||
// Handle new intents when the activity is already running,
|
||||
// like clicking a notification while the app is in use
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
handleIntent(intent);
|
||||
}
|
||||
|
||||
// Helper function to process intents for navigation.
|
||||
// like clicking a notification or just launching the app from a fresh start
|
||||
private void handleIntent(Intent intent) {
|
||||
if (intent != null && "chat".equals(intent.getStringExtra("navigate_to"))) {
|
||||
loadFragment(new ChatFragment());
|
||||
bottomNav.setSelectedItemId(R.id.nav_chat);
|
||||
} else {
|
||||
loadFragment(new ListFragment());
|
||||
bottomNav.setSelectedItemId(R.id.nav_list);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to start the notification service in the background
|
||||
// to receive notifications when a new conversation is created
|
||||
private void startNotificationService() {
|
||||
Intent serviceIntent = new Intent(this, ChatNotificationService.class);
|
||||
startService(serviceIntent);
|
||||
}
|
||||
|
||||
//Helper function to request for notification permission
|
||||
private void requestNotificationPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Helper function to load a fragment
|
||||
private void loadFragment(Fragment fragment) {
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.example.petstoremobile.api.auth.AuthApi;
|
||||
import com.example.petstoremobile.api.auth.TokenManager;
|
||||
import com.example.petstoremobile.dtos.ErrorResponse;
|
||||
import com.example.petstoremobile.dtos.UserDTO;
|
||||
import com.example.petstoremobile.services.ChatNotificationService;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
@@ -229,6 +230,10 @@ public class ProfileFragment extends Fragment {
|
||||
|
||||
//Logout button
|
||||
btnLogout.setOnClickListener(v -> {
|
||||
// Stop notification service before logging out so notifications stop
|
||||
Intent serviceIntent = new Intent(requireContext(), ChatNotificationService.class);
|
||||
requireContext().stopService(serviceIntent);
|
||||
|
||||
TokenManager.getInstance(requireContext()).clearLoginData(); // clear the token for next login
|
||||
//get the intent to the main activity and clear the back stack so the back button won't allow the user to go back to the previous screen
|
||||
Intent intent = new Intent(getActivity(), MainActivity.class);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.example.petstoremobile.services;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.example.petstoremobile.api.auth.TokenManager;
|
||||
import com.example.petstoremobile.dtos.ConversationDTO;
|
||||
import com.example.petstoremobile.utils.NotificationHelper;
|
||||
import com.example.petstoremobile.websocket.StompChatManager;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
// Service to receive notifications when a new conversation is created
|
||||
public class ChatNotificationService extends Service {
|
||||
private static final String TAG = "ChatNotificationService";
|
||||
private StompChatManager stompChatManager;
|
||||
private final Set<Long> knownConversationIds = new HashSet<>();
|
||||
|
||||
//When the service starts, connect to the websocket
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.d(TAG, "Service started");
|
||||
connectWebSocket();
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
// helper function to connect to the websocket
|
||||
private void connectWebSocket() {
|
||||
//get the token and role from the shared preferences
|
||||
TokenManager tm = TokenManager.getInstance(this);
|
||||
String token = tm.getToken();
|
||||
String role = tm.getRole();
|
||||
|
||||
|
||||
if (token != null && stompChatManager == null) {
|
||||
stompChatManager = new StompChatManager(token, role);
|
||||
|
||||
//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());
|
||||
NotificationHelper.showNotification(
|
||||
getApplicationContext(),
|
||||
"Customer Support",
|
||||
"A customer request for assistance"
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
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
|
||||
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
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (stompChatManager != null) {
|
||||
stompChatManager.disconnect();
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.example.petstoremobile.utils;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.activities.HomeActivity;
|
||||
|
||||
// Helper class to show notifications when called
|
||||
public class NotificationHelper {
|
||||
private static final String CHANNEL_ID = "chat_notifications";
|
||||
private static final String CHANNEL_NAME = "Chat Notifications";
|
||||
private static final String CHANNEL_DESC = "Notifications for new conversations";
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
|
||||
// a function to show a notification
|
||||
public static void showNotification(Context context, String title, String message) {
|
||||
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
|
||||
// for these devices
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// Create a notification channel
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.setDescription(CHANNEL_DESC);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
//make the notification navigate the chat if it is clicked
|
||||
Intent intent = new Intent(context, HomeActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
intent.putExtra("navigate_to", "chat");
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
//build the notification for display
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(pendingIntent);
|
||||
|
||||
notificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user