Tuesday, June 2, 2015

Geofence Transition Bitwise Logic


Problem:

You want to set multiple Geofence transitions either programmatically or through user input, but you can only call setTransitionTypes() once.

Solution:

Use bitwise OR on each transition type to get an int that can represent every combination of transitions.
public void createGeoFence(float rad, double lat, double lng,
    boolean enterChecked, boolean exitChecked, boolean dwellChecked) {
    //Set transitions based on what the user selects.
    int transitions = 0;
    if(enterChecked){
        transitions = Geofence.GEOFENCE_TRANSITION_ENTER;
    }
    if(exitChecked){
        transitions = transitions | Geofence.GEOFENCE_TRANSITION_EXIT;
    }
    if(dwellChecked){
        transitions = transitions | Geofence.GEOFENCE_TRANSITION_DWELL;
    }

    mGeofenceList.add(new Geofence.Builder()
        .setRequestId(fenceId)
        .setCircularRegion(lat, lng, rad)
        .setExpirationDuration(86400000) //24 hours
        .setTransitionTypes(transitions)
        .setNotificationResponsiveness(5000) //5 seconds
        .build());
}

The above sample method gets passed everything you need to construct a GeoFence. The booleans are the results of checking if the check boxes are checked, they will be true for each box that is checked, and false otherwise. I included a screenshot of the checkboxes for a visual aid.

Note that ints are primitive and can't be null, so if they are not assigned a value they are 0. Further, a transitionType of 0 will throw an error. Make sure to handle this. In another part of my code(not shown) I simply made it so that if the Dwell, Enter, and Exit controls were all unchecked the user would not be allowed to create the GeoFence and would be informed of their error.

Explanation:

Google has a pretty clever way for setting the transitions for a geofence. They use bitwise logic. So we should run with that. The pipe '|' operator is a bitwise OR.

Maybe you want to programmatically set your transitions on GeoFences, but you are having issues with the fact that you can only call setTransitionTypes() once for each GeoFence Builder. For me, I wanted to take user input, and add a transition type for each transition type the user selects. Instinctually you may want to examine each user input (checkboxes in this case) and then call setTransitionsTypes() for each one that the user enabled. But you can't. You can only call setTransitionTypes once, and you can only give it one argument.

So you must build your transition types before calling setTranstionTypes(). One simple and readable way to do this is a bitwise OR.
According to the Android API:
GEOFENCE_TRANSITION_ENTER has a value of 1 = 001 binary
GEOFENCE_TRANSITION_EXIT has a value of 2 = 010 binary
GEOFENCE_TRANSITION_DWELL has a value of 4 = 100 binary

Why these values? Look at the binary logic. Adding any combination of values will produce a unique result.
If we do an OR on ENTER and EXIT we get
001
010
-----
011
Which equals 3.

If we do EXIT and DWELL we get
010
100
----
110
Which equals 6.

So through bitwise OR you will always get a unique int between 1 and 7, inclusive. This explains why DWELL doesn't have a value of 3. Because that would mean there are multiple ways to get a value of 3. 

Let's pretend for a minute that DWELL = 011.
If you set that as the transition type, it's not clear if you want a DWELL, or if you want ENTER and EXIT transitions; these all evaluate to 3. And what if you wanted to set your transition types to DWELL and ENTER? There would be a bitwise OR between DWELL and ENTER, which would result in a value of 3 yet again, and the GeoFence wouldn't know if you wanted transitions on dwell, or dwell and enter.
Bitwise logic below, pretending DWELL is 011 and ENTER is 001 (which it really is).
001
011
----
011 

Now lets go back to real life where DWELL = 100 binary. How many different combinations of GeoFence transitions can you have?
That would be (2^3) -1 = 7. The minus 1 is because we can't send a value of 0 to setTransitionTypes without it throwing an error.

So what if Google wanted to add another transition type to the GeoFence? What's the lowest positive value they could use to not interfere with their schema?
It would just be the next highest power of 2, which is 8, also known as 1000 binary.
The number of combinations this would allow is (2^4) -1 = 15.

You may have noticed that in the entire scenario discussed in the post you could just add the values together and you will come up with the same int. The reason I chose the bitwise OR was because I liked the readability of it, and I liked keeping with the same logic Google is using. Adding up 3 ints and passing them to setTransitionTypes may appear to be slightly simpler code, but it makes very little sense if you don't already understand how setTransitionTypes works.


No comments:

Post a Comment