CultureQuestORG/SDP2023

View on GitHub
app/src/main/java/ch/epfl/culturequest/notifications/FireMessaging.java

Summary

Maintainability
A
1 hr
Test Coverage
B
86%
package ch.epfl.culturequest.notifications;

import androidx.annotation.NonNull;

import com.google.firebase.messaging.FirebaseMessaging;
import com.google.gson.JsonObject;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;

import ch.epfl.culturequest.backend.notifications_api.ApiClient;
import ch.epfl.culturequest.database.Database;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

/**
 * Class that handles the Firebase messaging operations
 */
public class FireMessaging {
    private static final FirebaseMessaging messagingInstance = FirebaseMessaging.getInstance();

    /**
     * This method returns the device token of the current device being used. This token can change
     * several times due to several reasons, this is this method is called at specific times to
     * handle these changes.
     *
     * @return a CompletableFuture that will be completed with the device token of the current device.
     */
    public static CompletableFuture<String> getDeviceToken() {
        CompletableFuture<String> future = new CompletableFuture<>();
        messagingInstance.getToken().addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                future.complete(task.getResult());
            } else {
                future.completeExceptionally(task.getException());
            }
        });
        return future;
    }

    /**
     * This method send a notification to a user with the given uid. It first adds the notification
     * to the database and then sends the notification to the user. This notification is sent to all
     * the registered devices of the user.
     * This notification is sent to the user by using the Retrofit Rest client to send a POST request
     * to the Firebase Cloud Messaging API that will then send the notification to the user.
     *
     * @param uid          the uid of the user to send the notification to
     * @param notification the notification to send
     * @return a CompletableFuture that will be completed with a boolean indicating if the
     * notification was sent successfully
     */
    public static List<CompletableFuture<AtomicBoolean>> sendNotification(String uid, PushNotification notification) {
        List<CompletableFuture<AtomicBoolean>> futures = new ArrayList<>();

        // add notification to database first
        Database.addNotification(uid, notification);

        // send the notification to all devices of the user
        Database.getDeviceTokens(uid).whenComplete((deviceTokens, throwable) -> {
            if (throwable != null || deviceTokens.isEmpty()) {
                futures.add(CompletableFuture.completedFuture(new AtomicBoolean(false)));
                return;
            }

            // initialize the futures list with the correct number of futures
            for (int i = 0; i < deviceTokens.size(); i++) {
                futures.add(new CompletableFuture<>());
            }

            for (int i = 0; i < deviceTokens.size(); i++) {
                JsonObject payload = buildNotificationPayload(deviceTokens.get(i), notification);
                int finalI = i;
                ApiClient.getApiService().sendNotification(payload).enqueue(new Callback<>() {
                    @Override
                    public void onResponse(@NonNull Call<JsonObject> call, @NonNull Response<JsonObject> response) {
                        if (response.isSuccessful()) {
                            futures.get(finalI).complete(new AtomicBoolean(true));
                        } else {
                            futures.get(finalI).complete(new AtomicBoolean(false));
                        }
                    }

                    @Override
                    public void onFailure(@NonNull Call<JsonObject> call, @NonNull Throwable t) {
                        futures.get(finalI).complete(new AtomicBoolean(false));
                    }
                });
            }
        });
        return futures;
    }

    /**
     * This method builds the payload (json file) of the notification to send to the user with
     * the given device token.
     *
     * @param token        the device token of the user to send the notification to
     * @param notification the notification to send
     * @return the payload of the notification to send
     */
    private static JsonObject buildNotificationPayload(String token, PushNotification notification) {
        JsonObject payload = new JsonObject();
        payload.addProperty("to", token);
        JsonObject data = new JsonObject();
        data.addProperty("title", notification.getTitle());
        data.addProperty("text", notification.getText());
        data.addProperty("channelId", notification.getChannelId());
        data.addProperty("senderId", notification.getSenderId());
        payload.add("data", data);
        return payload;
    }
}