diff --git a/.gitignore b/.gitignore index 2ae46c9..020929a 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ hs_err_pid* .idea/ .idea/*.xml .gradle/ +.iml # eclipse things # .classpath diff --git a/SeniorAssistant.iml b/SeniorAssistant.iml index 306ea4c..405022e 100644 --- a/SeniorAssistant.iml +++ b/SeniorAssistant.iml @@ -1,13 +1,19 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index b6edeb2..a87468b 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,6 @@ repositories { dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' // compile "com.sparkjava:spark-core:2.5.5" -// compile "org.slf4j:slf4j-simple:1.7.21" compile "com.google.code.gson:gson:2.8.0" // compile "org.xerial:sqlite-jdbc:3.15.1" compile 'org.apache.httpcomponents:httpclient:4.5.3' @@ -23,9 +22,8 @@ dependencies { // z-way-lib and all its dependencies (from https://github.com/pathec/ZWay-library-for-Java) - compile files('lib/zway-lib-0.2.9-SNAPSHOT.jar') -// compile 'com.google.code.gson:gson:2.4' //already up there + compile 'org.apache.commons:commons-lang3:3.4' compile 'org.eclipse.jetty:jetty-client:9.3.11.v20160721' compile 'org.eclipse.jetty:jetty-http:9.3.11.v20160721' @@ -35,4 +33,7 @@ dependencies { compile 'org.eclipse.jetty.websocket:websocket-client:9.3.12.v20160915' compile 'org.eclipse.jetty.websocket:websocket-common:9.3.12.v20160915' compile 'org.slf4j:slf4j-simple:1.7.21' + + //for objectMapper + compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.5' } diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 0000000..bc2c900 --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,13 @@ +import manage.FITBITData.FitBit; + +public class Main { + public static void main(String[] args) throws Exception { + FitBit fitBit = new FitBit(); + fitBit.getHoursSleep(); + + System.out.println("Today's average heart-rate: "+fitBit.getHeartRate()); + System.out.println("Today's hours of sleep: "+fitBit.getHoursSleep()); + System.out.println("Today's steps: "+fitBit.getSteps()); + System.out.println("Fine."); + } +} diff --git a/src/main/java/manage/AuthFITBIT.java b/src/main/java/manage/AuthFITBIT.java index ccc7ae0..bf6e266 100644 --- a/src/main/java/manage/AuthFITBIT.java +++ b/src/main/java/manage/AuthFITBIT.java @@ -1,25 +1,44 @@ package manage; -import com.google.api.client.auth.oauth2.*; +import java.io.IOException; +import java.util.Arrays; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.api.client.auth.oauth2.AuthorizationCodeFlow; +import com.google.api.client.auth.oauth2.BearerToken; +import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; +import com.google.api.client.http.BasicAuthentication; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpRequestFactory; -import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonObjectParser; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.store.DataStoreFactory; import com.google.api.client.util.store.FileDataStoreFactory; -import java.io.IOException; -import java.util.Arrays; public class AuthFITBIT { + private final HttpRequestFactory requestFactory; + + public AuthFITBIT() throws Exception { + DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR); + final Credential credential = authorize(); + + this.requestFactory = HTTP_TRANSPORT.createRequestFactory( request -> { + credential.initialize(request); + request.setParser(new JsonObjectParser(JSON_FACTORY)); + }); + } + + private static ObjectMapper mapper = new ObjectMapper(); /** Directory to store user credentials. */ /* Throw a Warning when change permission: they said it's a google bug 'cause is meant to run in linux/unix * @@ -36,31 +55,25 @@ public class AuthFITBIT { private static FileDataStoreFactory DATA_STORE_FACTORY; /** OAuth 2 scope. */ - private static final String SCOPE[] = new String[]{"activity","heartrate","location","sleep"}; + private static final String SCOPE[] = new String[]{"activity","heartrate","sleep","settings"}; /** Global instance of the HTTP transport. */ private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); /** Global instance of the JSON factory. */ private static final JsonFactory JSON_FACTORY = new JacksonFactory(); - /* When i try to accept the request it'll send a 401 Unauthorized - * on internet i found that this message appears when the client put a wrong - * header, client_id or client_secret - * https://dev.fitbit.com/build/reference/web-api/oauth2/ -> ALT+F "401 Unauthorized" - */ private static final String TOKEN_SERVER_URL = " https://api.fitbit.com/oauth2/token"; private static final String AUTHORIZATION_SERVER_URL = "https://www.fitbit.com/oauth2/authorize"; /** Authorizes the installed application to access user's protected data. */ - private static Credential authorize() throws Exception { - OAuth2ClientCredentials.errorIfNotSpecified(); + private Credential authorize() throws Exception { // set up authorization code flow AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken .authorizationHeaderAccessMethod(), HTTP_TRANSPORT, JSON_FACTORY, new GenericUrl(TOKEN_SERVER_URL), - new ClientParametersAuthentication( + new BasicAuthentication ( OAuth2ClientCredentials.API_KEY, OAuth2ClientCredentials.API_SECRET), OAuth2ClientCredentials.API_KEY, AUTHORIZATION_SERVER_URL).setScopes(Arrays.asList(SCOPE)) @@ -69,55 +82,39 @@ public class AuthFITBIT { LocalServerReceiver receiver = new LocalServerReceiver.Builder().setHost( OAuth2ClientCredentials.DOMAIN).setPort(OAuth2ClientCredentials.PORT).build(); - return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user" ); + return new AuthorizationCodeInstalledApp(flow, receiver).authorize( "user" ); } - private static void run(HttpRequestFactory requestFactory) throws IOException { - FITBITUrl url = new FITBITUrl("https://api.fitbit.com/1/user/-/profile.json"); //modificare con token? - url.setFields("activity,heartrate,location,sleep"); - - HttpRequest request = requestFactory.buildGetRequest(url); - UserData data = request.execute().parseAs(UserData.class); - if (data.list.isEmpty()) { - System.out.println("Error in retrieve user data"); - } else/* { - if (data.hasMore) { - System.out.print("First "); - }*/ //i don't think is necessary - /* System.out.println(data.list.size() + " favorite videos found:"); - */for (FITIBITData datas: data.list) { - System.out.println(datas.toString());/* - System.out.println("-----------------------------------------------"); - System.out.println("ID: " + datas.id); - System.out.println("Title: " + datas.title); - System.out.println("Tags: " + datas.tags); - System.out.println("URL: " + datas.url); - */ }/* - }*/ //neither this + public O run(String url, Class returnClass) throws IOException { + return run(url, returnClass, false); } - public static void main(String[] args) { - try { - DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR); - final Credential credential = authorize(); - HttpRequestFactory requestFactory = - HTTP_TRANSPORT.createRequestFactory(new HttpRequestInitializer() { - @Override - public void initialize(HttpRequest request) throws IOException { - credential.initialize(request); - request.setParser(new JsonObjectParser(JSON_FACTORY)); - } - }); - run(requestFactory); - System.err.print("DONE"); - // Success! - return; - } catch (IOException e) { - System.err.println(e.getClass().getSimpleName()+" "+e.getMessage()); - } catch (Throwable t) { - t.printStackTrace(); - } - System.exit(1); - } + public O run(String url, Class returnClass, boolean useAsParse) throws IOException { + FITBITUrl fitbitUrl = new FITBITUrl(url); + fitbitUrl.setFields(""); + HttpRequest request = requestFactory.buildGetRequest(fitbitUrl); + HttpResponse response = request.execute(); + + /* + GenericJson json = response.parseAs(GenericJson.class); + response.disconnect(); + + System.out.println(returnClass.getSimpleName()); + System.out.println(url); + System.out.println(json.toPrettyString()); + + return mapper.readValue(json.toString(), returnClass); + */ + + /**/ + O ret = ( useAsParse ? + response.parseAs(returnClass) : + mapper.readValue(response.parseAs(GenericJson.class).toString(), returnClass) + ); + + response.disconnect(); + return ret; + /**/ + } } diff --git a/src/main/java/manage/FITBITData/Device.java b/src/main/java/manage/FITBITData/Device.java new file mode 100644 index 0000000..cdfe9ea --- /dev/null +++ b/src/main/java/manage/FITBITData/Device.java @@ -0,0 +1,24 @@ +package manage.FITBITData; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.util.List; +import java.util.Map; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class Device { + + public String lastSyncTime; + + // @JsonProperty ("lastSyncTime") + public String getLastSyncTime(List> sync){ + lastSyncTime = null; + for(Map d: sync) { + String temp = d.get("lastSyncTime"); + if ((lastSyncTime == null) || (lastSyncTime.compareTo(temp) < 0)) + lastSyncTime = temp; + } + return lastSyncTime; + } + +} diff --git a/src/main/java/manage/FITBITData/FitBit.java b/src/main/java/manage/FITBITData/FitBit.java new file mode 100644 index 0000000..86666ee --- /dev/null +++ b/src/main/java/manage/FITBITData/FitBit.java @@ -0,0 +1,94 @@ +package manage.FITBITData; + +import java.io.IOException; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +import manage.AuthFITBIT; + +public class FitBit { + + public static final String BASIC_URL = "https://api.fitbit.com/"; + public static final String USER = "/user/-/"; + private static final long MINUTE = 60000; /* 5 minutes in millisec */ + + private final AuthFITBIT auth; + private final Map, Long> latestRequest = new HashMap<>(); + private final Calendar calendar = Calendar.getInstance(); + + private HeartRate heart = null; + private Sleep sleep = null; + private Steps steps = null; + + public FitBit() throws Exception { + this(new AuthFITBIT()); + } + + public FitBit(AuthFITBIT auth) { + if(auth == null) + throw new NullPointerException("I must have an Auth for the FitBit"); + this.auth = auth; + } + + /* passi */ + public int getSteps() throws IOException { + if(shouldUpdateFor(Steps.class)) { + long currentMillisec = System.currentTimeMillis(); + + steps = auth.run(BASIC_URL + "1" + USER + "activities/steps/date/today/1w.json", Steps.class); + latestRequest.put(steps.getClass(), currentMillisec); + } + return steps.getSteps(); + } + + /* battito */ + public double getHeartRate() throws IOException { + if(shouldUpdateFor(HeartRate.class)) { + long currentMillisec = System.currentTimeMillis(); + + String now = getHourMinutes(currentMillisec); + String ago = getHourMinutes(currentMillisec-(MINUTE*15)); + + if(now.compareTo(ago) < 0) + ago = "00:00"; + + heart = auth.run(BASIC_URL + "1" + USER + "activities/heart/date/today/1d/1sec/time/"+ago+"/"+now+".json", HeartRate.class); + latestRequest.put(heart.getClass(), currentMillisec); + } + return heart.getAverage(); + } + + /* sonno */ + public Object getHoursSleep() throws IOException { + if(shouldUpdateFor(Sleep.class)) { + long currentMillisec = System.currentTimeMillis(); + + sleep = auth.run(BASIC_URL + "1.2" + USER + "sleep/date/today.json", Sleep.class); + latestRequest.put(sleep.getClass(), currentMillisec); + } + return sleep.getMinutesAsleep(); + } + + private boolean shouldUpdateFor(Class type) { + try { + long current = System.currentTimeMillis(); + long latest = latestRequest.get(type); + + if (current - latest > MINUTE * 5) + return true; + return false; + } catch (NullPointerException e) {} + return true; + } + + private String getHourMinutes(long milliseconds) { + calendar.setTimeInMillis(milliseconds); + + int hour = calendar.get(Calendar.HOUR_OF_DAY); + int minu = calendar.get(Calendar.MINUTE); + return String.format("%02d:%02d", hour, minu); + } + + // Device dev = auth.run(BASIC_URL + "devices.json", Device.class); +} diff --git a/src/main/java/manage/FITBITData/HeartRate.java b/src/main/java/manage/FITBITData/HeartRate.java new file mode 100644 index 0000000..584544a --- /dev/null +++ b/src/main/java/manage/FITBITData/HeartRate.java @@ -0,0 +1,36 @@ +package manage.FITBITData; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.util.List; +import java.util.Map; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class HeartRate { + + private String dateTime; + private double average; + + public double getAverage() { + return average; + } + + @JsonProperty("activities-heart") + public void quelloCheVoglio(Map[] activities){ + dateTime = (String) activities[0].get("dateTime"); + } + + @JsonProperty("activities-heart-intraday") + public void setAverage(Map map) { + List data = (List) map.get("dataset"); + + int sum = 0; + for(Map dat: data) + sum += (int)dat.get("value"); + average = ((double)sum)/data.size(); + + if(data.size() == 0) + average = 0; + } +} diff --git a/src/main/java/manage/FITBITData/Sleep.java b/src/main/java/manage/FITBITData/Sleep.java new file mode 100644 index 0000000..8e4eecc --- /dev/null +++ b/src/main/java/manage/FITBITData/Sleep.java @@ -0,0 +1,21 @@ +package manage.FITBITData; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Map; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class Sleep { + + private int minutesAsleep; + + public int getMinutesAsleep() { + return minutesAsleep; + } + + @JsonProperty("summary") + public void setMinutesAsleep(Map map) { + minutesAsleep = (int) map.get("totalMinutesAsleep"); + } +} diff --git a/src/main/java/manage/FITBITData/Steps.java b/src/main/java/manage/FITBITData/Steps.java new file mode 100644 index 0000000..7d92a82 --- /dev/null +++ b/src/main/java/manage/FITBITData/Steps.java @@ -0,0 +1,30 @@ +package manage.FITBITData; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class Steps { + + private int steps=0; + + @JsonProperty("activities-steps") + public void setSteps(Map[] array) { + SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd"); + Date now = new Date(); + String strDate = sdfDate.format(now); + + for(Map map : array) + if(map.get("dateTime").equals(strDate)) + steps = Integer.parseInt(map.get("value"))+1; + } + + public int getSteps() { + return steps; + } + +} diff --git a/src/main/java/manage/FITIBITData.java b/src/main/java/manage/FITIBITData.java deleted file mode 100644 index 374cbf7..0000000 --- a/src/main/java/manage/FITIBITData.java +++ /dev/null @@ -1,21 +0,0 @@ -package manage; - -import com.google.api.client.util.Key; - -import java.util.List; - - -public class FITIBITData { - - @Key - public String activity; - - @Key - public String heartrate; - - @Key - public String sleep; - - @Key - public String location; -} \ No newline at end of file diff --git a/src/main/java/manage/OAuth2ClientCredentials.java b/src/main/java/manage/OAuth2ClientCredentials.java index 0a77a40..ccbf967 100644 --- a/src/main/java/manage/OAuth2ClientCredentials.java +++ b/src/main/java/manage/OAuth2ClientCredentials.java @@ -13,14 +13,5 @@ public class OAuth2ClientCredentials { /** Domain name in the "Callback URL". */ public static final String DOMAIN = "127.0.0.1"; - - public static void errorIfNotSpecified() { - if (API_KEY.startsWith("Enter ") || API_SECRET.startsWith("Enter ")) { - System.out.println( - "Enter API Key and API Secret from http://www.dailymotion.com/profile/developer" - + " into API_KEY and API_SECRET in " + OAuth2ClientCredentials.class); - System.exit(1); - } - } } diff --git a/src/main/java/manage/UserData.java b/src/main/java/manage/UserData.java deleted file mode 100644 index 68bc9cc..0000000 --- a/src/main/java/manage/UserData.java +++ /dev/null @@ -1,17 +0,0 @@ -package manage; - - -import com.google.api.client.util.Key; -import java.util.List; - -public class UserData { - - @Key - public List list; -/* - @Key - public int limit; - - @Key("has more") - public boolean hasMore;*/ //don't think are needed -} \ No newline at end of file