0

Data Hunting With Android

Share this article!

Before “analyzing” any data, we “need” data. In this tutorial I am going to demonstrate how to gather users’ Location Data with their current activity using google-play-services location api in Android. We will use fusedLocationApi and activityRecognitionApi of google-locations-api. For storing the data in real time we will use Firebase. So let’s get started.

This is what our final result will look like.

 

 

You can view and download the source code from Github anytime.

Pre-requisite Setup:

  1. Firebase Setup
  2. Play-Services Setup

After the introduction of google fusedlocation api, it has become quite easy to set things up in code and make battery efficient applications for the users. We are going to collect location parameters ( lat, lon, radiuserror, time) and for activity recognition, we will store type, confidence level , time.

I hope you have your Firebase setup ready. Now add these dependencies

compile ‘com.google.firebase:firebase-database:10.0.1’

compile ‘com.google.android.gms:play-services-location:10.0.1’

MainActivity

Our Main activity is simple. Start the service on click of a button.

btnStart.setOnClickListener(new View.OnClickListener() {
  @Override public void onClick(View view) {
    startService(new Intent(MainActivity.this , MyLocationService.class));
  }
});

MyLocationService

This is the meat of the application. So in this service we first init our googleApiClient and firebase reference object. When  mGoogleApiClient.connect()  runs , onConeccted callback is triggered where our LocationRequest object is built and FusedLocationApi is used to requestLocationUpdates. After that onLocationChanged is called whenever there is a change in the location according to the filters we have applied to the LocationRequest object. This service runs inside different processes, so even if the app is closed the service keeps running and sends real-time location and activity data :v. At last, inside our onLocationChange method, we call requestActivityUpdatesHandler which triggers a pending Intent DetectedActivitiesIntentService.

public class MyLocationService extends Service
    implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
    com.google.android.gms.location.LocationListener , ResultCallback<Status> {

  private String TAG = "MyLocationService";
  private GoogleApiClient mGoogleApiClient;
  private LocationRequest mLocationRequest;
  private FirebaseDatabase database;
  private DatabaseReference myRefLocation;

  public static final long DETECTION_INTERVAL_IN_MILLISECONDS = 5000;

  @Override public void onCreate() {
    super.onCreate();
    buildGoogleApiClient();

    FirebaseApp.initializeApp(getBaseContext());

    // Write a message to the database
    database = FirebaseDatabase.getInstance();
    myRefLocation = database.getReference("location");

  }


  protected synchronized void buildGoogleApiClient() {
    mGoogleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this)
        .addOnConnectionFailedListener(this)
        .addApi(LocationServices.API)
        .addApi(ActivityRecognition.API)
        .build();
    mGoogleApiClient.connect();
  }

  @Override public int onStartCommand(Intent intent, int flags, int startId) {
    Log.i(TAG, "service starting");

    return START_STICKY;
  }

  @Nullable @Override public IBinder onBind(Intent intent) {
    return null;
  }

  @Override public void onConnected(@Nullable Bundle bundle) {

    mLocationRequest = LocationRequest.create();
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    // get updates when displacement is 50 or more
    mLocationRequest.setSmallestDisplacement(50);
    if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED
        && ActivityCompat.checkSelfPermission(this,
        android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
      // TODO: Consider calling
      //    ActivityCompat#requestPermissions
      // here to request the missing permissions, and then overriding
      //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
      //                                          int[] grantResults)
      // to handle the case where the user grants the permission. See the documentation
      // for ActivityCompat#requestPermissions for more details.
      return;
    }
    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest,
        this);
  }

  @Override public void onConnectionSuspended(int i) {

  }

  @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

  }

  @Override public void onLocationChanged(Location location) {

    String latitude = String.valueOf(location.getLatitude());
    String longitude = String.valueOf(location.getLongitude());
    String accuracy = String.valueOf(location.getAccuracy());
    String time;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
      time = String.valueOf(location.getElapsedRealtimeNanos());
    }else {
      time = String.valueOf(location.getTime());
    }
    MyLocation myLocation = new MyLocation(latitude, longitude, accuracy , time);

    myRefLocation.push().setValue(myLocation);


    requestActivityUpdatesHandler();

  }

  public void requestActivityUpdatesHandler() {
    if (!mGoogleApiClient.isConnected()) {
      Log.e(TAG, "GoogleApi is not Connected");
      return;
    }
    ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(
        mGoogleApiClient,
        DETECTION_INTERVAL_IN_MILLISECONDS,
        getActivityDetectionPendingIntent()
    ).setResultCallback(this);
  }


  private PendingIntent getActivityDetectionPendingIntent() {
    Intent intent = new Intent(this, DetectedActivitiesIntentService.class);

    // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
    // requestActivityUpdates() and removeActivityUpdates().
    return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
  }


  @Override public void onDestroy() {
    super.onDestroy();
    if (mGoogleApiClient.isConnected()) {
      mGoogleApiClient.disconnect();
    }
  }

  @Override public void onResult(@NonNull Status status) {

    if (status.isSuccess()) {
      Log.e(TAG, "Successfully added activity detection.");

    } else {
      Log.e(TAG, "Error adding or removing activity detection: " + status.getStatusMessage());
    }
  }
}

DetectedActivitiedIntentServicete

Handling Activity Recognition Intentservice in onHandleIntent by getting all the DetectedActivities from detectedActivities. The getType() method  tells us the type of activity the user might be doing with its confidence level. So we now bind all our data into myActivites object including type of the activity , its confidence level and time of recording that information. The object is then pushed to the firebase. At the time of pushing the data, even if the Internet is weak or not available firebase caches the data to be sent later.

public class DetectedActivitiesIntentService extends IntentService {
  protected static final String TAG = "detection_is";
  private FirebaseDatabase database;
  private DatabaseReference myRefActivities;

  /**
   * This constructor is required, and calls the super IntentService(String)
   * constructor with the name for a worker thread.
   */
  public DetectedActivitiesIntentService() {
    // Use the TAG to name the worker thread.
    super(TAG);
  }

  @Override
  public void onCreate() {
    super.onCreate();

    database = FirebaseDatabase.getInstance();
    myRefActivities = database.getReference("activity");
  }

  /**
   * Handles incoming intents.
   * @param intent The Intent is provided (inside a PendingIntent) when requestActivityUpdates()
   *               is called.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
    ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);

    // Get the list of the probable activities associated with the current state of the
    // device. Each activity is associated with a confidence level, which is an int between
    // 0 and 100.
    ArrayList<DetectedActivity> detectedActivities = (ArrayList) result.getProbableActivities();

    // Log each activity.
    Log.i(TAG, "activities detected");

    for(DetectedActivity thisActivity: detectedActivities){
      String type = String.valueOf(thisActivity.getType());
      String confidence = String.valueOf(thisActivity.getConfidence());
      String time = String.valueOf(System.currentTimeMillis());

      MyActivity myActivity = new MyActivity(type, confidence, time);
      myRefActivities.push().setValue(myActivity);


    }
  }
}

Don't forget to add permissions in the manifest
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/>


Add the services in the manifest.

<service android:name=".MyLocationService"
  android:process=":remote"/>
<service
    android:name=".DetectedActivitiesIntentService"
    android:exported="false" />


Yay! you made it to the end. Once you have the data you can do many things with it. SEIZE THE DATA!

Share this article!

Rishabh Jindal

I’m a core Android developer on an adventure to explore the depths of Data. I like taking up complex problems and turning them into beautiful Solutions. My love for programming the logic and modular code keeps me pushing towards writing elegant and beautiful solutions whether it is Android, Python or any other platform.
LinkedIn: linkedin.com/in/rrishabhj/

Latest posts by Rishabh Jindal (see all)

Rishabh Jindal

I’m a core Android developer on an adventure to explore the depths of Data. I like taking up complex problems and turning them into beautiful Solutions. My love for programming the logic and modular code keeps me pushing towards writing elegant and beautiful solutions whether it is Android, Python or any other platform. LinkedIn: linkedin.com/in/rrishabhj/

Leave a Reply

Your email address will not be published. Required fields are marked *