Introduction
In a previous article, the Gimbal SDK v1.x Beta for Android as it pertains to proximity beacons was explored. Topics covered included going over the concept of beacons, contextual location awareness, beacon arrivals & departures, potential beacon use cases, and creating the necessary classes to set up an Android application to work with the SDK. This is to discover and react accordingly with beacons. It is assumed that the previous article was followed and upgrading from 1.x Beta of the Gimbal SDK to v2.x Beta is desired. The new features of the Gimbal SDK will be discussed, along with modifying the classes previously created to work with the new API. It is important to keep in mind that Gimbal is constantly changing the SDK by adding and removing features. Because of this, it is possible this article may not be relevant to every situation. Other areas of confusion may be the differences between the Beta and non-Beta versions. Gimbal may introduce a feature into the Beta version of the SDK and then later move it to the non-Beta version. In addition, Gimbal used to previously label the v1.x SDK as one with Proximity and one without (two separate versions). This is no longer the case with v2.x of the SDK, as all features have been consolidated into it. Thus, due to the rapid development rate of the SDK, newer versions may not 100% align with the material contained within this article. Therefore, it is recommended that the reader refer to the official Gimbal Documentation in those situations and as good practice. This article complements the official documentation.
Note: This article only covers the features of the SDK that involve beacons. Not all features involving beacons will be covered, so be sure to explore the various Gimbal classes and their methods. Moreover, before beginning, make sure to read this article on properly setting up the v2.x Beta SDK with Android Studio as it is slightly different from the v1.x Beta SDK. A final thing to keep in mind is that earlier versions of Android (4.4.3 and 4.4.4) disable the Gimbal service by default. To enable these Android versions to work with the new SDK, please visit the Gimbal Developer Account and view the application settings page shown in the next section.
Abstracting Location Providers with Places
Previously, individual beacons were discovered and acted upon independently. Each disparate beacon would trigger an arrival or departure event when within the appropriate range. Things would get tricky when adding multiple beacons into a large room and trying to get them to react to an event logically as one beacon. This is no longer the case in v2.x Beta of the Gimbal SDK. To fix this and make it easier to work with the various location providers (beacons, geofences, etc.), the concept of Places was introduced. Places abstract away the location provider so that the developer only needs to be concerned with a Place itself. The Place can trigger an arrival or departure, now called Entry and Exit, just like an individual beacon did. However, a Place can have a combination of one or more beacons and geofences. Creating and managing Places is done from the Gimbal Developer Account. As long as beacons have been registered and added to the account, when creating a Place they will be usable. The same thing goes for geofences. This has greatly reduced the complexity of working with location providers, and now it is possible for a group of beacons to logically appear as one.
Updating the Gimbal Helper Class

The concept of Places has changed the way the SDK works. The Proximity class is no longer present. Manually setting options such as the signal smoothing window has changed, and Gimbal recommends setting this from within the Gimbal Developer Account (see image). The SDK will phone home and get these settings from the backend. This certainly makes things easier.
Other things that have changed are the removal of the interfaces ProximityListener and VisitListener. There is now the introduction of new classes that can be extended to provide callback mechanisms for Entries, Exits, and beacon sightings. These classes are PlaceEventListener and BeaconEventListener. They are not interfaces that require implementation but rather abstract classes.
Below is the updated helper class, which is implemented as a singleton. Please compare this to the code from the previous article.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
public class GimbalHelper { private YourApplication mApplication; private boolean mIsInitialized; private boolean mIsProximityServiceRunning; private boolean mHasOptions; private GimbalPlaceListener mGimbalPlaceListener; private GimbalBeaconListener mGimbalBeaconListener; private PlaceManager mPlaceManager; private BeaconManager mBeaconManager; private static volatile ProximityHelper mInstance; private GimbalHelper() { mApplication = null; mIsInitialized = false; mIsGimbalServiceRunning = false; mPlaceManager = null; mGimbalPlaceListener = null; mGimbalBeaconListener = null; } public static GimbalHelper getInstance() { if (mInstance == null) { synchronized (GimbalHelper.class) { if (mInstance == null) { mInstance = new GimbalHelper(); } } } return mInstance; } public boolean getIsGimbalServiceRunning() { return mIsGimbalServiceRunning; } public PlaceManager getPlaceManager() { if (mPlaceManager == null) { mPlaceManager = PlaceManager.getInstance(); } return mPlaceManager; } public BeaconManager getBeaconManager() { if (mBeaconManager == null) { mBeaconManager = BeaconManager.getInstance(); } return mBeaconManager; } public YourApplication getApplication() { return mApplication; } public void setApplication(YourApplication application) { mApplication = application; } public void setIsGimbalServiceRunning(boolean isGimbalServiceRunning) { mIsGimbalServiceRunning = isGimbalServiceRunning; } public void startGimbalService() { if (!getIsGimbalServiceRunning()) { LogHelper.logDebug(LOG_TAG, "Starting the Gimbal Service."); if (mApplication == null) { throw new IllegalArgumentException("mApplication cannot be null. Did you set the application reference using setApplication()?"); } if (!mIsInitialized) { initialize(); if (mGimbalPlaceListener == null) { mGimbalPlaceListener = new GimbalPlaceListener(); } if (mGimbalBeaconListener == null) { mGimbalBeaconListener = new GimbalBeaconListener(); } getPlaceManager().addPlaceEventListener(mGimbalPlaceListener); getPlaceManager().startMonitoring(); getBeaconManager().addListener(mGimbalBeaconListener); getBeaconManager().startMonitoring(); } } else { Log.d("Gimbal Helper", "Cannot start service as it's already running."); } } public void stopGimbalService() { if (mIsGimbalServiceRunning) { Log.d("Gimbal Helper", "Stopping the Gimbal Service."); mGimbalPlaceListener = null; getPlaceManager().stopMonitoring(); getPlaceManager().removePlaceEventListener(mGimbalPlaceListener); mGimbalBeaconListener = null; getBeaconManager().stopMonitoring(); getBeaconManager().removeListener(mGimbalBeaconListener); mPlaceManager = null; mBeaconManager = null; } else { Log.d("Gimbal Helper", "Cannot stop service as it isn't currently running."); } } private void initialize() { if (mApplication == null) { throw new IllegalArgumentException("mApplication cannot be null. Did you properly set the application reference?"); } else if (!mIsInitialized) { Gimbal.setApiKey(mApplication, "Gimbal API key"); mIsInitialized = true; } else { Log.d("Gimbal Helper", "Cannot initialize as Gimbal Service is already running."); } } public void setLoggingLevel(int loggingLevel) { Log.d("Gimbal Helper", "Setting logging level."); switch (loggingLevel) { case DEBUG_LOGGING: GimbalLogConfig.setLogLevel(GimbalLogLevel.DEBUG); break; case WARNING_LOGGING: GimbalLogConfig.setLogLevel(GimbalLogLevel.WARN); break; case ERROR_LOGGING: GimbalLogConfig.setLogLevel(GimbalLogLevel.ERROR); break; case INFO_LOGGING: GimbalLogConfig.setLogLevel(GimbalLogLevel.INFO); break; case TRACE_LOGGING: GimbalLogConfig.setLogLevel(GimbalLogLevel.TRACE); break; default: break; } } } |
Implementing the new PlaceEventListener
The new PlaceEventListener abstract class must be implemented to provide the onEntry and onExit callback methods. This will be triggered when a Place is entered or exited (previously called arrivals and departures). Below is the implementation of the PlaceEventListener.
1 2 3 4 5 6 7 8 9 10 11 |
public class GimbalPlaceListener extends PlaceEventListener { @Override public void onEntry(Place place, long entryTimeInMillis) { Log.d("Gimbal Place Listener", "Entered Gimbal Place " + place.getName() + ", ID " + place.getIdentifier() + "."); } @Override public void onExit(Place place, long entryTimeInMillis, long exitTimeInMillis) { Log.d("Gimbal Place Listener", "Gimbal Place " + place.getName() + " has exited."); } } |
Implementing the new BeaconEventListener
The new BeaconEventListener abstract class must be implemented to provide the onBeaconSighting callback method. This will be triggered when a beacon is sighted (within detectable range). Below is the implementation of the BeaconEventListener.
1 2 3 4 5 6 |
public class GimbalBeaconListener extends BeaconEventListener { @Override public void onBeaconSighting(BeaconSighting sighting) { Log.d("Gimbal Beacon Listener", "Beacon " + sighting.getBeacon().getName() + " with a signal strength of " + sighting.getRSSI() + " has been sighted."); } } |
Conclusion
This article focused on the changes made to the new Gimbal SDK v2.x Beta for Android. Along with the knowledge from the previous article, the Gimbal Helper class was updated and the new event listeners were implemented. The new SDK is easier to use with the concept of Places, which is a welcome abstraction. The changes made to the Gimbal Developer Account make it easy to set options which were previously defined in a custom method. With this new knowledge, updating to the v2.x Beta SDK should be easier to perform. As always, if any help is desired, please post in the comments section below. Happy Gimbaling.