Java tutorial
/* * Copyright (C) 2013 jonas.oreland@gmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.runnerup.export; import android.annotation.TargetApi; import android.content.ContentValues; import android.database.sqlite.SQLiteDatabase; import android.os.Build; import android.util.Log; import org.apache.http.HttpStatus; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.runnerup.common.util.Constants.DB; import org.runnerup.common.util.Constants.DB.FEED; import org.runnerup.export.format.GPX; import org.runnerup.export.format.NikeXML; import org.runnerup.export.util.FormValues; import org.runnerup.export.util.Part; import org.runnerup.export.util.StringWritable; import org.runnerup.export.util.SyncHelper; import org.runnerup.feed.FeedList; import org.runnerup.feed.FeedList.FeedUpdater; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.StringWriter; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.UnknownHostException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.TimeZone; @TargetApi(Build.VERSION_CODES.FROYO) public class NikePlusSynchronizer extends DefaultSynchronizer { public static final String NAME = "Nike+"; private static String CLIENT_ID = null; private static String CLIENT_SECRET = null; private static String APP_ID = null; private static final String BASE_URL = "https://api.nike.com"; private static final String LOGIN_URL = BASE_URL + "/nsl/v2.0/user/login?client_id=%s&client_secret=%s&app=%s"; private static final String SYNC_URL = BASE_URL + "/v2.0/me/sync?access_token=%s"; private static final String SYNC_COMPLETE_URL = BASE_URL + "/v2.0/me/sync/complete?access_token=%s"; private static final String USER_AGENT = "NPConnect"; private static final String PROFILE_URL = BASE_URL + "/v1.0/me/profile?access_token=%s"; private static final String MY_FEED_URL = BASE_URL + "/v1.0/me/home/feed?access_token=%s&start=%d&count=%d"; private static final String FRIEND_FEED_URL = BASE_URL + "/v1.0/me/friends/feed?access_token=%s&startIndex=%d&count=%d"; long id = 0; private String username = null; private String password = null; private String access_token = null; private long expires_timeout = 0; NikePlusSynchronizer(SyncManager syncManager) { if (CLIENT_ID == null || CLIENT_SECRET == null || APP_ID == null) { try { JSONObject tmp = new JSONObject(syncManager.loadData(this)); CLIENT_ID = tmp.getString("CLIENT_ID"); CLIENT_SECRET = tmp.getString("CLIENT_SECRET"); APP_ID = tmp.getString("APP_ID"); } catch (Exception ex) { ex.printStackTrace(); } } } @Override public long getId() { return id; } @Override public String getName() { return NAME; } @Override public void init(ContentValues config) { id = config.getAsLong("_id"); String authToken = config.getAsString(DB.ACCOUNT.AUTH_CONFIG); if (authToken != null) { try { JSONObject tmp = new JSONObject(authToken); username = tmp.optString("username", null); password = tmp.optString("password", null); } catch (JSONException e) { e.printStackTrace(); } } } @Override public boolean isConfigured() { if (username != null && password != null) { return true; } return false; } @Override public String getAuthConfig() { JSONObject tmp = new JSONObject(); try { tmp.put("username", username); tmp.put("password", password); } catch (JSONException e) { e.printStackTrace(); } return tmp.toString(); } @Override public void reset() { username = null; password = null; access_token = null; } private static long now() { return android.os.SystemClock.elapsedRealtime() / 1000; } @Override public Status connect() { if (now() > expires_timeout) { access_token = null; } if (access_token != null) { return Status.OK; } Status s = Status.NEED_AUTH; s.authMethod = Synchronizer.AuthMethod.USER_PASS; if (username == null || password == null) { return s; } HttpURLConnection conn = null; try { /** * get user id/key */ String url = String.format(LOGIN_URL, CLIENT_ID, CLIENT_SECRET, APP_ID); conn = (HttpURLConnection) new URL(url).openConnection(); conn.setDoOutput(true); conn.setRequestMethod(RequestMethod.POST.name()); conn.addRequestProperty("user-agent", USER_AGENT); conn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.addRequestProperty("Accept", "application/json"); FormValues kv = new FormValues(); kv.put("email", username); kv.put("password", password); { OutputStream wr = new BufferedOutputStream(conn.getOutputStream()); kv.write(wr); wr.flush(); wr.close(); String response; { BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); StringBuilder buf = new StringBuilder(); String line; while ((line = in.readLine()) != null) { buf.append(line); } response = buf.toString().replaceAll("<User>.*</User>", "\"\""); } JSONObject obj = SyncHelper.parse(new ByteArrayInputStream(response.getBytes())); access_token = obj.getString("access_token"); String expires = obj.getString("expires_in"); expires_timeout = now() + Long.parseLong(expires); s = Status.OK; } } catch (UnknownHostException e) { // probably no internet connection available s = Status.SKIP; } catch (Exception e) { s.ex = e; } if (conn != null) { conn.disconnect(); } if (s.ex != null) { Log.e(getClass().getSimpleName(), s.ex.getMessage()); } return s; } @Override public Status upload(SQLiteDatabase db, long mID) { Status s; if ((s = connect()) != Status.OK) { return s; } NikeXML nikeXML = new NikeXML(db); GPX nikeGPX = new GPX(db); HttpURLConnection conn = null; Exception ex = null; try { StringWriter xml = new StringWriter(); nikeXML.export(mID, xml); StringWriter gpx = new StringWriter(); nikeGPX.export(mID, gpx); String url = String.format(SYNC_URL, access_token); conn = (HttpURLConnection) new URL(url).openConnection(); conn.setDoOutput(true); conn.setRequestMethod(RequestMethod.POST.name()); conn.addRequestProperty("user-agent", USER_AGENT); conn.addRequestProperty("appid", APP_ID); Part<StringWritable> part1 = new Part<StringWritable>("runXML", new StringWritable(xml.toString())); part1.setFilename("runXML.xml"); part1.setContentType("text/plain; charset=US-ASCII"); part1.setContentTransferEncoding("8bit"); Part<StringWritable> part2 = new Part<StringWritable>("gpxXML", new StringWritable(gpx.toString())); part2.setFilename("gpxXML.xml"); part2.setContentType("text/plain; charset=US-ASCII"); part2.setContentTransferEncoding("8bit"); Part<?> parts[] = { part1, part2 }; SyncHelper.postMulti(conn, parts); int responseCode = conn.getResponseCode(); String amsg = conn.getResponseMessage(); conn.connect(); if (responseCode != HttpStatus.SC_OK) { throw new Exception(amsg); } url = String.format(SYNC_COMPLETE_URL, access_token); conn = (HttpURLConnection) new URL(url).openConnection(); conn.setDoOutput(true); conn.setRequestMethod(RequestMethod.POST.name()); conn.addRequestProperty("user-agent", USER_AGENT); conn.addRequestProperty("appid", APP_ID); responseCode = conn.getResponseCode(); amsg = conn.getResponseMessage(); conn.disconnect(); if (responseCode == HttpStatus.SC_OK) { s = Status.OK; s.activityId = mID; return s; } ex = new Exception(amsg); } catch (Exception e) { ex = e; } s = Synchronizer.Status.ERROR; s.ex = ex; s.activityId = mID; if (ex != null) { ex.printStackTrace(); } return s; } @Override public boolean checkSupport(Synchronizer.Feature f) { switch (f) { case FEED: case UPLOAD: return true; case GET_WORKOUT: case WORKOUT_LIST: case LIVE: case SKIP_MAP: break; } return false; } @Override public Status getFeed(FeedUpdater feedUpdater) { Status s; if ((s = connect()) != Status.OK) { return s; } try { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()); df.setTimeZone(TimeZone.getTimeZone("UTC")); List<ContentValues> result = new ArrayList<ContentValues>(); getOwnFeed(df, result); getFriendsFeed(df, result); FeedList.sort(result); feedUpdater.addAll(result); return Status.OK; } finally { } } JSONObject makeGetRequest(String url) throws MalformedURLException, IOException, JSONException { HttpURLConnection conn = null; try { conn = (HttpURLConnection) new URL(url).openConnection(); conn.setRequestMethod(RequestMethod.GET.name()); conn.addRequestProperty("Accept", "application/json"); conn.addRequestProperty("User-Agent", USER_AGENT); conn.addRequestProperty("appid", APP_ID); final InputStream in = new BufferedInputStream(conn.getInputStream()); final JSONObject reply = SyncHelper.parse(in); final int code = conn.getResponseCode(); conn.disconnect(); if (code == HttpStatus.SC_OK) return reply; } finally { if (conn != null) conn.disconnect(); } return new JSONObject(); } private boolean parsePayload(ContentValues c, JSONObject p) throws NumberFormatException, JSONException { long duration = Long.parseLong(p.getString("duration")) / 1000; double distance = 1000 * Double.parseDouble(p.getString("distance")); if (duration < 0 || distance < 0) { return false; } if (duration > 0) c.put(FEED.DURATION, duration); if (distance > 0) c.put(FEED.DISTANCE, distance); return true; } private void getOwnFeed(SimpleDateFormat df, List<ContentValues> result) { JSONObject profile = null; try { profile = makeGetRequest(String.format(PROFILE_URL, access_token)); String first = profile.getString("firstName"); String last = profile.getString("lastName"); String userUrl = profile.has("avatarFullUrl") ? profile.getString("avatarFullUrl") : null; JSONObject feed = makeGetRequest(String.format(MY_FEED_URL, access_token, 1, 25)); JSONArray arr = feed.getJSONArray("events"); for (int i = 0; i < arr.length(); i++) { JSONObject e = arr.getJSONObject(i); try { String type = e.getString("eventType"); if (!"APPLICATION.SYNC.RUN".contentEquals(type)) continue; ContentValues c = new ContentValues(); c.put(FEED.ACCOUNT_ID, getId()); c.put(FEED.EXTERNAL_ID, e.getString("entityId")); c.put(FEED.FEED_TYPE, FEED.FEED_TYPE_ACTIVITY); c.put(FEED.FEED_SUBTYPE, DB.ACTIVITY.SPORT_RUNNING); // TODO c.put(FEED.START_TIME, df.parse(e.getString("entityDate")).getTime()); if (e.has("payload")) { JSONObject p = e.getJSONObject("payload"); if (!parsePayload(c, p)) continue; // skip this } c.put(FEED.USER_FIRST_NAME, first); c.put(FEED.USER_LAST_NAME, last); if (userUrl != null) { c.put(FEED.USER_IMAGE_URL, userUrl); } result.add(c); } catch (Exception ex) { ex.printStackTrace(); } } } catch (Exception e) { if (profile != null) { Log.e(getClass().getSimpleName(), "Failed for profile '" + profile + "' because of the following: " + e.getMessage()); } else { e.getStackTrace(); } } } private void getFriendsFeed(SimpleDateFormat df, List<ContentValues> result) { try { JSONObject feed = makeGetRequest(String.format(FRIEND_FEED_URL, access_token, 1, 25)); if (!feed.has("friends")) { Log.i(getClass().getSimpleName(), "No friends found, skipping their feed..."); return; } JSONArray arr = feed.getJSONArray("friends"); for (int i = 0; i < arr.length(); i++) { JSONObject e = arr.getJSONObject(i).getJSONObject("event"); try { String type = e.getString("eventType"); if (!"APPLICATION.SYNC.RUN".contentEquals(type)) continue; ContentValues c = new ContentValues(); c.put(FEED.ACCOUNT_ID, getId()); c.put(FEED.EXTERNAL_ID, e.getString("entityId")); c.put(FEED.FEED_TYPE, FEED.FEED_TYPE_ACTIVITY); c.put(FEED.FEED_SUBTYPE, DB.ACTIVITY.SPORT_RUNNING); // TODO c.put(FEED.START_TIME, df.parse(e.getString("entityDate")).getTime()); if (e.has("payload")) { JSONObject p = e.getJSONObject("payload"); if (!parsePayload(c, p)) continue; // skip this } c.put(FEED.USER_IMAGE_URL, e.getString("avatar")); result.add(c); } catch (Exception ex) { ex.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); } } }