Java tutorial
/* * Copyright 2012-2013 Coronastreet Networks * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License */ package org.coronastreet.gpxconverter; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JTextArea; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.CookieStore; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.InputStreamBody; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.apache.xml.serialize.OutputFormat; import org.apache.xml.serialize.XMLSerializer; import org.json.JSONArray; import org.json.JSONObject; import org.json.JSONException; import org.jsoup.Jsoup; import org.jsoup.nodes.Node; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; @SuppressWarnings("deprecation") public class GarminForm { private CloseableHttpClient httpClient; private HttpContext localContext; private CookieStore cookieStore; private String email; private String password; private String tripName; private String activityType; private Document outDoc; private String rideStartTime; private String deviceType; private String totalTimeInSeconds; private String distanceMeters; private String maximumSpeed; private boolean hasAltimeter = false; private String outFile = "C:\\Temp\\temp.tcx"; private JTextArea statusTextArea; private List<Trkpt> trackPoints; public GarminForm() { } @SuppressWarnings("unused") private String convertDoc() { OutputFormat format = new OutputFormat(outDoc); format.setIndenting(true); StringWriter stringOut = new StringWriter(); XMLSerializer serializer = new XMLSerializer(stringOut, format); try { serializer.serialize(outDoc); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return stringOut.toString(); } public boolean processData() { boolean success = false; //load the output template loadTCXTemplate(); setIdAndStartTime(); setDistanceAndTime(); setDeviceType(); // Add the track data we imported to the output document if (addTrackData()) { success = true; } // Spit out the TCX file printOutFile(); success = true; return success; } private void printOutFile() { try { OutputFormat format = new OutputFormat(outDoc); format.setIndenting(true); XMLSerializer serializer = new XMLSerializer(new FileOutputStream(new File(outFile)), format); log("Writing out TCX file."); serializer.serialize(outDoc); } catch (IOException ie) { ie.printStackTrace(); } } private void setIdAndStartTime() { NodeList nl = outDoc.getElementsByTagName("Activity"); NodeList nl2 = ((Element) nl.item(0)).getElementsByTagName("Id"); if (nl2 != null && nl2.getLength() > 0) { Element el = (Element) nl2.item(0); el.appendChild(outDoc.createTextNode(rideStartTime)); } NodeList nl3 = ((Element) nl.item(0)).getElementsByTagName("Lap"); if (nl3 != null && nl3.getLength() > 0) { Element el = (Element) nl3.item(0); el.setAttribute("StartTime", rideStartTime); } } private void setDistanceAndTime() { NodeList nl = outDoc.getElementsByTagName("Activity"); NodeList nl1 = ((Element) nl.item(0)).getElementsByTagName("Lap"); NodeList nl2 = ((Element) nl1.item(0)).getElementsByTagName("TotalTimeSeconds"); if (nl2 != null && nl2.getLength() > 0) { Element el = (Element) nl2.item(0); el.getFirstChild().setNodeValue(totalTimeInSeconds); } NodeList nl3 = ((Element) nl.item(0)).getElementsByTagName("DistanceMeters"); if (nl3 != null && nl3.getLength() > 0) { Element el = (Element) nl3.item(0); el.getFirstChild().setNodeValue(distanceMeters); } } private Element createTrackPointElement(Trkpt tp) { Element eTrackpoint = outDoc.createElement("Trackpoint"); //create time element and time text node and attach it to the trackpoint Element eTime = outDoc.createElement("Time"); eTime.appendChild(outDoc.createTextNode(tp.getTime())); eTrackpoint.appendChild(eTime); //create elevation element and elevation text node and attach it to the trackpoint Element eElevation = outDoc.createElement("AltitudeMeters"); eElevation.appendChild(outDoc.createTextNode(tp.getElevation())); eTrackpoint.appendChild(eElevation); //create Speed Sensor element and attach it to the trackpoint Element eSensorState = outDoc.createElement("SensorState"); eSensorState.appendChild(outDoc.createTextNode("Absent")); eTrackpoint.appendChild(eSensorState); // Create Lat/Long and add them to Position Element ePosition = outDoc.createElement("Position"); Element eLatitudeDegrees = outDoc.createElement("LatitudeDegrees"); eLatitudeDegrees.appendChild(outDoc.createTextNode(tp.getLat())); Element eLongitudeDegrees = outDoc.createElement("LongitudeDegrees"); eLongitudeDegrees.appendChild(outDoc.createTextNode(tp.getLon())); ePosition.appendChild(eLongitudeDegrees); ePosition.appendChild(eLatitudeDegrees); eTrackpoint.appendChild(ePosition); //create HeartRate and add it to the trackpoint Element eHR = outDoc.createElement("HeartRateBpm"); eHR.setAttribute("xsi:type", "HeartRateInBeatsPerMinute_t"); Element eHRValue = outDoc.createElement("Value"); eHRValue.appendChild(outDoc.createTextNode(tp.getHr())); eHR.appendChild(eHRValue); eTrackpoint.appendChild(eHR); //create Cadence element text node and add it to the trackpoint Element eCad = outDoc.createElement("Cadence"); eCad.appendChild(outDoc.createTextNode(tp.getCad())); eTrackpoint.appendChild(eCad); //create Temperature element text node and add it to the trackpoint Element eTemp = outDoc.createElement("Temperature"); eTemp.appendChild(outDoc.createTextNode(tp.getTemp())); eTrackpoint.appendChild(eTemp); return eTrackpoint; } private void setDeviceType() { // Strava only recognizes Garmin devices for Altimeter stuff // Everything else shows up as "Mobile" if (hasAltimeter) { deviceType = "Garmin Edge 800"; } else { deviceType = "Garmin Edge 200"; } NodeList nl = outDoc.getElementsByTagName("Activity"); NodeList nl1 = ((Element) nl.item(0)).getElementsByTagName("Creator"); NodeList nl2 = ((Element) nl1.item(0)).getElementsByTagName("Name"); if (nl2 != null && nl2.getLength() > 0) { Element el = (Element) nl2.item(0); el.appendChild(outDoc.createTextNode(deviceType)); } } private boolean addTrackData() { boolean success = false; // Get the Track element Element track = null; Element docEle = outDoc.getDocumentElement(); // GRabbing the track node. in theory, the node list should always return 1 NodeList nl = docEle.getElementsByTagName("Track"); if (nl != null && nl.getLength() > 0) { track = (Element) nl.item(0); } int trkCounter = 0; Iterator<Trkpt> it = trackPoints.iterator(); while (it.hasNext()) { Trkpt t = (Trkpt) it.next(); Element tp = createTrackPointElement(t); //dumpNode(tp); track.appendChild(tp); trkCounter++; } log("Added " + trkCounter + " trackpoints to the template."); if (trkCounter >= 1) { success = true; } return success; } private static String findFlowKey(Node node) { String key = null; for (int i = 0; i < node.childNodes().size();) { Node child = node.childNode(i); if (child.nodeName().equals("#comment")) { //System.out.println(child.toString()); String flowKeyPattern = "\\<\\!-- flowExecutionKey\\: \\[(e1s1)\\] --\\>"; key = child.toString().replaceAll(flowKeyPattern, "$1").trim(); break; } else { findFlowKey(child); i++; } } return key; } public void upload() { httpClient = HttpClientBuilder.create().build(); localContext = new BasicHttpContext(); cookieStore = new BasicCookieStore(); localContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore); //httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY); if (doLogin()) { try { HttpGet get = new HttpGet("http://connect.garmin.com/transfer/upload#"); HttpResponse formResponse = httpClient.execute(get, localContext); HttpEntity formEntity = formResponse.getEntity(); EntityUtils.consume(formEntity); HttpPost request = new HttpPost( "http://connect.garmin.com/proxy/upload-service-1.1/json/upload/.tcx"); request.setHeader("Referer", "http://connect.garmin.com/api/upload/widget/manualUpload.faces?uploadServiceVersion=1.1"); request.setHeader("Accept", "text/html, application/xhtml+xml, */*"); MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); entity.addPart("data", new InputStreamBody(document2InputStream(outDoc), "application/octet-stream", "temp.tcx")); // Need to do this bit because without it you can't disable chunked encoding ByteArrayOutputStream bArrOS = new ByteArrayOutputStream(); entity.writeTo(bArrOS); bArrOS.flush(); ByteArrayEntity bArrEntity = new ByteArrayEntity(bArrOS.toByteArray()); bArrOS.close(); bArrEntity.setChunked(false); bArrEntity.setContentEncoding(entity.getContentEncoding()); bArrEntity.setContentType(entity.getContentType()); request.setEntity(bArrEntity); HttpResponse response = httpClient.execute(request, localContext); if (response.getStatusLine().getStatusCode() != 200) { log("Failed to Upload"); HttpEntity en = response.getEntity(); if (en != null) { String output = EntityUtils.toString(en); log(output); } } else { HttpEntity ent = response.getEntity(); if (ent != null) { String output = EntityUtils.toString(ent); output = "[" + output + "]"; //OMG Garmin Sucks at JSON..... JSONObject uploadResponse = new JSONArray(output).getJSONObject(0); JSONObject importResult = uploadResponse.getJSONObject("detailedImportResult"); try { int uploadID = importResult.getInt("uploadId"); log("Success! UploadID is " + uploadID); } catch (Exception e) { JSONArray failures = (JSONArray) importResult.get("failures"); JSONObject failure = (JSONObject) failures.get(0); JSONArray errorMessages = failure.getJSONArray("messages"); JSONObject errorMessage = errorMessages.getJSONObject(0); String content = errorMessage.getString("content"); log("Upload Failed! Error: " + content); } } } httpClient.close(); } catch (Exception ex) { log("Exception? " + ex.getMessage()); ex.printStackTrace(); // handle exception here } } else { log("Failed to upload!"); } } protected InputStream document2InputStream(Document document) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); OutputFormat outputFormat = new OutputFormat(document); XMLSerializer serializer = new XMLSerializer(outputStream, outputFormat); serializer.serialize(document); return new ByteArrayInputStream(outputStream.toByteArray()); } protected boolean doLogin() { boolean ret = false; log("Authenticating athlete..."); String gauthURL = "https://sso.garmin.com/sso/login?service=http%3A%2F%2Fconnect.garmin.com%2Fpost-auth%2Flogin&webhost=olaxpw-connect07.garmin.com&source=http%3A%2F%2Fconnect.garmin.com%2Fde-DE%2Fsignin&redirectAfterAccountLoginUrl=http%3A%2F%2Fconnect.garmin.com%2Fpost-auth%2Flogin&redirectAfterAccountCreationUrl=http%3A%2F%2Fconnect.garmin.com%2Fpost-auth%2Flogin&gauthHost=https%3A%2F%2Fsso.garmin.com%2Fsso&locale=de&id=gauth-widget&cssUrl=https%3A%2F%2Fstatic.garmincdn.com%2Fcom.garmin.connect%2Fui%2Fsrc-css%2Fgauth-custom.css&clientId=GarminConnect&rememberMeShown=true&rememberMeChecked=false&createAccountShown=true&openCreateAccount=false&usernameShown=true&displayNameShown=false&consumeServiceTicket=false&initialFocus=true&embedWidget=false"; try { HttpGet get = new HttpGet(gauthURL); HttpResponse formResponse = httpClient.execute(get, localContext); //log("Fetched the gauth url...: " + formResponse.getStatusLine()); String out = EntityUtils.toString(formResponse.getEntity()); org.jsoup.nodes.Document doc = Jsoup.parse(out); //System.out.println("RAW:\n" + out); String flowKey = findFlowKey(doc); //log("Looks like our Key is " + flowKey); HttpPost post = new HttpPost(gauthURL); post.setHeader("Referer", "https://sso.garmin.com/sso/login"); List<NameValuePair> nvps = new ArrayList<NameValuePair>(); nvps.add(new BasicNameValuePair("lt", flowKey)); nvps.add(new BasicNameValuePair("embed", "true")); nvps.add(new BasicNameValuePair("username", GPXConverter.getPref("garmin_username"))); nvps.add(new BasicNameValuePair("password", AccountManager.decrypt(GPXConverter.getPref("garmin_password")))); nvps.add(new BasicNameValuePair("_eventId", "submit")); post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); HttpResponse sessionResponse = httpClient.execute(post, localContext); String output = EntityUtils.toString(sessionResponse.getEntity()); Pattern ticketPattern = Pattern.compile("= '(http.*ticket=.*)';"); Matcher m = ticketPattern.matcher(output); String ticketURL = null; while (m.find()) { ticketURL = m.group(1); } //log("Ticket? " + ticketURL); HttpEntity entity = sessionResponse.getEntity(); EntityUtils.consume(entity); HttpHead head = new HttpHead(ticketURL); HttpResponse headResponse = httpClient.execute(head, localContext); if (headResponse.getStatusLine().getStatusCode() == 200) { ret = true; } HttpEntity ent = headResponse.getEntity(); EntityUtils.consume(ent); } catch (Exception e) { e.printStackTrace(); } return ret; } private void loadTCXTemplate() { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); try { DocumentBuilder db = dbf.newDocumentBuilder(); log("Loading TCX template file."); outDoc = db .parse(this.getClass().getResourceAsStream("/org/coronastreet/gpxconverter/tcxtemplate.xml")); } catch (ParserConfigurationException pce) { pce.printStackTrace(); } catch (SAXException se) { se.printStackTrace(); } catch (IOException ioe) { ioe.printStackTrace(); } } @SuppressWarnings("unused") private void dumpNode(JSONObject o) throws JSONException { log(o.toString(2)); } private void log(String s) { this.statusTextArea.append("GARMIN: " + s + "\n"); this.statusTextArea.repaint(1); } @SuppressWarnings("unused") private void log(InputStream is) { try { BufferedReader rd = new BufferedReader(new InputStreamReader(is)); String line = ""; while ((line = rd.readLine()) != null) { log(line); } } catch (Exception e) { e.printStackTrace(); } } public List<Trkpt> getTrackPoints() { return trackPoints; } public void setTrackPoints(List<Trkpt> trackPoints) { this.trackPoints = trackPoints; } public JTextArea getStatusTextArea() { return statusTextArea; } public void setStatusTextArea(JTextArea statusTextArea) { this.statusTextArea = statusTextArea; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getTripName() { return tripName; } public void setTripName(String tripName) { this.tripName = tripName; } public String getActivityType() { return activityType; } public void setActivityType(String at) { this.activityType = at; } public String getRideStartTime() { return rideStartTime; } public void setRideStartTime(String rideStartTime) { this.rideStartTime = rideStartTime; } public String getTotalTimeInSeconds() { return totalTimeInSeconds; } public void setTotalTimeInSeconds(String totalTimeInSeconds) { this.totalTimeInSeconds = totalTimeInSeconds; } public String getDistanceMeters() { return distanceMeters; } public void setDistanceMeters(String distanceMeters) { this.distanceMeters = distanceMeters; } public String getMaximumSpeed() { return maximumSpeed; } public void setMaximumSpeed(String maximumSpeed) { this.maximumSpeed = maximumSpeed; } public void setHasAltimeter(boolean hasAltimeter) { this.hasAltimeter = hasAltimeter; } }