-
-
Notifications
You must be signed in to change notification settings - Fork 45
Markdown in messaging #2948
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Markdown in messaging #2948
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,20 @@ | ||
| package org.commcare.fragments.connectMessaging; | ||
|
|
||
| import android.content.BroadcastReceiver; | ||
| import android.content.Context; | ||
| import android.content.Intent; | ||
| import android.content.IntentFilter; | ||
| import android.os.Bundle; | ||
| import android.os.Handler; | ||
| import android.text.Editable; | ||
| import android.text.TextWatcher; | ||
| import android.view.LayoutInflater; | ||
| import android.view.View; | ||
| import android.view.ViewGroup; | ||
| import android.view.inputmethod.InputMethodManager; | ||
|
|
||
| import androidx.annotation.NonNull; | ||
| import androidx.fragment.app.Fragment; | ||
| import androidx.localbroadcastmanager.content.LocalBroadcastManager; | ||
| import androidx.recyclerview.widget.RecyclerView; | ||
|
|
||
| import org.commcare.adapters.ConnectMessageAdapter; | ||
|
|
@@ -20,14 +23,15 @@ | |
| import org.commcare.connect.ConnectDatabaseHelper; | ||
| import org.commcare.connect.MessageManager; | ||
| import org.commcare.dalvik.databinding.FragmentConnectMessageBinding; | ||
| import org.commcare.services.CommCareFirebaseMessagingService; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Date; | ||
| import java.util.List; | ||
| import java.util.UUID; | ||
|
|
||
| public class ConnectMessageFragment extends Fragment { | ||
|
|
||
| public static String activeChannel; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider alternative to static variable for tracking active channel. Using a static variable to track the active channel state could lead to memory leaks and issues during configuration changes. Consider using a ViewModel or other lifecycle-aware component to manage this state. Here's a suggested implementation using ViewModel: class ConnectMessageViewModel : ViewModel() {
private val _activeChannel = MutableLiveData<String?>()
val activeChannel: LiveData<String?> = _activeChannel
fun setActiveChannel(channelId: String?) {
_activeChannel.value = channelId
}
}Usage in fragment: -public static String activeChannel;
+private ConnectMessageViewModel viewModel;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ viewModel = new ViewModelProvider(this).get(ConnectMessageViewModel.class);
} |
||
| private String channelId; | ||
| private FragmentConnectMessageBinding binding; | ||
| private ConnectMessageAdapter adapter; | ||
|
|
@@ -63,17 +67,33 @@ public void run() { | |
| @Override | ||
| public void onResume() { | ||
| super.onResume(); | ||
| activeChannel = channelId; | ||
|
|
||
| LocalBroadcastManager.getInstance(requireContext()).registerReceiver(updateReceiver, | ||
| new IntentFilter(CommCareFirebaseMessagingService.MESSAGING_UPDATE_BROADCAST)); | ||
|
|
||
| // Start periodic API calls | ||
| handler.post(apiCallRunnable); | ||
| } | ||
|
|
||
| @Override | ||
| public void onPause() { | ||
| super.onPause(); | ||
| activeChannel = null; | ||
|
|
||
| LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(updateReceiver); | ||
|
|
||
| // Stop the periodic API calls when the screen is not active | ||
| handler.removeCallbacks(apiCallRunnable); | ||
| } | ||
|
|
||
| private final BroadcastReceiver updateReceiver = new BroadcastReceiver() { | ||
| @Override | ||
| public void onReceive(Context context, Intent intent) { | ||
| refreshUi(); | ||
| } | ||
| }; | ||
|
|
||
| private void makeApiCall() { | ||
| MessageManager.retrieveMessages(requireActivity(), success -> { | ||
| refreshUi(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| import android.os.Build; | ||
|
|
||
| import androidx.core.app.NotificationCompat; | ||
| import androidx.localbroadcastmanager.content.LocalBroadcastManager; | ||
|
|
||
| import com.google.firebase.messaging.FirebaseMessagingService; | ||
| import com.google.firebase.messaging.RemoteMessage; | ||
|
|
@@ -21,6 +22,8 @@ | |
| import org.commcare.connect.ConnectDatabaseHelper; | ||
| import org.commcare.connect.MessageManager; | ||
| import org.commcare.dalvik.R; | ||
| import org.commcare.fragments.connectMessaging.ConnectMessageChannelListFragment; | ||
| import org.commcare.fragments.connectMessaging.ConnectMessageFragment; | ||
| import org.commcare.google.services.analytics.FirebaseAnalyticsUtil; | ||
| import org.commcare.sync.FirebaseMessagingDataSyncer; | ||
| import org.commcare.util.LogTypes; | ||
|
|
@@ -37,6 +40,7 @@ | |
| public class CommCareFirebaseMessagingService extends FirebaseMessagingService { | ||
|
|
||
| private final static int FCM_NOTIFICATION = R.string.fcm_notification; | ||
| public static final String MESSAGING_UPDATE_BROADCAST = "com.dimagi.messaging.update"; | ||
| public static final String OPPORTUNITY_ID = "opportunity_id"; | ||
| public static final String PAYMENT_ID = "payment_id"; | ||
| public static final String PAYMENT_STATUS = "payment_status"; | ||
|
|
@@ -62,7 +66,8 @@ enum ActionTypes { | |
| */ | ||
| @Override | ||
| public void onMessageReceived(RemoteMessage remoteMessage) { | ||
| Logger.log(LogTypes.TYPE_FCM, "CommCareFirebaseMessagingService Message received: " + remoteMessage.getData()); | ||
| Logger.log(LogTypes.TYPE_FCM, "CommCareFirebaseMessagingService Message received: " + | ||
| remoteMessage.getData()); | ||
| Map<String, String> payloadData = remoteMessage.getData(); | ||
|
|
||
| // Check if the message contains a data object, there is no further action if not | ||
|
|
@@ -96,9 +101,8 @@ public void onNewToken(String token) { | |
| private void showNotification(Map<String, String> payloadData) { | ||
| String notificationTitle = payloadData.get("title"); | ||
| String notificationText = payloadData.get("body"); | ||
| NotificationManager mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); | ||
|
|
||
| Intent intent; | ||
| Intent intent = null; | ||
| String action = payloadData.get("action"); | ||
|
|
||
| if (hasCccAction(action)) { | ||
|
|
@@ -112,14 +116,12 @@ private void showNotification(Map<String, String> payloadData) { | |
| //On channels page (just update the page) | ||
| //On message page for the active channel | ||
|
|
||
| intent = new Intent(getApplicationContext(), ConnectMessagingActivity.class); | ||
| intent.putExtra("action", action); | ||
|
|
||
| int notificationTitleId; | ||
| String notificationMessage; | ||
| String channelId; | ||
| if(isMessage) { | ||
| ConnectMessagingMessageRecord message = MessageManager.handleReceivedMessage(this, payloadData); | ||
| ConnectMessagingMessageRecord message = MessageManager.handleReceivedMessage(this, | ||
| payloadData); | ||
|
|
||
| if(message == null) { | ||
| Logger.log(LogTypes.TYPE_FCM, "Ignoring message without known consented channel: " + | ||
|
|
@@ -128,26 +130,40 @@ private void showNotification(Map<String, String> payloadData) { | |
| return; | ||
| } | ||
|
|
||
| ConnectMessagingChannelRecord channel = ConnectDatabaseHelper.getMessagingChannel(this, message.getChannelId()); | ||
| ConnectMessagingChannelRecord channel = ConnectDatabaseHelper.getMessagingChannel(this, | ||
| message.getChannelId()); | ||
|
|
||
| notificationTitleId = R.string.connect_messaging_message_notification_title; | ||
| notificationMessage = getString(R.string.connect_messaging_message_notification_message, channel.getChannelName()); | ||
| notificationMessage = getString(R.string.connect_messaging_message_notification_message, | ||
| channel.getChannelName()); | ||
|
|
||
| channelId = message.getChannelId(); | ||
| } else { | ||
| //Channel | ||
| ConnectMessagingChannelRecord channel = MessageManager.handleReceivedChannel(this, payloadData); | ||
| ConnectMessagingChannelRecord channel = MessageManager.handleReceivedChannel(this, | ||
| payloadData); | ||
|
|
||
| notificationTitleId = R.string.connect_messaging_channel_notification_title; | ||
| notificationMessage = getString(R.string.connect_messaging_channel_notification_message, channel.getChannelName()); | ||
| notificationMessage = getString(R.string.connect_messaging_channel_notification_message, | ||
| channel.getChannelName()); | ||
|
|
||
| channelId = channel.getChannelId(); | ||
| } | ||
|
|
||
| notificationTitle = getString(notificationTitleId); | ||
| notificationText = notificationMessage; | ||
| if(ConnectMessageChannelListFragment.isActive || | ||
| channelId.equals(ConnectMessageFragment.activeChannel)) { | ||
| //Notify active page to update instead of showing notification | ||
| Intent broadcastIntent = new Intent(MESSAGING_UPDATE_BROADCAST); | ||
| LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent); | ||
| } else { | ||
|
Comment on lines
+153
to
+158
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Include channel information in the broadcast intent. The broadcast intent should include the channel ID to allow receivers to verify if they need to update their UI. if(ConnectMessageChannelListFragment.isActive ||
channelId.equals(ConnectMessageFragment.activeChannel)) {
//Notify active page to update instead of showing notification
Intent broadcastIntent = new Intent(MESSAGING_UPDATE_BROADCAST);
+ broadcastIntent.putExtra("channel_id", channelId);
+ broadcastIntent.putExtra("update_type", isMessage ? "message" : "channel");
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
}Then update the receiver in ConnectMessageFragment: private final BroadcastReceiver updateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ String updatedChannelId = intent.getStringExtra("channel_id");
+ if (updatedChannelId != null && updatedChannelId.equals(channelId)) {
refreshUi();
+ }
}
};
|
||
| //Show push notification | ||
| notificationTitle = getString(notificationTitleId); | ||
| notificationText = notificationMessage; | ||
|
|
||
| intent.putExtra(ConnectMessagingMessageRecord.META_MESSAGE_CHANNEL_ID, channelId); | ||
| intent = new Intent(getApplicationContext(), ConnectMessagingActivity.class); | ||
| intent.putExtra("action", action); | ||
| intent.putExtra(ConnectMessagingMessageRecord.META_MESSAGE_CHANNEL_ID, channelId); | ||
| } | ||
| } else { | ||
| //Intent for ConnectActivity | ||
| intent = new Intent(getApplicationContext(), ConnectActivity.class); | ||
|
|
@@ -158,54 +174,62 @@ private void showNotification(Map<String, String> payloadData) { | |
| intent.setAction(Intent.ACTION_MAIN); | ||
| intent.addCategory(Intent.CATEGORY_LAUNCHER); | ||
| } | ||
| intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); | ||
|
|
||
| int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S | ||
| ? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE | ||
| : PendingIntent.FLAG_UPDATE_CURRENT; | ||
|
|
||
| PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, flags); | ||
|
|
||
| NotificationCompat.Builder fcmNotification = new NotificationCompat.Builder(this, | ||
| CommCareNoficationManager.NOTIFICATION_CHANNEL_PUSH_NOTIFICATIONS_ID) | ||
| .setContentTitle(notificationTitle) | ||
| .setContentText(notificationText) | ||
| .setContentIntent(contentIntent) | ||
| .setAutoCancel(true) | ||
| .setSmallIcon(R.drawable.commcare_actionbar_logo) | ||
| .setPriority(NotificationCompat.PRIORITY_HIGH) | ||
| .setWhen(System.currentTimeMillis()); | ||
|
|
||
| // Check if the payload action is CCC_PAYMENTS | ||
| if (action.equals(ConnectConstants.CCC_DEST_PAYMENTS)) { | ||
| // Yes button intent with payment_id from payload | ||
| Intent yesIntent = new Intent(this, PaymentAcknowledgeReceiver.class); | ||
| yesIntent.putExtra(OPPORTUNITY_ID,payloadData.get(OPPORTUNITY_ID)); | ||
| yesIntent.putExtra(PAYMENT_ID,payloadData.get(PAYMENT_ID)); | ||
| yesIntent.putExtra(PAYMENT_STATUS,true); | ||
| PendingIntent yesPendingIntent = PendingIntent.getBroadcast(this, 1, yesIntent, flags); | ||
|
|
||
| // No button intent with payment_id from payload | ||
| Intent noIntent = new Intent(this, PaymentAcknowledgeReceiver.class); | ||
| noIntent.putExtra(OPPORTUNITY_ID,payloadData.get(OPPORTUNITY_ID)); | ||
| noIntent.putExtra(PAYMENT_ID,payloadData.get(PAYMENT_ID)); | ||
| noIntent.putExtra(PAYMENT_STATUS,false); | ||
| PendingIntent noPendingIntent = PendingIntent.getBroadcast(this, 2, noIntent, flags); | ||
|
|
||
| // Add Yes & No action button to the notification | ||
| fcmNotification.addAction(0, getString(R.string.connect_payment_acknowledge_notification_yes), yesPendingIntent); | ||
| fcmNotification.addAction(0, getString(R.string.connect_payment_acknowledge_notification_no), noPendingIntent); | ||
| } | ||
|
|
||
| mNM.notify(FCM_NOTIFICATION, fcmNotification.build()); | ||
| if(intent != null) { | ||
| intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | | ||
| Intent.FLAG_ACTIVITY_NEW_TASK); | ||
|
|
||
| int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S | ||
| ? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE | ||
| : PendingIntent.FLAG_UPDATE_CURRENT; | ||
|
|
||
| PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, flags); | ||
|
|
||
| NotificationCompat.Builder fcmNotification = new NotificationCompat.Builder(this, | ||
| CommCareNoficationManager.NOTIFICATION_CHANNEL_PUSH_NOTIFICATIONS_ID) | ||
| .setContentTitle(notificationTitle) | ||
| .setContentText(notificationText) | ||
| .setContentIntent(contentIntent) | ||
| .setAutoCancel(true) | ||
| .setSmallIcon(R.drawable.commcare_actionbar_logo) | ||
| .setPriority(NotificationCompat.PRIORITY_HIGH) | ||
| .setWhen(System.currentTimeMillis()); | ||
|
|
||
| // Check if the payload action is CCC_PAYMENTS | ||
| if (action.equals(ConnectConstants.CCC_DEST_PAYMENTS)) { | ||
| // Yes button intent with payment_id from payload | ||
| Intent yesIntent = new Intent(this, PaymentAcknowledgeReceiver.class); | ||
| yesIntent.putExtra(OPPORTUNITY_ID, payloadData.get(OPPORTUNITY_ID)); | ||
| yesIntent.putExtra(PAYMENT_ID, payloadData.get(PAYMENT_ID)); | ||
| yesIntent.putExtra(PAYMENT_STATUS, true); | ||
| PendingIntent yesPendingIntent = PendingIntent.getBroadcast(this, 1, | ||
| yesIntent, flags); | ||
|
|
||
| // No button intent with payment_id from payload | ||
| Intent noIntent = new Intent(this, PaymentAcknowledgeReceiver.class); | ||
| noIntent.putExtra(OPPORTUNITY_ID, payloadData.get(OPPORTUNITY_ID)); | ||
| noIntent.putExtra(PAYMENT_ID, payloadData.get(PAYMENT_ID)); | ||
| noIntent.putExtra(PAYMENT_STATUS, false); | ||
| PendingIntent noPendingIntent = PendingIntent.getBroadcast(this, 2, | ||
| noIntent, flags); | ||
|
|
||
| // Add Yes & No action button to the notification | ||
| fcmNotification.addAction(0, getString(R.string.connect_payment_acknowledge_notification_yes), yesPendingIntent); | ||
| fcmNotification.addAction(0, getString(R.string.connect_payment_acknowledge_notification_no), noPendingIntent); | ||
| } | ||
|
|
||
| NotificationManager mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); | ||
| mNM.notify(FCM_NOTIFICATION, fcmNotification.build()); | ||
| } | ||
| } | ||
|
|
||
| private boolean hasCccAction(String action) { | ||
| return action != null && action.contains("ccc_"); | ||
| } | ||
|
|
||
| public static void clearNotification(Context context){ | ||
| NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); | ||
| NotificationManager notificationManager = (NotificationManager) context.getSystemService( | ||
| Context.NOTIFICATION_SERVICE); | ||
| if (notificationManager != null) { | ||
| notificationManager.cancel(R.string.fcm_notification); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid duplicate message retrieval calls.
MessageManager.retrieveMessages()is called in bothonCreateViewandonResume. This could lead to unnecessary network calls and potential race conditions. Consider consolidating these calls or implementing a debounce mechanism.@Override public void onResume() { super.onResume(); isActive = true; LocalBroadcastManager.getInstance(requireContext()).registerReceiver(updateReceiver, new IntentFilter(CommCareFirebaseMessagingService.MESSAGING_UPDATE_BROADCAST)); - MessageManager.retrieveMessages(requireActivity(), success -> { - refreshUi(); - }); }📝 Committable suggestion