0

Following up on this grate answer I use the ValueEventListener to listen for only new items with this query:

Query query = ref.child(USER)  
                    .child(mFireBaseAuth.getCurrentUser().getUid())
                    .child(TOYS)
                    .orderByKey().startAt(-KXN04BDYdEG0aii9mMY);

query.addValueEventListener(mvalueEventListener);

The -KXN04BDYdEG0aii9mMY in the code sample above is the latest item i already have and I want to get only push keys after that time stamp. With this code I also get the -KXN04BDYdEG0aii9mMY again and that is not needed because I already have it right.

How should I solve this? Maybe add a millisecond to this Firebase push key -KXN04BDYdEG0aii9mMY?

Under the TOYS key is a list of Firebase Puch keys

Community
  • 1
  • 1
Tord Larsen
  • 2,670
  • 8
  • 33
  • 76

2 Answers2

1

There is no way to specify an exclusive anchor with Firebase Database queries, so you will always get the key that you specify for startAt() (and also if you specify it endAt()). So you'll have to skip the first/last item in your client-side code.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • This was tricky one - I have to both filter away the key that I specify for `startAt()` and at the same time in the ´onDataChange()´ handle a second call because of TimeStamp correcting from Firebase, when doing ´...ServerValue.TIMESTAMP);´ Would love to see some open source code doing just this in a proper way, would be nice to learn it the right way – Tord Larsen Nov 26 '16 at 09:52
0

I got it working just like I was asking for in the question. Here is the code since it´s the answer for me, and for anyone's delight.

This was a tricky one I have to both filter away the key that I specify for startAt() and at the same time in the ´onDataChange()´ handle a second call because of TimeStamp correcting from Firebase, when doing ´...ServerValue.TIMESTAMP);´

       // When startAt() get defined this will make sure that we dont save the 
       // startAt key item again.
       // Best would be if the startAfter("pushKey") existed but it does not.
       String skipStartAtKey = "";        


         /**
         * Typically when user sign in or app start
         */
        public void userSignedIn() {
            Log.i(TAG, "userSignedIn");
            // start listening
            activateFirebaseListeners();
        }

         /**
         * When user sign in this listeners are started
         */
        private void activateFirebaseListeners() {

           // [START Listen for history Address changes ]

            /**
             * The ChildEventListener.
             * This is only used when app first install or user wipe data
             */
            ChildEventListener userHistoryAddressChildEventListener = new ChildEventListener() {

                @Override
                public void onChildAdded(DataSnapshot snapshot, String prevKey) {
                    userHistoryAddressChildEvent(snapshot, prevKey);
                }

                @Override
                public void onChildChanged(DataSnapshot snapshot, String prevKey) {
                    userHistoryAddressChildEvent(snapshot, prevKey);
                }

                @Override
                public void onCancelled(DatabaseError error) {
                 // TODO dont forget to remove reset listeners
                }

                @Override
                public void onChildRemoved(DataSnapshot snapshot) {
                }

                @Override
                public void onChildMoved(DataSnapshot snapshot, String prevKey) {
                }
            };

            // If client don´t have any history start the ChildEventListener.
            // Typically this is the first time app starts or user have cleared data.
            if (ToyManager.getInstance().getHistoryItems(mFireBaseAuth.getCurrentUser().getUid()).size() == 0) {
                Log.i(TAG, "HistoryItems SIZE = 0 starting ChildEventListener");
                // Local cache of address history is empty get all address history for this user and start listen for new items
                final Query query = ref.child(USER_HISTORY).child(mFireBaseAuth.getCurrentUser()
                   .getUid())
                   .child(TOYS);
                mChildListenerMap.put(query.getRef(), userHistoryAddressChildEventListener);
                query.addChildEventListener(userHistoryAddressChildEventListener);

            } else {
                // If client have history then start the ValueEventListener.
                // Typically this is not the first time app starts.
                startListenForUserAddressHistory();
            }
            // [END Listen for history Address changes ]
        }

         private void userHistoryAddressChildEvent(DataSnapshot snapshot, String prevKey) {
            // get history for current user
            UserHistory.AddressHistory addressHistory = snapshot.getValue(UserHistory.AddressHistory.class);
            ToyManager.getInstance().addHistory(
                    mFireBaseAuth.getCurrentUser().getUid(),
                    addressHistory.getAddressId(),
                    addressHistory.getTime(),
                    addressHistory.getToy(),
                    addressHistory.getPushKey());
        }

     /**
         * Start listen for nye entries based on that server have entries<br>
         * and client have entries, typically this is not the first time App starts.
         * This uses a ValueEventListener.
         */
        private void startListenForUserAddressHistory() {
            // Local history is not empty so we must go get only new items
            final ValueEventListener listener = new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    // get history for current user
                    if (dataSnapshot.getChildrenCount() != 0) {
                        for (DataSnapshot child : dataSnapshot.getChildren()) {
                            UserHistory.AddressHistory addressHistory = child.getValue(UserHistory.AddressHistory.class);
                            if (dataSnapshot.getChildrenCount() == 1) {
                                // child count is one so this can be a SERVERTIME correctness call.
                                // skip the skipStartAtKey if key and time is the same.
                                if (addressHistory.getPushKey().equals(skipStartAtKey)) {
                                    // get historyItem
                                    HistoryItem historyItem = ToyManager.getInstance().getHistoryItem(mFireBaseAuth.getCurrentUser().getUid().concat(addressHistory.getPushKey()));
                                    // compare time
                                    if (historyItem.getTime().toString().equals(addressHistory.getTime().toString())) {
                                        Log.i(TAG, "time is the same exiting with return");
                                        return;
                                    } else
                                        Log.i(TAG, "time is different");
                                } else {
                                    Log.i(TAG, "PushKey not same as skipStartAtKey");
                                }

                            } else if (dataSnapshot.getChildrenCount() > 1) {
                                // two children or more so lets dump the skipStartAtKey and save the rest
                                Log.i(TAG, "TESTING  getChildrenCount > 1" + " skipStartAtKey " + skipStartAtKey + " time " + addressHistory.getTime());
                                if (addressHistory.getPushKey().equals(skipStartAtKey)) {
                                    Log.i(TAG, "PushKey same as skipStartAtKey");
                                    continue;
                                }
                            }
                            ToyManager.getInstance().addHistory(
                                    mFireBaseAuth.getCurrentUser().getUid(),
                                    addressHistory.getAddressId(),
                                    addressHistory.getTime(),
                                    addressHistory.getToy(),
                                    addressHistory.getPushKey());
                        }
                        ValueEventListener v = mValueListenerMap.get(dataSnapshot.getRef());
                        Log.i(TAG, "removing ValueEventListener for");
                        dataSnapshot.getRef().removeEventListener(v);
                        startListenForUserAddressHistory();
                    }
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {
                    Log.w(TAG, "onCancelled" + databaseError.toException());
                     // TODO dont forget to remove reset listeners
                }
            };
            // get the latest history item
            HistoryItem historyItem = ToyManager.getInstance().orderHistoryByDateAndGetNewest(mFireBaseAuth.getCurrentUser().getUid());
            final Query query = ref.child(USER_HISTORY)
                    .child(mFireBaseAuth.getCurrentUser().getUid())
                    .child(TOYS)
                    .orderByKey().startAt(historyItem.getPushKey()); // start listen on the latest history
            mValueListenerMap.put(query.getRef(), listener);
            skipStartAtKey = historyItem.getPushKey();
            query.addValueEventListener(listener);
        }


         /**
         * App is closing or User has signed out
         * Called from both onClose() and userSignedOut()
         */
        private void closeFirebase() {
            userLogger.log(USER_SIGNED_OUT);

            // Close ChildEventListener(s)
            for (Map.Entry<DatabaseReference, ChildEventListener> entry : mChildListenerMap.entrySet()) {
                DatabaseReference ref = entry.getKey();
                ChildEventListener listener = entry.getValue();
                if (listener != null && ref != null)
                    ref.removeEventListener(listener);
            }
            // Close ValueEventListener(s)
            for (Map.Entry<DatabaseReference, ValueEventListener> entry : mValueListenerMap.entrySet()) {
                DatabaseReference ref = entry.getKey();
                ValueEventListener listener = entry.getValue();
                if (listener != null && ref != null)
                    ref.removeEventListener(listener);
            }
            // Close
            if (userValueEventListener != null)
                ref.child(USER).child(mFireBaseAuth.getCurrentUser().getUid())
                        .removeEventListener(userValueEventListener);
            if (userAuthListener != null)
                mFireBaseAuth.removeAuthStateListener(userAuthListener);
            userLogger = null;


            }
Tord Larsen
  • 2,670
  • 8
  • 33
  • 76