Skip to content

Instantly share code, notes, and snippets.

@baluubas
Created January 3, 2016 16:41
Show Gist options
  • Save baluubas/ef74d149a5abd14d0f71 to your computer and use it in GitHub Desktop.
Save baluubas/ef74d149a5abd14d0f71 to your computer and use it in GitHub Desktop.
Uploading a workout to Google Fit
package com.anderspersson.mioji.Services.GoogleFit;
import android.app.IntentService;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.util.Log;
import com.anderspersson.mioji.Infrastructure.Db;
import com.anderspersson.mioji.Services.Persistence.Workouts;
import com.anderspersson.mioji.Utils.CursorHelper;
import com.anderspersson.mioji.Utils.GoogleFitFactory;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.fitness.Fitness;
import com.google.android.gms.fitness.FitnessActivities;
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.Session;
import com.google.android.gms.fitness.request.SessionInsertRequest;
import com.google.android.gms.fitness.request.SessionReadRequest;
import com.google.android.gms.fitness.result.SessionReadResult;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class UploadWorkouts extends IntentService {
private final static String TAG = UploadWorkouts.class.getSimpleName();
public UploadWorkouts() {
super("UploadWorkoutsService");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent == null) {
return;
}
Cursor data = null;
try {
GoogleApiClient client = GoogleFitFactory.createClient(this);
ConnectionResult result = client.blockingConnect(10, TimeUnit.SECONDS);
if(!result.isSuccess()) {
Log.e(TAG, "Failed to connect to google api: Code: " + result.getErrorCode());
return;
}
ContentResolver contentResolver = this.getBaseContext().getContentResolver();
data = contentResolver.query(
Workouts.WORKOUTS_URI,
null,
Db.Workouts.COLUMN_NAME_SYNCED_FIT + " <= 0",
null,
null,
null);
upload(data, client);
}
catch (Exception e) {
Log.e(TAG, "Unable to upload to google fit.", e);
}
finally {
if(data != null) {
data.close();
}
}
}
private void upload(Cursor data, GoogleApiClient client) {
while(data.moveToNext()) {
int startTime = CursorHelper.getInt(data, Db.Workouts.COLUMN_NAME_STARTTIME);
int duration = CursorHelper.getInt(data, Db.Workouts.COLUMN_NAME_DURATION);
int stopTime = startTime + duration;
int calorieCount = CursorHelper.getInt(data, Db.Workouts.COLUMN_NAME_CALORIE);
byte[] heartRates = CursorHelper.getBlob(data, Db.Workouts.COLUMN_NAME_HR);
int durationMinutes = (int)Math.floor(duration / 60d);
Session session = new Session.Builder()
.setName(durationMinutes + " min workout")
.setDescription("Workout")
.setIdentifier("mioji-workout-" + CursorHelper.getInt(data, Db.Workouts.COLUMN_NAME_STARTTIME))
.setActivity(FitnessActivities.OTHER)
.setStartTime(startTime, TimeUnit.SECONDS)
.setEndTime(stopTime, TimeUnit.SECONDS)
.build();
DataSet calorieDataset = getCalorieDataSet(startTime, stopTime, calorieCount);
DataSet hrDataset = getHeartRateDataSet(startTime, stopTime, heartRates);
SessionInsertRequest insertRequest = new SessionInsertRequest.Builder()
.setSession(session)
.addDataSet(calorieDataset)
.addDataSet(hrDataset)
.build();
Log.i(TAG, "Inserting the session in the History API");
com.google.android.gms.common.api.Status insertStatus =
Fitness.SessionsApi.insertSession(client, insertRequest)
.await(15, TimeUnit.SECONDS);
if (!insertStatus.isSuccess()) {
Log.i(TAG, "There was a problem inserting the session: " +
insertStatus.getStatusMessage());
setWorkoutAsFailed(CursorHelper.getInt(data, Db.Workouts.COLUMN_NAME_ID));
continue;
}
setWorkoutAsSynced(CursorHelper.getInt(data, Db.Workouts.COLUMN_NAME_ID));
Log.i(TAG, "Session insert was successful!");
}
}
private DataSet getHeartRateDataSet(int startTime, int stopTime, byte[] heartRates) {
DataSource hrDataSource = new DataSource.Builder()
.setAppPackageName(this.getPackageName())
.setDataType(DataType.TYPE_HEART_RATE_BPM)
.setType(DataSource.TYPE_RAW)
.build();
DataSet hrDataset = DataSet.create(hrDataSource);
for(int i = startTime, j = 0; i < stopTime && j < heartRates.length; i++, j++) {
DataPoint dataPoint = hrDataset.createDataPoint().setTimeInterval(i, i+1, TimeUnit.SECONDS);
int hr = 0xff & heartRates[j];
dataPoint.getValue(Field.FIELD_BPM).setFloat((float)hr);
hrDataset.add(dataPoint);
}
return hrDataset;
}
private DataSet getCalorieDataSet(int startTime, int stopTime, int calorieCount) {
DataSource caloriesDataSource = new DataSource.Builder()
.setAppPackageName(this.getPackageName())
.setDataType(DataType.TYPE_CALORIES_EXPENDED)
.setType(DataSource.TYPE_RAW)
.build();
DataSet calorieDataset = DataSet.create(caloriesDataSource);
DataPoint dataPoint = calorieDataset.createDataPoint().setTimeInterval(startTime, stopTime, TimeUnit.SECONDS);
dataPoint.getValue(Field.FIELD_CALORIES).setFloat(calorieCount);
calorieDataset.add(dataPoint);
return calorieDataset;
}
private void setWorkoutAsFailed(int id) {
setSyncState(id, -1);
}
private void setWorkoutAsSynced(int id) {
setSyncState(id, 1);
}
private void setSyncState(int id, int state) {
ContentResolver contentResolver = this.getBaseContext().getContentResolver();
Uri workoutUri = ContentUris.withAppendedId(Workouts.WORKOUTS_URI, id);
ContentValues values = new ContentValues();
values.put(Db.Workouts.COLUMN_NAME_SYNCED_FIT, state);
contentResolver.update(workoutUri, values, null, null);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment