-
-
Save ingyesid/3d12ceaf24ab89e4df2c to your computer and use it in GitHub Desktop.
Uploading a workout to Google Fit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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