This repository has been archived on 2025-04-11. You can view files and clone it, but cannot push or open issues or pull requests.
mochapine64backup/MoCha/Assets/Scripts/FitBitAPI.cs
Patrick Kitchens 9e847cb658 ok
2018-04-26 10:59:14 -05:00

516 lines
17 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using Newtonsoft.Json;
using UnityEngine;
namespace Assets.Scripts.Fitbit
{
public class FitBitAPI : MonoBehaviour
{
private const string _consumerSecret = "69307b9f332caf9946ef4e23cabde2e4";
private const string _clientId = "22CX4L";
private const string _callbackURL = "http://localhost/callback";
private const string CustomAndroidScheme = "mocha://";
private const string _tokenUrl = "https://api.fitbit.com/oauth2/token";
private const string _baseGetUrl = "https://api.fitbit.com/1/user/-/";
private const string _profileUrl = _baseGetUrl + "profile.json/";
private const string _activityUrl = _baseGetUrl + "activities/";
private string _distanceUrl = _activityUrl + "distance/date/" + _currentDateTime + "/1d.json";
private string _stepsUrl = _activityUrl + "steps/date/" + _currentDateTime + "/1d.json";
private string _caloriesUrl = _activityUrl + "calories/date/" + _currentDateTime + "/1d.json";
private string _sleepUrl = _baseGetUrl + "sleep/minutesAsleep/date/" + _currentDateTime + "/" + _currentDateTime + ".json";
private static string _currentDateTime = GetCurrentDate();
private string _returnCode;
private WWW _wwwRequest;
private bool _bGotTheData = false;
private bool _bFirstFire = true;
private OAuth2AccessToken _oAuth2 = new OAuth2AccessToken();
public FitbitData _fitbitData = new FitbitData();
private string _statusMessage;
private string CallBackUrl
{
get
{
//determine which platform we're running on and use the appropriate url
if (Application.platform == RuntimePlatform.WindowsEditor)
return WWW.EscapeURL(_callbackURL);
else if (Application.platform == RuntimePlatform.Android)
{
return WWW.EscapeURL(CustomAndroidScheme);
}
else
{
return WWW.EscapeURL(CustomAndroidScheme);
}
}
}
public void Start()
{
DontDestroyOnLoad(this);
}
private void OnGUI()
{
if (!_bGotTheData && !string.IsNullOrEmpty(_statusMessage) && _bFirstFire)
{
_bFirstFire = false;
}
}
public void LoginToFitbit()
{
if (PlayerPrefs.HasKey("FitbitRefreshToken"))
{
UseRefreshToken();
}
else
{
UserAcceptOrDeny();
}
}
public void UserAcceptOrDeny()
{
var url =
"https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=" + _clientId + "&prompt=login&redirect_uri=" +
CallBackUrl +
"&scope=activity%20nutrition%20heartrate%20location%20profile%20sleep%20weight%20social";
Application.OpenURL(url);
// print(url);
#if UNITY_EDITOR
#endif
}
public void ClearRefreshCode()
{
PlayerPrefs.DeleteKey("FitbitRefreshToken");
Debug.Log("Refresh Token has been CLEARED!");
}
private void UseReturnCode()
{
Debug.Log("return code isn't empty");
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(_clientId + ":" + _consumerSecret);
var encoded = Convert.ToBase64String(plainTextBytes);
var form = new WWWForm();
form.AddField("client_id", _clientId);
form.AddField("grant_type", "authorization_code");
form.AddField("redirect_uri", WWW.UnEscapeURL(CallBackUrl));
form.AddField("code", _returnCode);
var headers = form.headers;
headers["Authorization"] = "Basic " + encoded;
_wwwRequest = new WWW(_tokenUrl, form.data, headers);
StartCoroutine(WaitForAccess(_wwwRequest));
while (!_wwwRequest.isDone)
{
}
Debug.Log("Token: " + _wwwRequest.text);
Debug.Log("parsing token");
var parsed = new JSONObject(_wwwRequest.text);
ParseAccessToken(parsed);
Debug.Log("\nParsed Token: " + _oAuth2.Token);
GetAllData();
Debug.Log("Steps from Fitbit: " + _fitbitData.CurrentSteps);
_bGotTheData = true;
}
public void UseRefreshToken()
{
Debug.Log("Using Refresh Token");
var plainTextBytes = Encoding.UTF8.GetBytes(_clientId + ":" + _consumerSecret);
var encoded = Convert.ToBase64String(plainTextBytes);
var form = new WWWForm();
form.AddField("grant_type", "refresh_token");
form.AddField("refresh_token", PlayerPrefs.GetString("FitbitRefreshToken"));
var headers = form.headers;
headers["Authorization"] = "Basic " + encoded;
_wwwRequest = new WWW(_tokenUrl, form.data, headers);
StartCoroutine(WaitForAccess(_wwwRequest));
while (!_wwwRequest.isDone)
{
}
Debug.Log("RefreshToken wwwText: " + _wwwRequest.text);
if (!String.IsNullOrEmpty(_wwwRequest.error))
{
PlayerPrefs.DeleteKey("FitbitRefreshToken");
UserAcceptOrDeny();
UseReturnCode();
GetAllData();
}
else
{
Debug.Log("Using the Auth Token (UseRefreshToken)");
var parsed = new JSONObject(_wwwRequest.text);
ParseAccessToken(parsed);
GetAllData();
}
}
public void SetReturnCodeFromAndroid(string code)
{
if (string.IsNullOrEmpty(code))
return;
_returnCode = code.Substring(CustomAndroidScheme.Length + 6);
Debug.Log("Return Code is: " + _returnCode);
UseReturnCode();
}
public void SetReturnCode(string code)
{
if (string.IsNullOrEmpty(code))
return;
_returnCode = code;
UseReturnCode();
}
public void GetAllData()
{
GetProfileData();
GetAllRelevantData();
BuildProfile();
_fitbitData.LastSyncTime = DateTime.Now.ToUniversalTime();
Debug.Log("LastSyncTime: " + DateTime.Now.ToUniversalTime().ToString("g"));
}
private void GetAllRelevantData()
{
GetSteps();
GetDistance();
GetCalories();
GetSleep();
}
#region GetData
private void GetProfileData()
{
//time for Getting Dataz
var headers = new Dictionary<string, string>();
headers["Authorization"] = "Bearer " + _oAuth2.Token;
_wwwRequest = new WWW(_profileUrl, null, headers);
Debug.Log("Doing GET Request");
StartCoroutine(WaitForAccess(_wwwRequest));
while (!_wwwRequest.isDone)
{
}
ParseProfileData(_wwwRequest.text);
}
private void GetCalories()
{
var headers = new Dictionary<string, string>();
headers["Authorization"] = "Bearer " + _oAuth2.Token;
Debug.Log("Calories URL is: " + _caloriesUrl);
_wwwRequest = new WWW(_caloriesUrl, null, headers);
Debug.Log("Doing Calories GET Request");
StartCoroutine(WaitForAccess(_wwwRequest));
while (!_wwwRequest.isDone)
{
}
ParseCaloriesData(_wwwRequest.text);
}
private void GetDistance()
{
var headers = new Dictionary<string, string>();
headers["Authorization"] = "Bearer " + _oAuth2.Token;
Debug.Log("Distance URL is: " + _distanceUrl);
_wwwRequest = new WWW(_distanceUrl, null, headers);
Debug.Log("Doing Distance GET Request");
StartCoroutine(WaitForAccess(_wwwRequest));
while (!_wwwRequest.isDone)
{
}
ParseDistanceData(_wwwRequest.text);
}
private void GetSteps()
{
var headers = new Dictionary<string, string>();
headers["Authorization"] = "Bearer " + _oAuth2.Token;
Debug.Log("Steps URL is: " + _stepsUrl);
_wwwRequest = new WWW(_stepsUrl, null, headers);
Debug.Log("Doing Steps GET Request");
StartCoroutine(WaitForAccess(_wwwRequest));
while (!_wwwRequest.isDone)
{
}
ParseStepsData(_wwwRequest.text);
}
private void GetSleep()
{
var headers = new Dictionary<string, string>();
headers["Authorization"] = "Bearer " + _oAuth2.Token;
Debug.Log("Sleep URL is: " + _sleepUrl);
_wwwRequest = new WWW(_sleepUrl, null, headers);
Debug.Log("Doing Sleep GET Request");
StartCoroutine(WaitForAccess(_wwwRequest));
//DIRTY DIRTY HACK
while (!_wwwRequest.isDone)
{
}
ParseSleepData(_wwwRequest.text);
}
private void BuildProfile()
{
var imageWWW = new WWW(_fitbitData.ProfileData["avatar"]);
//DIRTY DIRTY HACK
while (!imageWWW.isDone)
{
}
Debug.Log(_fitbitData.RawProfileData["fullName"]);
//we should check to see if there is "data" already
if (_fitbitData.ProfileData.Count != 0)
{
foreach (KeyValuePair<string, string> kvp in _fitbitData.ProfileData)
{
if (kvp.Key == "avatar")
continue;
//put a space between the camelCase
var tempKey = Regex.Replace(kvp.Key, "(\\B[A-Z])", " $1");
//then capitalize the first letter
UppercaseFirst(tempKey);
}
}
_bGotTheData = true;
}
#endregion
#region Parsing
private void ParseAccessToken(JSONObject parsed)
{
var dict = parsed.ToDictionary();
foreach (KeyValuePair<string, string> kvp in dict)
{
if (kvp.Key == "access_token")
{
_oAuth2.Token = kvp.Value;
PlayerPrefs.SetString("FitbitAccessToken", kvp.Value);
}
else if (kvp.Key == "expires_in")
{
var num = 0;
Int32.TryParse(kvp.Value, out num);
_oAuth2.ExpiresIn = num;
}
else if (kvp.Key == "refresh_token")
{
_oAuth2.RefreshToken = kvp.Value;
Debug.Log("REFRESH TOKEN: " + kvp.Value);
PlayerPrefs.SetString("FitbitRefreshToken", kvp.Value);
Debug.Log("Token We Just Store: " + PlayerPrefs.GetString("FitbitRefreshToken"));
}
else if (kvp.Key == "token_type")
{
_oAuth2.TokenType = kvp.Value;
PlayerPrefs.SetString("FitbitTokenType", kvp.Value);
}
}
}
private void ParseProfileData(string data)
{
Debug.Log("inserting json data into fitbitData.RawProfileData");
//Debug.LogWarning(data);
XmlDocument xmldoc = JsonConvert.DeserializeXmlNode(data);
var doc = XDocument.Parse(xmldoc.InnerXml);
doc.Descendants("topBadges").Remove();
foreach (XElement xElement in doc.Descendants())
{
//Debug.Log(xElement.Name.LocalName + ": Value:" + xElement.Value);V
if (!_fitbitData.RawProfileData.ContainsKey(xElement.Name.LocalName))
_fitbitData.RawProfileData.Add(xElement.Name.LocalName, xElement.Value);
else
{
//Debug.LogWarning("Key already found in RawProfileData: " + xElement.Name.LocalName);
//if the key is already in the dict, we will just update the value for consistency.
_fitbitData.RawProfileData[xElement.Name.LocalName] = xElement.Value;
}
if (_fitbitData.ProfileData.ContainsKey(xElement.Name.LocalName))
{
_fitbitData.ProfileData[xElement.Name.LocalName] = xElement.Value;
}
}
}
private void ParseStepsData(string data)
{
//convert the json to xml cause json blows hard.
XmlDocument json = JsonConvert.DeserializeXmlNode(data);
XDocument doc = XDocument.Parse(json.InnerXml);
var root = doc.Descendants("value").FirstOrDefault();
_fitbitData.CurrentSteps = ToInt(root.Value);
Debug.Log("Steps from Fitbit: " + _fitbitData.CurrentSteps);
}
private void ParseDistanceData(string data)
{
XmlDocument json = JsonConvert.DeserializeXmlNode(data);
XDocument doc = XDocument.Parse(json.InnerXml);
var root = doc.Descendants("value").FirstOrDefault().Value;
//trim the value
if (root.Length > 4)
root = root.Substring(0, 4);
_fitbitData.CurrentDistance = ToDouble(root);
Debug.Log("Distance from Fitbit is:" + _fitbitData.CurrentDistance);
}
private void ParseCaloriesData(string data)
{
XmlDocument json = JsonConvert.DeserializeXmlNode(data);
var doc = XDocument.Parse(json.InnerXml);
var calories = doc.Descendants("value").FirstOrDefault().Value;
_fitbitData.CurrentCalories = ToInt(calories);
}
private void ParseSleepData(string data)
{
Debug.Log(data);
XmlDocument json = JsonConvert.DeserializeXmlNode(data);
var doc = XDocument.Parse(json.InnerXml);
var sleepTimeTotal = doc.Descendants("value").FirstOrDefault().Value;
Debug.Log("Minutes asleep for: " + sleepTimeTotal);
_fitbitData.CurrentSleep = ToInt(sleepTimeTotal);
}
#endregion
static string UppercaseFirst(string s)
{
// Check for empty string.
if (string.IsNullOrEmpty(s))
{
return string.Empty;
}
// Return char and concat substring.
return char.ToUpper(s[0]) + s.Substring(1);
}
IEnumerator WaitForAccess(WWW www)
{
Debug.Log("waiting for access\n");
yield return www;
Debug.Log("Past the Yield \n");
// check for errors
if (www.error == null)
{
Debug.Log("no error \n");
Debug.Log("Steps from Fitbit: " + _fitbitData.CurrentSteps);
Debug.Log(www.text);
Debug.Log("Steps from Fitbit: " + _fitbitData.CurrentSteps);
}
if (www.error != null)
{
Debug.Log("\n Error" + www.error);
Debug.Log(www.error);
}
Debug.Log("end of WaitForAccess \n");
}
//just a utility function to get the correct date format for activity calls that require one
public static string GetCurrentDate()
{
var date = "";
date += DateTime.Now.Year;
if (DateTime.Now.Month < 10)
{
date += "-" + "0" + DateTime.Now.Month;
}
else
{
date += "-" + DateTime.Now.Month;
}
if (DateTime.Now.Day < 10)
{
date += "-" + "0" + DateTime.Now.Day;
}
else
{
date += "-" + DateTime.Now.Day;
}
//date += "-" + 15;
return date;
}
private int ToInt(string thing)
{
var temp = 0;
Int32.TryParse(thing, out temp);
return temp;
}
private double ToDouble(string thing)
{
var temp = 0.0;
Double.TryParse(thing, out temp);
return temp;
}
}
}