Accessing sensors at Android NDK

Many Android devices have built-in sensors to detect and measure motion, orientation, and other environmental conditions. It is possible to access sensors in Android NDK. This recipe will discuss how to do it in detail.

Getting ready

The example provided in this recipe is based on the sample code in the previous two recipes. Readers are recommended to read them first:

  • Managing native windows at Android NDK
  • Detecting and handling input events at Android NDK

How to do it…

The following steps develop the sample Android application, which demonstrates how to access sensors from Android NDK:

  1. Create an Android application named nativesensors. Set the package name as cookbook.chapter5.nativesensors. Please refer to the Loading native libraries and registering native methods recipe of Chapter 2, Java Native Interface, if you want more detailed instructions.
  2. Right-click on the nativesensors project, select Android Tools | Add Native Support.
  3. Update AndroidManifest.xml. Please refer to previous recipe or the downloaded code for details. Note that the metadata android.app.lib_name must have a value as nativesensors.
  4. Add two files named nativesensors.cpp and mylog.h under the jni folder. Let's show a part of the code in nativesensors.cpp.
    • handle_activity_lifecycle_events: This function handles activity lifecycle events. We enable the sensor when the activity is in focus and disable it when the activity loses its focus. This saves the battery life by avoiding reading sensors when our activity is not in focus:
      void handle_activity_lifecycle_events(struct android_app* app, int32_t cmd) {
        USERDATA* userData;
        switch (cmd) {
      …...
        case APP_CMD_SAVE_STATE:
          // save current state
          userData = (USERDATA*)(app->userData);
          app->savedState = malloc(sizeof(SAVED_USERDATA));
          *((SAVED_USERDATA*)app->savedState) = userData->drawingData;
          app->savedStateSize = sizeof(SAVED_USERDATA);
          break;
        case APP_CMD_GAINED_FOCUS:
          userData = (USERDATA*)(app->userData);
          if (NULL != userData->accelerometerSensor) {
            ASensorEventQueue_enableSensor(userData->sensorEventQueue,
                userData->accelerometerSensor);
            ASensorEventQueue_setEventRate(userData->sensorEventQueue,
                userData->accelerometerSensor, (1000L/60)*1000);
          }
          break;
        case APP_CMD_LOST_FOCUS:
          USERDATA userData = *(USERDATA*) app->userData;
          if (NULL!=userData.accelerometerSensor) {      ASensorEventQueue_disableSensor(userData.sensorEventQueue, userData.accelerometerSensor);
          }
          break;
        }
      }
    • android_main: We continuously poll for events and handle the sensor events identified by the LOOPER_ID_USER identifier:
      void android_main(struct android_app* app) {
      … ...
      while (0==app->destroyRequested) {
        int ident, events;
        struct android_poll_source* source;
        if ((ident=ALooper_pollOnce(-1, NULL, &events, (void**)&source)) >= 0) {
          if (LOOPER_ID_USER == ident) {
          ASensorEvent event;
          while (ASensorEventQueue_getEvents(userData.sensorEventQueue,
              &event, 1) > 0) {
            int64_t currentTime = get_time();
            … ...
            if ((currentTime - lastTime) > TIME_THRESHOLD) {
              long diff = currentTime - lastTime;
              float speedX = (event.acceleration.x - lastX)/diff*10000;
              float speedY = (event.acceleration.y - lastY)/diff*10000;
              float speedZ = (event.acceleration.z - lastZ)/diff*10000;
              float speed = fabs(speedX + speedY + speedZ);
      …...        
            }
          }
          }
        }
      }
      ASensorManager_destroyEventQueue(userData.sensorManager, userData.sensorEventQueue);
      }
      
  5. Add the Android.mk file under the jni folder, which is similar to the one used in the previous recipe. We just need to replace the module name as nativesensors and the source file as nativesensors.cpp.
  6. Build the Android application and run it on an emulator or device. We can shake the device to see the rectangle moving horizontally:
    How to do it…

How it works…

In our example, we used the accelerometer sensor to detect phone shaking. Then, based on the phone shaking speed, we move the red rectangle to one side of the phone screen. Once the rectangle reaches an edge of the phone screen, it starts to move to the other edge.

The example code provides a simple algorithm to determine whether a shake has happened or not. More complex and accurate algorithms exist and can be implemented. We can also adjust the SHAKE_TIMEOUT and SHAKE_COUNT_THRESHOLD constants to fine tune the algorithm.

The important part of the example is how to access sensors. Let's summarize the steps:

  1. Get a reference to the sensor manager: This is done by using the following function:
    ASensorManager* ASensorManager_getInstance();
  2. Get the default sensor of a given type: We can also get a list of all available sensors. This is done by using the following two functions respectively:
    ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type);
    int ASensorManager_getSensorList(ASensorManager* manager, ASensorList* list);

    The available types are defined in android/sensor.h. In our example, we print all sensor names and types but only use ASENSOR_TYPE_ACCELEROMETER.

  3. Create a new sensor queue and attach it to the looper of the thread: This is done by using the ASensorManager_createEventQueue function as follows:
    ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager, ALooper* looper, int ident, ALooper_callbackFunc callback, void* data);

    The usage of this function is similar to the usagw of the ALooper_addFd function in the Creating a native activity with the Android native app glue recipe and AInputQueue_attachLooper in the Detecting and handling input events at Android NDK recipe. In our example, we set the ident as LOOPER_ID_USER. Note that we may also define a new looper ID by changing the code of android_native_app_glue.h and setting it here.

  4. Enable and configure the sensor:
    int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor);
    int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor, int32_t usec);

    The first function enables the sensor referred by the sensor input argument. The second function sets the delivery rate of the events, in microseconds, for the sensor referred by the sensor input argument. In our example, we called these two functions when the activity gained focus.

  5. Poll for events and get the available events from the queue: The polling is done by calling ALooper_pollOnce, as shown in the previous recipe. If the event identifier returned is LOOPER_ID_USER, we know that it is a sensor event and we can use the following function to get it:
    ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);

    count indicates the maximum number of available events we want to get. In our example, we set it to 1. It is also possible to define an array of ASensorEvent and get multiple events at one time.

  6. Handle sensor events: The sensor event is represented by the ASensorEvent data structure, which can be found at android/sensor.h (the exact path to the file is <Android NDK root dir>/platforms/android-<version>/arch-arm/usr/include/android/sensor.h). In our example, we accessed the acceleration readings at the x, y, and z axes, and used the readings to determine if a phone shake has happened.
  7. Disable the sensor: After you are done accessing the sensors, you can disable it with the following function:
    int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sensor);
  8. Destroy the sensor event queue and free all resources associated with it:
    int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* queue);
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset