Search code examples
firebase-cloud-messagingandroid-roomandroid-livedata

Is there a way to get a LifecycleOwner in FirebaseMessagingService


I'm developing a chat app and I'm using Firebase Cloud Messaging for notifications. I found that it was best to save my notifications (notification info) in Local database i.e Room so it help me to handle the badge counts and the clearing of specific chat notifications.

Steps:

  1. Setup my FirebaseMessagingService and tested. (Getting my notifications successfully);
  2. Setup Room database and tested to insert and get all data (LiveData) (working good);
  3. I want to observe the liveData inside MyFirebaseMessagingService but to do so, I need a LivecycleOwner and I don't have any idea from where I will get it.

I searched on google but the only solution was to use a LifecycleService, but I need FirebaseMessagingService for my notification purpose.

this is my code:

//Room Database class
private static volatile LocalDatabase INSTANCE;
private static final int NUMBER_OF_THREADS = 4;
public static final ExecutorService taskExecutor =
        Executors.newFixedThreadPool(NUMBER_OF_THREADS);

public static LocalDatabase getDatabase(final Context context) {
    if (INSTANCE == null) {
        synchronized (RoomDatabase.class) {
            if (INSTANCE == null) {
                INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                        LocalDatabase.class, "local_database")
                        .build();
            }
        }
    }
    return INSTANCE;
}
public abstract  NotificationDao dao();




//DAO interface
@Insert
void insert(NotificationEntity notificationEntity);

@Query("DELETE FROM notificationentity WHERE trade_id = :tradeId")
int clearByTrade(String  tradeId);

@Query("SELECT * FROM notificationentity")
LiveData<List<NotificationEntity>> getAll();




//Repository class{}
private LiveData<List<NotificationEntity>> listLiveData;

public Repository() {
    firestore = FirebaseFirestore.getInstance();
    storage = FirebaseStorage.getInstance();
}
public Repository(Application application) {
    LocalDatabase localDb = LocalDatabase.getDatabase(application);
    dao = localDb.dao();
    listLiveData = dao.getAll();
}
...
public void saveNotificationInfo(@NonNull NotificationEntity entity){
    LocalDatabase.taskExecutor.execute(() -> {
        try {
            dao.insert(entity);
            H.debug("NotificationData saved in local db");
        }catch (Exception e){
            H.debug("Failed to save NotificationData in local db: "+e.getMessage());
        }
    });
}

public LiveData<List<NotificationEntity>> getNotifications(){return listLiveData;}

public void clearNotificationInf(@NonNull String tradeId){
    LocalDatabase.taskExecutor.execute(() -> {
        try {
            H.debug("trying to delete rows for id :"+tradeId+"...");
            int n = dao.clearByTrade(tradeId);
            H.debug("Cleared: "+n+" notification info from localDatabase");
        }catch (Exception e){
            H.debug("Failed clear NotificationData in local db: "+e.getMessage());
        }
    });
}




//ViewModel class{}
private Repository rep;
private LiveData<List<NotificationEntity>> list;

public VModel(@NonNull Application application) {
    super(application);
    rep = new Repository(application);
    list = rep.getNotifications();
}

public void saveNotificationInfo(Context context, @NonNull NotificationEntity entity){
    rep.saveNotificationInfo(entity);
}
public LiveData<List<NotificationEntity>> getNotifications(){
    return rep.getNotifications();
}
public void clearNotificationInf(Context context, @NonNull String tradeId){
    rep.clearNotificationInf(tradeId);
}




and finally the FiebaseMessagingService class{}
private static final String TAG = "MyFireBaseService";
private static final int SUMMARY_ID = 999;
private SoundManager sm;
private Context context;
private  final String  GROUP_KEY = "com.opendev.xpresso.group_xpresso_group_key";
private Repository rep;
private NotificationDao dao;

@Override
public void onCreate() {
    super.onCreate();
    context = this;
    rep = new Repository();
}

/**
 * Called if InstanceID token is updated. This may occur if the security of
 * the previous token had been compromised. Note that this is called when the InstanceID token
 * is initially generated so this is where you would retrieve the token.
 */
@Override
public void onNewToken(@NonNull String s) {
    super.onNewToken(s);
}

@Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
    super.onMessageReceived(remoteMessage);
    H.debug("OnMessageReceived...");
    try {
        Map<String, String> data = remoteMessage.getData();

        if (Objects.requireNonNull(data.get("purpose")).equals("notify_message")) {

            String ChatId
            if ((chatId=data.get("chatId"))==null){
                H.debug("onMessageReceived: tradeId null! Aborting...");
                return;
            }

            FirebaseFirestore db = FirebaseFirestore.getInstance();
            Task<DocumentSnapshot> tradeTask = db.collection("activeTrades").document(chatTask).get();
            Task<DocumentSnapshot> userTask = db.collection("users")
                    .document(FirebaseAuth.getInstance().getCurrentUser().getUid()).get();

            Tasks.whenAllSuccess(chatTask, userTask).addOnSuccessListener(objects -> {

                if (!((DocumentSnapshot)objects.get(0)).exists() || !((DocumentSnapshot)objects.get(1)).exists()){
                    H.debug("OnMessageReceived: querying data failed:  NOT EXISTS");
                    return;
                }
                Chat chat = ((DocumentSnapshot)objects.get(0)).toObject(Trade.class);
                MainActivity.USER = ((DocumentSnapshot)objects.get(1)).toObject(User.class);


                //Now we got all the needed info we cant process the notification
                //Saving the notification locally and updating badge count
                //then notify for all the notification in localDatabase

                    NotificationEntity entity = new NotificationEntity();
                    entity.setNotificationId(getNextNotificationId());
                    entity.setTradeId(tradeId);
                    entity.setChanelId(context.getResources().getString(R.string.channel_id));
                    entity.setTitle(data.get("title"));
                    entity.setMessage(data.get("message"));
                    entity.setPriority(NotificationCompat.PRIORITY_HIGH);
                    entity.setCategory(NotificationCompat.CATEGORY_MESSAGE);
                    rep.saveNotificationInfo(entity);
                    rep.getNotifications().observe(HOW_TO_GET_THE_LIVECYCLE_OWNER, new Observer<List<NotificationEntity>>() {
                        @Override
                        public void onChanged(List<NotificationEntity> notificationEntities) {
                            //
                        }
                    });
            }).addOnFailureListener(e -> H.debug("OnMessageReceived: querying data failed:  "+e.getMessage()));
        }
    }catch (Exception e){H.debug(e.getMessage());}
}

Solution

  • Updated, Because It is not recommended to use a LiveData object inside of a FirebaseMessagingService because a FirebaseMessagingService is not a part of the Android activity lifecycle and therefore does not have a lifecycle owner. Instead of trying to use LiveData inside of the FirebaseMessagingService, you could consider using a different approach to handle badge count and clearing specific chat notifications.

    So I used a broadcast receiver to receive the notifications. Then I could set the broadcast receiver in my FirebaseMessagingService, and it will receive the notifications and update the badge count in local Room database.

    I created a Broadcast Receiver for this, and in onReceive method I send a Intent to a service and handled the badge logic in service.

    I'm answering my own question just to show my alternative workaround. I believe the liveDataObserver still the best way for me but until someone help me by giving me the solution to get LivecycleOwner in FirebaseMessagingService, I'm going to use custom listener for my insert() and my getAll()

    like follow

    public interface RoomInsertListener{
        void onInsert();
    }
    public interface RoomGetListener{
        void onGet(List<NotificationEntity> list);
    }
    

    Then use it in FirebaseMessagingService as follow

    NotificationEntity entity = new NotificationEntity();
        entity.setNotificationId(getNextNotificationId());
        entity.setTradeId(tradeId);
        entity.setChanelId(context.getResources().getString(R.string.channel_id));
        entity.setTitle(data.get("title"));
        entity.setMessage(data.get("message"));
        entity.setPriority(NotificationCompat.PRIORITY_HIGH);
        entity.setCategory(NotificationCompat.CATEGORY_MESSAGE);
        rep.saveNotificationInfo(entity, () -> rep.getNotifications(list -> {
            ShortcutBadger.applyCount(context, list.size());
            H.debug(list.size()+" notifications in Database: applied badge count...");
            for (NotificationEntity e:list){
                H.debug("id:"+e.getNotificationId()+" trade: "+e.getTradeId());
            }
        }));