Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save francislainy/c4fa12d1122fde80e932444b7498aa16 to your computer and use it in GitHub Desktop.

Select an option

Save francislainy/c4fa12d1122fde80e932444b7498aa16 to your computer and use it in GitHub Desktop.
GoogleFit Get Distance
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.Scopes;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.fitness.Fitness;
import com.google.android.gms.fitness.FitnessStatusCodes;
import com.google.android.gms.fitness.data.Bucket;
import com.google.android.gms.fitness.data.DataPoint;
import com.google.android.gms.fitness.data.DataSet;
import com.google.android.gms.fitness.data.DataSource;
import com.google.android.gms.fitness.data.DataType;
import com.google.android.gms.fitness.data.Field;
import com.google.android.gms.fitness.data.Value;
import com.google.android.gms.fitness.request.DataReadRequest;
import com.google.android.gms.fitness.result.DailyTotalResult;
import com.google.android.gms.fitness.result.DataReadResult;
import com.google.gson.JsonObject;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static java.text.DateFormat.getDateInstance;
/**
* Created by fcampos on 17/05/2018.
*/
public class GoogleFitUtils {
private final static String LOG_TAG = GoogleFitUtils.class.getName();
private static final int ACCESS_FINE_LOCATION_REQUEST_CODE = 200;
private static final int SENSORS_REQUEST_CODE = 300;
private static boolean checkPermissionsAccessFineLocation() {
int permissionState = ActivityCompat.checkSelfPermission(App.getInstance(),
Manifest.permission.ACCESS_FINE_LOCATION);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
public static void checkSensorsPermission(Activity activity) {
if (!checkPermissionsBodySensor()) {
if (activity != null) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.BODY_SENSORS}, SENSORS_REQUEST_CODE);
}
}
}
public static void checkAccessFineLocationPermission(Activity activity) {
if (!checkPermissionsAccessFineLocation()) {
if (activity != null) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, ACCESS_FINE_LOCATION_REQUEST_CODE);
}
}
}
// Sensor
private static boolean checkPermissionsBodySensor() {
int permissionState = ActivityCompat.checkSelfPermission(App.getInstance(),
Manifest.permission.BODY_SENSORS);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
private static void updateDistanceGoogle(float distanceCount, FragmentManager fm) {
Log.d(LOG_TAG, "updateDistanceGoogle ===== " + distanceCount);
HomeFragment homeFrag = (HomeFragment) fm.findFragmentByTag(String.valueOf(MainActivity.HOME_MAIN));
if (homeFrag != null && homeFrag.isVisible()) {
homeFrag.distanceFromGoogleFit(distanceCount);
}
}
/*
Build a GoogleApiClient that will authenticate the user and allow the application to connect to Fitness APIs.
*/
public static GoogleApiClient buildFitnessClient(GoogleApiClient client, Activity activity) {
Log.d(LOG_TAG, "buildFitnessClient called");
if (client == null && GoogleFitUtils.checkPermissionsAccessFineLocation() && GoogleFitUtils.checkPermissionsBodySensor()) {
client = new GoogleApiClient.Builder(activity)
.addApi(Fitness.RECORDING_API)
.addApi(Fitness.HISTORY_API)
.addApi(Fitness.SENSORS_API)
.addScope(new Scope(Scopes.FITNESS_LOCATION_READ))
.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE))
.addConnectionCallbacks(
new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(@Nullable Bundle bundle) {
Log.d(LOG_TAG, "buildFitnessClient connected");
// Now you can make calls to the Fitness APIs
// the HomeFragment will call the "subscribeDailySteps()"
}
@Override
public void onConnectionSuspended(int i) {
Log.i(LOG_TAG, "buildFitnessClient connection suspended");
if (i == GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST) {
Log.w(LOG_TAG, "Connection lost. Cause: Network Lost.");
} else if (i == GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) {
Log.w(LOG_TAG, "Connection lost. Reason: Service Disconnected");
}
}
}
)
.enableAutoManage((MainActivity) activity, 0, new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e(LOG_TAG, "Google Play services failed. Cause: " + connectionResult.toString());
}
})
.build();
}
return client;
}
private static DataReadRequest getDataDistanceReadRequest(int numberOfDaysPast) {
Calendar cal = new GregorianCalendar();
cal.setTime(new Date());
cal.add(Calendar.DAY_OF_YEAR, -numberOfDaysPast);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.DAY_OF_YEAR, -1);
long startTime = cal.getTimeInMillis();
DateFormat dateFormat = getDateInstance();
Log.i(LOG_TAG, "Range Start: " + dateFormat.format(startTime));
Log.i(LOG_TAG, "Range End: " + dateFormat.format(endTime));
DataSource ESTIMATED_STEP_DELTAS = new DataSource.Builder()
.setDataType(DataType.TYPE_DISTANCE_DELTA)
.setType(DataSource.TYPE_DERIVED)
.setStreamName("merge_distance_delta")
.setAppPackageName("com.google.android.gms")
.build();
return new DataReadRequest.Builder()
// The data request can specify multiple data types to return, effectively
// combining multiple data queries into one call.
// In this example, it's very unlikely that the request is for several hundred
// datapoints each consisting of a few steps and a timestamp. The more likely
// scenario is wanting to see how many steps were walked per day, for 7 days.
.aggregate(ESTIMATED_STEP_DELTAS, DataType.AGGREGATE_DISTANCE_DELTA)
// Analogous to a "Group By" in SQL, defines how data should be aggregated.
// bucketByTime allows for a time span, whereas bucketBySession would allow
// bucketing by "sessions", which would need to be defined in code.
.bucketByTime(1, TimeUnit.DAYS)
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.build();
}
private static void postDataSetDistanceToAPI(DataSet dataSet, int daysIndex) {
Log.i(LOG_TAG, "Data returned for Data type: " + dataSet.getDataType().getName());
if (dataSet.getDataPoints().isEmpty()) {
Log.i(LOG_TAG, "No distance this day:");
Calendar calStart = new GregorianCalendar();
calStart.setTime(new Date());
calStart.add(Calendar.DAY_OF_YEAR, -(daysIndex + 1));
Date calStartTime = calStart.getTime();
String dateStr = DateUtils.formatDateAsString(calStartTime, "yyyy-MM-dd");
logActivitiesInThePastDate("distance", "0", dateStr);
} else {
for (DataPoint dp : dataSet.getDataPoints()) {
Log.i(LOG_TAG, "Data point:");
Log.i(LOG_TAG, "\tType: " + dp.getDataType().getName());
long milliSeconds = dp.getStartTime(TimeUnit.MILLISECONDS);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(milliSeconds);
String dateStr = DateUtils.formatDateAsString(calendar.getTime(), "yyyy-MM-dd");
for (Field field : dp.getDataType().getFields()) { // 4 times
Value value = dp.getValue(field);
Log.i(LOG_TAG, "\tField: " + field.getName() +
" Value: " + value);
// GOOGLE FIT
float distance = Float.valueOf(value.toString());
String distanceStr = String.format("%.2f", distance / 1000);
// Finally call to log-activity
logActivitiesInThePastDate("distance", distanceStr, dateStr);
}
}
}
}
private static void logActivitiesInThePastDate(String activityLabel, String value, String dateStr) {
JsonObject jo = new JsonObject();
jo.addProperty("user_id", LocalSave.getUserId());
jo.addProperty("activity", activityLabel);
jo.addProperty("value", value);
jo.addProperty("date", dateStr);
LogActivityAPI.postData(jo, new LogActivityAPI.ThisCallback() {
@Override
public void onSuccess(LogActivityResponse log) {
if (log.getSuccess()) {
Log.d(LOG_TAG, "Logged Past Activity successfully :)");
// don't update anything. Just logged and happy days :)
} else {
Log.e(LOG_TAG, "Logged Past Activity was NOT successful!!! :(");
}
}
@Override
public void onFailure() {
Log.e(LOG_TAG, "onFailure");
}
@Override
public void onError(APIError error) {
Log.e(LOG_TAG, "Exception: " + error.getErrorMessage());
}
});
}
public static void requestGoogleFitPermissionResult(int requestCode, @NonNull int[] grantResults,
GoogleApiClient client, Activity activity) {
Log.i(LOG_TAG, "onRequestPermissionsResult");
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.length <= 0) {
// If user interaction was interupted, the permission request is cancelled and you
// receive empty arrays.
Log.i(LOG_TAG, "User interaction was cancelled");
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission was granted
buildFitnessClient(client, activity);
} else {
// Permission denied
Toast.makeText(App.getInstance(), "Permission denied for Google FIT", Toast.LENGTH_LONG).show();
}
}
}
private static void postBackgroundHistoryGoogleFit(int days) {
Log.d(LOG_TAG, "The number of days Not Launched saved was: " + days);
for (int i = 0; i <= days; i++) {
if (checkPermissionsAccessFineLocation() && checkPermissionsBodySensor()) {
new DataTaskDistanceBackgroundGoogleFit().execute(i);
}
}
}
public static class DataTaskDistanceBackgroundGoogleFit extends AsyncTask<Integer, Void, Void> {
DataSet dataSet;
int daysIndex;
protected Void doInBackground(Integer... params) {
daysIndex = params[0];
DataReadRequest dataReadRequest = getDataDistanceReadRequest(daysIndex);
// Invoke the History API to fetch the data with the query and await the result of
// the read request.
DataReadResult dataReadResult =
Fitness.HistoryApi.readData(client, dataReadRequest).await(1, TimeUnit.MINUTES);
if (dataReadResult.getStatus().isSuccess()) {
Log.d(LOG_TAG, " result: " + dataReadResult.toString());
} else {
Log.d(LOG_TAG, "result: " + dataReadResult.toString());
}
List<Bucket> bucketList = dataReadResult.getBuckets();
for (Bucket bucket : bucketList) {
dataSet = (bucket.getDataSet(DataType.TYPE_DISTANCE_DELTA));
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
if (dataSet != null) {
postDataSetDistanceToSpectrumAPI(dataSet, daysIndex);
}
}
}
public static class VerifyDataTaskDistance extends AsyncTask<GoogleApiClient, Void, Void> {
float total = 0;
protected Void doInBackground(GoogleApiClient... clients) {
PendingResult<DailyTotalResult> result = Fitness.HistoryApi.readDailyTotal(clients[0], DataType.TYPE_DISTANCE_DELTA);
DailyTotalResult totalResult = result.await(30, TimeUnit.SECONDS);
if (totalResult.getStatus().isSuccess()) {
DataSet totalSet = totalResult.getTotal();
if (totalSet != null) {
total = totalSet.isEmpty()
? 0
: totalSet.getDataPoints().get(0).getValue(Field.FIELD_DISTANCE).asFloat();
}
} else {
Log.e(LOG_TAG, "There was a problem getting the distance count");
}
Log.i(LOG_TAG, "Total distance: " + total);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
// UI
updateDistanceGoogle(total, MainActivity.mainActivity.getSupportFragmentManager());
}
}
private static void readDistanceToday() {
new VerifyDataTaskDistance().execute(client);
}
// ----------- Google Fit Daily DISTANCE -----------
public static void subscribeDailyDistance() {
Log.d(LOG_TAG, "subscribeDailyDistance was called");
if (client != null) {
// To create a subscription, invoke the Recording API.
// As soon as the subscription is active, fitness data will start recording
Fitness.RecordingApi.subscribe(client, DataType.TYPE_DISTANCE_DELTA)
.setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(@NonNull Status status) {
if (status.isSuccess()) {
if (status.getStatusCode() == FitnessStatusCodes.SUCCESS_ALREADY_SUBSCRIBED) {
Log.d(LOG_TAG, "Existing subscription for activity detected.");
} else {
Log.d(LOG_TAG, "Successfully subscribed");
}
// :)
readDistanceToday();
} else {
Log.e(LOG_TAG, "There was a problem subscribing");
}
}
});
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment