package com.piusvelte.sonet.social; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; import com.piusvelte.sonet.BuildConfig; import com.piusvelte.sonet.R; import com.piusvelte.sonet.Sonet; import com.piusvelte.sonet.SonetCrypto; import com.piusvelte.sonet.SonetHttpClient; import com.piusvelte.sonet.provider.Entity; import com.piusvelte.sonet.provider.Notifications; import com.piusvelte.sonet.provider.Statuses; import com.squareup.okhttp.Request; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Set; import static com.piusvelte.sonet.Sonet.SNearby; import static com.piusvelte.sonet.Sonet.Saccess_token; import static com.piusvelte.sonet.Sonet.Scheckin; import static com.piusvelte.sonet.Sonet.Scomments; import static com.piusvelte.sonet.Sonet.ScreatedAt; import static com.piusvelte.sonet.Sonet.SfirstName; import static com.piusvelte.sonet.Sonet.Sgroups; import static com.piusvelte.sonet.Sonet.Sid; import static com.piusvelte.sonet.Sonet.Sitems; import static com.piusvelte.sonet.Sonet.SlastName; import static com.piusvelte.sonet.Sonet.Sname; import static com.piusvelte.sonet.Sonet.Sphoto; import static com.piusvelte.sonet.Sonet.Srecent; import static com.piusvelte.sonet.Sonet.Sresponse; import static com.piusvelte.sonet.Sonet.Sshout; import static com.piusvelte.sonet.Sonet.Stext; import static com.piusvelte.sonet.Sonet.Suser; import static com.piusvelte.sonet.Sonet.Svenue; /** * Created by bemmanuel on 2/15/15. */ public class Foursquare extends Client { private static final String API_VERSION = "20140101"; private static final String PHOTO_SIZE = "500x500"; private static final String FOURSQUARE_BASE_URL = "https://api.foursquare.com/v2/"; private static final String FOURSQUARE_URL_AUTHORIZE = "https://foursquare" + ".com/oauth2/authorize?client_id=%s&response_type=token&redirect_uri=%s&display=touch"; private static final String FOURSQUARE_URL_ME = "%susers/self?oauth_token=%s&m=foursquare&v=" + API_VERSION; private static final String FOURSQUARE_URL_PROFILE = "https://foursquare.com/user/%s&m=foursquare&v=" + API_VERSION; private static final String FOURSQUARE_CHECKINS = "%scheckins/recent?oauth_token=%s&m=foursquare&v=" + API_VERSION; private static final String FOURSQUARE_CHECKIN = "%scheckins/add?venueId=%s&shout=%s&ll=%s,%s&broadcast=public&oauth_token=%s&m=foursquare&v=" + API_VERSION; private static final String FOURSQUARE_CHECKIN_NO_VENUE = "%scheckins/add?shout=%s&broadcast=public&oauth_token=%s&m=foursquare&v=" + API_VERSION; private static final String FOURSQUARE_CHECKIN_NO_SHOUT = "%scheckins/add?venueId=%s&ll=%s,%s&broadcast=public&oauth_token=%s&m=foursquare&v=" + API_VERSION; private static final String FOURSQUARE_ADDCOMMENT = "%scheckins/%s/addcomment?text=%s&oauth_token=%s&m=foursquare&v=" + API_VERSION; private static final String FOURSQUARE_SEARCH = "%svenues/search?ll=%s,%s&intent=checkin&oauth_token=%s&m=foursquare&v=" + API_VERSION; private static final String FOURSQUARE_GET_CHECKIN = "%scheckins/%s?oauth_token=%s&m=foursquare&v=" + API_VERSION; private static final String FOURSQUARE_URL_USER = "%susers/%s?oauth_token=%s&m=foursquare&v=" + API_VERSION; public Foursquare(Context context, String token, String secret, String accountEsid, int network) { super(context, token, secret, accountEsid, network); } @Nullable @Override public String getProfileUrl(@NonNull String esid) { return String.format(FOURSQUARE_URL_PROFILE, esid); } @Nullable @Override public String getProfilePhotoUrl() { return getProfilePhotoUrl(mAccountEsid); } @Nullable @Override public String getProfilePhotoUrl(String esid) { String httpResponse = SonetHttpClient.httpResponse(String.format(FOURSQUARE_URL_ME, FOURSQUARE_BASE_URL, mToken)); if (!TextUtils.isEmpty(httpResponse)) { JSONObject jobj; try { jobj = (new JSONObject(httpResponse)).getJSONObject("response").getJSONObject("user"); JSONObject photo = jobj.getJSONObject(Sphoto); return photo.getString("prefix") + PHOTO_SIZE + photo.getString("suffix"); } catch (JSONException e) { if (BuildConfig.DEBUG) { Log.d(mTag, "error parsing foursquare me response: " + httpResponse, e); } } } return null; } @Nullable @Override public Uri getCallback() { return Uri.parse("sonet://foursquare"); } @Override String getRequestUrl() { return null; } @Override String getAccessUrl() { return null; } @Override String getAuthorizeUrl() { return null; } @Override public String getCallbackUrl() { return null; } @Override public MemberAuthentication getMemberAuthentication(@NonNull String authenticatedUrl) { String token = getParamValue(authenticatedUrl, Saccess_token); if (!TextUtils.isEmpty(token)) { String httpResponse = SonetHttpClient.httpResponse(String.format(FOURSQUARE_URL_ME, FOURSQUARE_BASE_URL, token)); if (!TextUtils.isEmpty(httpResponse)) { JSONObject jobj; try { jobj = (new JSONObject(httpResponse)).getJSONObject("response").getJSONObject("user"); if (jobj.has("firstName") && jobj.has(Sid)) { MemberAuthentication memberAuthentication = new MemberAuthentication(); memberAuthentication.username = jobj.getString("firstName") + " " + jobj.getString("lastName"); memberAuthentication.token = token; memberAuthentication.secret = ""; memberAuthentication.expiry = 0; memberAuthentication.network = mNetwork; memberAuthentication.id = jobj.getString(Sid); return memberAuthentication; } } catch (JSONException e) { if (BuildConfig.DEBUG) { Log.d(mTag, "error parsing foursquare me response: " + httpResponse, e); } } } } return null; } @Nullable @Override public String getAuthUrl() { return String.format(FOURSQUARE_URL_AUTHORIZE, BuildConfig.FOURSQUARE_KEY, getCallback().toString()); } @Nullable @Override public Set<String> getNotificationStatusIds(long account, String[] notificationMessage) { Set<String> notificationSids = new HashSet<>(); Cursor currentNotifications = getContentResolver().query(Notifications.getContentUri(mContext), new String[] { Notifications._ID, Notifications.SID, Notifications.UPDATED, Notifications.CLEARED, Notifications.ESID }, Notifications.ACCOUNT + "=?", new String[] { Long.toString(account) }, null); // loop over notifications if (currentNotifications.moveToFirst()) { while (!currentNotifications.isAfterLast()) { long notificationId = currentNotifications.getLong(0); String sid = SonetCrypto.getInstance(mContext).Decrypt(currentNotifications.getString(1)); long updated = currentNotifications.getLong(2); boolean cleared = currentNotifications.getInt(3) == 1; // store sids, to avoid duplicates when requesting the latest feed if (!notificationSids.contains(sid)) { notificationSids.add(sid); } // get comments for current notifications String response = SonetHttpClient .httpResponse(String.format(FOURSQUARE_GET_CHECKIN, FOURSQUARE_BASE_URL, sid, mToken)); if (!TextUtils.isEmpty(response)) { // check for a newer post, if it's the user's own, then set CLEARED=0 try { JSONArray commentsArray = new JSONObject(response).getJSONObject(Sresponse).getJSONObject(Scheckin).getJSONObject(Scomments) .getJSONArray(Sitems); int i2 = commentsArray.length(); if (i2 > 0) { for (int i = 0; i < i2; i++) { JSONObject commentObj = commentsArray.getJSONObject(i); long created_time = commentObj.getLong(ScreatedAt) * 1000; if (created_time > updated) { JSONObject friendObj = commentObj.getJSONObject(Suser); updateNotificationMessage(notificationMessage, updateNotification(notificationId, created_time, mAccountEsid, friendObj.getString(Sid), friendObj.getString(SfirstName) + " " + friendObj.getString(SlastName), cleared)); } } } } catch (JSONException e) { if (BuildConfig.DEBUG) Log.d(mTag, "error parsing notifications", e); } } currentNotifications.moveToNext(); } } currentNotifications.close(); return notificationSids; } @Nullable @Override public String getFeedResponse(int status_count) { return SonetHttpClient.httpResponse(String.format(FOURSQUARE_CHECKINS, FOURSQUARE_BASE_URL, mToken)); } @Nullable @Override public JSONArray parseFeed(@NonNull String response) throws JSONException { return new JSONObject(response).getJSONObject(Sresponse).getJSONArray(Srecent); } @Nullable @Override public void addFeedItem(@NonNull JSONObject item, boolean display_profile, boolean time24hr, int appWidgetId, long account, Set<String> notificationSids, String[] notificationMessage, boolean doNotify) throws JSONException { JSONObject friendObj = item.getJSONObject(Suser); String shout = ""; if (item.has(Sshout)) { shout = item.getString(Sshout) + "\n"; } if (item.has(Svenue)) { JSONObject venue = item.getJSONObject(Svenue); if (venue.has(Sname)) { shout += "@" + venue.getString(Sname); } } long date = item.getLong(ScreatedAt) * 1000; // notifications String esid = friendObj.getString(Sid); int commentCount = 0; String sid = item.getString(Sid); String friend = friendObj.getString(SfirstName) + " " + friendObj.getString(SlastName); String notification = null; if (item.has(Scomments)) { JSONObject commentsJObj = item.getJSONObject(Scomments); if (commentsJObj.has(Sitems)) { JSONArray commentsArray = item.getJSONObject(Scomments).getJSONArray(Sitems); commentCount = commentsArray.length(); if (!notificationSids.contains(sid) && commentCount > 0) { // default hasCommented to whether or not these comments are for the own user's status boolean hasCommented = notification != null || esid.equals(mAccountEsid); for (int c2 = 0; c2 < commentCount; c2++) { JSONObject commentObj = commentsArray.getJSONObject(c2); if (commentObj.has(Suser)) { JSONObject c4 = commentObj.getJSONObject(Suser); if (c4.getString(Sid).equals(mAccountEsid)) { if (!hasCommented) { // the user has commented on this thread, notify any updates hasCommented = true; } // clear any notifications, as the user is already aware if (notification != null) { notification = null; } } else if (hasCommented) { // don't notify about user's own comments // send the parent comment sid notification = String.format(getString(R.string.friendcommented), c4.getString(SfirstName) + " " + c4.getString(SlastName)); } } } } } } if (doNotify && notification != null) { // new notification addNotification(sid, esid, friend, shout, date, account, notification); updateNotificationMessage(notificationMessage, notification); } JSONObject photo = friendObj.getJSONObject(Sphoto); addStatusItem(date, friend, display_profile ? photo.getString("prefix") + PHOTO_SIZE + photo.getString("suffix") : null, String.format(getString(R.string.messageWithCommentCount), shout, commentCount), time24hr, appWidgetId, account, sid, esid ); } @Nullable @Override public void getNotificationMessage(long account, String[] notificationMessage) { } @Override public void getNotifications(long account, String[] notificationMessage) { Cursor currentNotifications = getContentResolver().query(Notifications.getContentUri(mContext), new String[] { Notifications._ID, Notifications.SID, Notifications.UPDATED, Notifications.CLEARED, Notifications.ESID }, Notifications.ACCOUNT + "=?", new String[] { Long.toString(account) }, null); if (currentNotifications.moveToFirst()) { Set<String> notificationSids = new HashSet<>(); // loop over notifications while (!currentNotifications.isAfterLast()) { long notificationId = currentNotifications.getLong(0); String sid = SonetCrypto.getInstance(mContext).Decrypt(currentNotifications.getString(1)); long updated = currentNotifications.getLong(2); boolean cleared = currentNotifications.getInt(3) == 1; // store sids, to avoid duplicates when requesting the latest feed notificationSids.add(sid); // get comments for current notifications String response = SonetHttpClient .httpResponse(String.format(FOURSQUARE_GET_CHECKIN, FOURSQUARE_BASE_URL, sid, mToken)); if (!TextUtils.isEmpty(response)) { // check for a newer post, if it's the user's own, then set CLEARED=0 try { JSONArray comments = new JSONObject(response).getJSONObject(Sresponse).getJSONObject(Scheckin).getJSONObject(Scomments) .getJSONArray(Sitems); int i2 = comments.length(); if (i2 > 0) { for (int i = 0; i < i2; i++) { JSONObject comment = comments.getJSONObject(i); long created_time = comment.getLong(ScreatedAt) * 1000; if (created_time > updated) { // new comment ContentValues values = new ContentValues(); values.put(Notifications.UPDATED, created_time); JSONObject user = comment.getJSONObject(Suser); if (mAccountEsid.equals(user.getString(Sid))) { // user's own comment, clear the notification values.put(Notifications.CLEARED, 1); } else if (cleared) { values.put(Notifications.NOTIFICATION, String.format(getString(R.string.friendcommented), user.getString(SfirstName) + " " + user.getString(SlastName))); values.put(Notifications.CLEARED, 0); } else { values.put(Notifications.NOTIFICATION, String.format(getString(R.string.friendcommented), user.getString(SfirstName) + " " + user.getString(SlastName) + " and others")); } getContentResolver().update(Notifications.getContentUri(mContext), values, Notifications._ID + "=?", new String[] { Long.toString(notificationId) }); } } } } catch (JSONException e) { if (BuildConfig.DEBUG) Log.e(mTag, e.toString()); } } currentNotifications.moveToNext(); } // check the latest feed String response = SonetHttpClient.httpResponse(String.format(FOURSQUARE_CHECKINS, FOURSQUARE_BASE_URL, mToken)); if (!TextUtils.isEmpty(response)) { try { JSONArray jarr = new JSONObject(response).getJSONObject(Sresponse).getJSONArray(Srecent); // if there are updates, clear the cache int d2 = jarr.length(); if (d2 > 0) { for (int d = 0; d < d2; d++) { JSONObject o = jarr.getJSONObject(d); String sid = o.getString(Sid); // if already notified, ignore if (!notificationSids.contains(sid)) { if (o.has(Suser) && o.has(Scomments)) { JSONObject f = o.getJSONObject(Suser); if (f.has(SfirstName) && f.has(SlastName) && f.has(Sid)) { String notification = null; String esid = f.getString(Sid); String friend = f.getString(SfirstName) + " " + f.getString(SlastName); JSONArray comments = o.getJSONArray(Scomments); int commentCount = comments.length(); // notifications if (commentCount > 0) { // default hasCommented to whether or not these comments are for the own user's status boolean hasCommented = notification != null || esid.equals(mAccountEsid); for (int c2 = 0; c2 < commentCount; c2++) { JSONObject c3 = comments.getJSONObject(c2); if (c3.has(Suser)) { JSONObject c4 = c3.getJSONObject(Suser); if (c4.getString(Sid).equals(mAccountEsid)) { if (!hasCommented) { // the user has commented on this thread, notify any updates hasCommented = true; } // clear any notifications, as the user is already aware if (notification != null) { notification = null; } } else if (hasCommented) { // don't notify about user's own comments // send the parent comment sid notification = String.format(getString(R.string.friendcommented), c4.getString(SfirstName) + " " + c4.getString(SlastName)); } } } } if (notification != null) { String message = ""; if (o.has(Sshout)) { message = o.getString(Sshout) + "\n"; } if (o.has(Svenue)) { JSONObject venue = o.getJSONObject(Svenue); if (venue.has(Sname)) { message += "@" + venue.getString(Sname); } } // new notification addNotification(sid, esid, friend, message, o.getLong(ScreatedAt) * 1000, account, notification); } } } } } } } catch (JSONException e) { if (BuildConfig.DEBUG) Log.e(mTag, e.toString()); } } } currentNotifications.close(); } @Override public boolean createPost(String message, String placeId, String latitude, String longitude, String photoPath, String[] tags) { try { message = URLEncoder.encode(message, "UTF-8"); } catch (UnsupportedEncodingException e) { if (BuildConfig.DEBUG) { Log.d(mTag, "url encode exception: " + message, e); } } String url; if (!TextUtils.isEmpty(placeId)) { if (!TextUtils.isEmpty(message)) { url = String.format(FOURSQUARE_CHECKIN, FOURSQUARE_BASE_URL, placeId, message, latitude, longitude, mToken); } else { url = String.format(FOURSQUARE_CHECKIN_NO_SHOUT, FOURSQUARE_BASE_URL, placeId, latitude, longitude, mToken); } } else { url = String.format(FOURSQUARE_CHECKIN_NO_VENUE, FOURSQUARE_BASE_URL, message, mToken); } Request request = new Request.Builder() .url(url) .post(null) .build(); return SonetHttpClient.request(request); } @Override public boolean isLikeable(String statusId) { return true; } @Override public boolean isLiked(String statusId, String accountId) { return false; } @Override public boolean likeStatus(String statusId, String accountId, boolean doLike) { return false; } @Override public String getLikeText(boolean isLiked) { return getString(isLiked ? R.string.unlike : R.string.like); } @Override public boolean isCommentable(String statusId) { return true; } @Override public String getCommentPretext(String accountId) { return null; } @Override public void onDelete() { } @Nullable @Override public String getCommentsResponse(String statusId) { return SonetHttpClient.httpResponse(String.format(FOURSQUARE_GET_CHECKIN, FOURSQUARE_BASE_URL, statusId, mToken)); } @Nullable @Override public JSONArray parseComments(@NonNull String response) throws JSONException { return new JSONObject(response).getJSONObject(Sresponse).getJSONObject(Scheckin).getJSONObject(Scomments).getJSONArray(Sitems); } @Nullable @Override public HashMap<String, String> parseComment(@NonNull String statusId, @NonNull JSONObject jsonComment, boolean time24hr) throws JSONException { JSONObject user = jsonComment.getJSONObject(Suser); HashMap<String, String> commentMap = new HashMap<>(); commentMap.put(Statuses.SID, jsonComment.getString(Sid)); commentMap.put(Entity.FRIEND, user.getString(SfirstName) + " " + user.getString(SlastName)); commentMap.put(Statuses.MESSAGE, jsonComment.getString(Stext)); commentMap.put(Statuses.CREATEDTEXT, Sonet.getCreatedText(jsonComment.getLong(ScreatedAt) * 1000, time24hr)); commentMap.put(getString(R.string.like), ""); return commentMap; } @Override public LinkedHashMap<String, String> getLocations(String latitude, String longitude) { String response = SonetHttpClient .httpResponse(String.format(FOURSQUARE_SEARCH, FOURSQUARE_BASE_URL, latitude, longitude, mToken)); if (!TextUtils.isEmpty(response)) { LinkedHashMap<String, String> locations = new LinkedHashMap<String, String>(); try { JSONArray groups = new JSONObject(response).getJSONObject(Sresponse).getJSONArray(Sgroups); for (int g = 0, g2 = groups.length(); g < g2; g++) { JSONObject group = groups.getJSONObject(g); if (group.getString(Sname).equals(SNearby)) { JSONArray places = group.getJSONArray(Sitems); for (int i = 0, i2 = places.length(); i < i2; i++) { JSONObject place = places.getJSONObject(i); locations.put(place.getString(Sid), place.getString(Sname)); } break; } } } catch (JSONException e) { Log.e(mTag, e.toString()); } return locations; } return null; } @Override public boolean sendComment(@NonNull String statusId, @NonNull String message) { try { message = URLEncoder.encode(message, "UTF-8"); Request request = new Request.Builder() .url(String.format(FOURSQUARE_ADDCOMMENT, FOURSQUARE_BASE_URL, statusId, message, mToken)) .post(null) .build(); return SonetHttpClient.request(request); } catch (UnsupportedEncodingException e) { if (BuildConfig.DEBUG) Log.e(mTag, e.toString()); } return false; } @Override public List<HashMap<String, String>> getFriends() { return null; } @Override String getApiKey() { return BuildConfig.FOURSQUARE_KEY; } @Override String getApiSecret() { return BuildConfig.FOURSQUARE_SECRET; } }