com.popdeem.sdk.core.api.PDAPIClient.java Source code

Java tutorial

Introduction

Here is the source code for com.popdeem.sdk.core.api.PDAPIClient.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Popdeem
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.popdeem.sdk.core.api;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.jakewharton.retrofit.Ok3Client;
import com.popdeem.sdk.core.PopdeemSDK;
import com.popdeem.sdk.core.api.response.PDBasicResponse;
import com.popdeem.sdk.core.deserializer.PDBGScanResponseDeserializer;
import com.popdeem.sdk.core.deserializer.PDBrandsDeserializer;
import com.popdeem.sdk.core.deserializer.PDFeedsDeserializer;
import com.popdeem.sdk.core.deserializer.PDInstagramUserDeserializer;
import com.popdeem.sdk.core.deserializer.PDMessagesDeserializer;
import com.popdeem.sdk.core.deserializer.PDRewardsDeserializer;
import com.popdeem.sdk.core.deserializer.PDTwitterUserDeserializer;
import com.popdeem.sdk.core.deserializer.PDUserDeserializer;
import com.popdeem.sdk.core.exception.PopdeemSDKNotInitializedException;
import com.popdeem.sdk.core.model.PDBGScanResponseModel;
import com.popdeem.sdk.core.model.PDBrand;
import com.popdeem.sdk.core.model.PDFeed;
import com.popdeem.sdk.core.model.PDMessage;
import com.popdeem.sdk.core.model.PDReward;
import com.popdeem.sdk.core.model.PDUser;
import com.popdeem.sdk.core.realm.PDRealmNonSocialUID;
import com.popdeem.sdk.core.realm.PDRealmReferral;
import com.popdeem.sdk.core.realm.PDRealmThirdPartyToken;
import com.popdeem.sdk.core.realm.PDRealmUserDetails;
import com.popdeem.sdk.core.utils.PDSocialUtils;
import com.popdeem.sdk.core.utils.PDUtils;

import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

import io.realm.Realm;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit.RestAdapter;
import retrofit.RetrofitError;
import retrofit.client.Header;
import retrofit.client.Response;
import retrofit.converter.Converter;
import retrofit.converter.GsonConverter;
import retrofit.mime.TypedByteArray;
import retrofit.mime.TypedInput;
import retrofit.mime.TypedString;

/**
 * PDAPIClient is used to access the Popdeem API.
 * You access this class using the {@link #instance()}.
 * <p>
 * Created by mikenolan on 18/02/16.
 * </p>
 */
public class PDAPIClient {

    /**
     * {@link Interceptor} for adding Popdeem API Key to request headers.
     */
    private static final Interceptor PD_API_KEY_INTERCEPTOR = new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            Log.i("Popdeem API Key", "intercept: " + PopdeemSDK.getPopdeemAPIKey());
            return chain.proceed(chain.request().newBuilder()
                    .addHeader(PDAPIConfig.REQUEST_HEADER_API_KEY, PopdeemSDK.getPopdeemAPIKey()).build());
        }
    };

    //    private static Interceptor PD_USER_TOKEN_INTERCEPTOR = null;
    //    private static String userToken = null;

    /**
     * Private empty constructor to stop instances being created using "new"
     */
    private PDAPIClient() {
    }

    /**
     * Create an instance of {@link PDAPIClient} to access Popdeem API.
     * <p>
     * Popdeem SDK must be initialized using {@link PopdeemSDK#initializeSDK(Application)} and your Popdeem API Key must be present in your Application's AndroidManifest.xml before creating an instance of {@link PDAPIClient}.
     * </p>
     *
     * @return instance of {@link PDAPIClient}
     */
    public static PDAPIClient instance() {
        if (!PopdeemSDK.isPopdeemSDKInitialized()) {
            throw new PopdeemSDKNotInitializedException(
                    "Popdeem SDK is not initialized. Be sure to call PopdeemSDK.initializeSDK(Application application) in your Application class before using the SDK.");
        }
        PDUtils.validateAPIKeyIsPresent();
        //        if (userToken == null) {
        //            userToken = PDUtils.getUserToken();
        //        }
        return new PDAPIClient();
    }

    /**
     * Create a Non Social User. This user will be updated upon a social login.
     *
     * @param uid         Unique Identifier for user
     * @param deviceToken Device Token for GCM Push
     * @param callback    {@link PDAPICallback} for API result
     */
    public void createNonSocialUser(@NonNull String uid, String deviceToken,
            @NonNull final PDAPICallback<PDBasicResponse> callback) {
        PopdeemAPI api = getApiInterface(null, null);
        api.createNonSocialUser("", uid, deviceToken, PDAPIConfig.PLATFORM_VALUE, callback);
    }

    /**
     * Register a user using the Facebook Access Token and Facebook App ID from Login
     * <p>
     * If user is a new user, then a new user will be created
     * If not, user will be loaded
     * In both cases response is a User object
     * </p>
     *
     * @param facebookAccessToken Access Token {@link String} received on successful Facebook login
     * @param facebookUserID      Facebook Application ID received on successful Facebook login
     * @param callback            {@link PDAPICallback} for API result
     */
    public void registerUserWithFacebook(@NonNull final String facebookAccessToken,
            @NonNull final String facebookUserID, @NonNull final PDAPICallback<PDUser> callback) {
        Gson gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDUserDeserializer()).create();

        Realm realm = Realm.getDefaultInstance();
        final PDRealmNonSocialUID uid = realm.where(PDRealmNonSocialUID.class).findFirst();
        final String uidString = uid == null ? null : uid.getUid();
        realm.close();

        PopdeemAPI api = getApiInterface(null, new GsonConverter(gson));
        api.registerUserWithFacebook("", facebookAccessToken, facebookUserID, uidString, callback);
    }

    /**
     * Register a user using the Facebook Access Token and Facebook App ID from Login
     * <p>
     * If user is a new user, then a new user will be created
     * If not, user will be loaded
     * In both cases response is a User object
     * </p>
     *
     * @param context             Application {@link Context}
     * @param facebookAccessToken Access Token {@link String} received on successful Facebook login
     * @param facebookUserID      Facebook Application ID received on successful Facebook login
     * @param callback            {@link PDAPICallback} for API result
     * @deprecated use {@link #registerUserWithFacebook(String, String, PDAPICallback)} instead
     */
    @Deprecated
    public void registerUserWithFacebookAccessToken(@NonNull final Context context,
            @NonNull final String facebookAccessToken, @NonNull final String facebookUserID,
            @NonNull final PDAPICallback<PDUser> callback) {
        //        PDDataManager.setFacebookAccessTokenProperty(context, facebookAccessToken);

        Realm realm = Realm.getDefaultInstance();
        final PDRealmNonSocialUID uid = realm.where(PDRealmNonSocialUID.class).findFirst();
        final String uidString = uid == null ? null : uid.getUid();
        realm.close();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    URL url = new URL(PDAPIConfig.PD_API_ENDPOINT + PDAPIConfig.PD_USERS_PATH);
                    final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setDoOutput(true);
                    connection.setDoInput(true);
                    connection.setConnectTimeout(15000);
                    connection.setRequestMethod("POST");
                    connection.setRequestProperty(PDAPIConfig.REQUEST_HEADER_API_KEY,
                            PopdeemSDK.getPopdeemAPIKey());

                    ArrayList<AbstractMap.SimpleEntry<String, String>> params = new ArrayList<>();
                    params.add(new AbstractMap.SimpleEntry<>("user[facebook][access_token]", facebookAccessToken));
                    params.add(new AbstractMap.SimpleEntry<>("user[facebook][id]", facebookUserID));
                    if (uidString != null && !uidString.isEmpty()) {
                        params.add(new AbstractMap.SimpleEntry<>("user[unique_identifier]", uid.getUid()));
                    }

                    StringBuilder result = new StringBuilder();
                    boolean first = true;

                    for (AbstractMap.SimpleEntry<String, String> map : params) {
                        if (first) {
                            first = false;
                        } else {
                            result.append("&");
                        }

                        result.append(URLEncoder.encode(map.getKey(), "UTF-8"));
                        result.append("=");
                        result.append(URLEncoder.encode(map.getValue(), "UTF-8"));
                    }

                    OutputStream os = connection.getOutputStream();
                    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
                    writer.write(result.toString());
                    writer.flush();
                    writer.close();
                    os.close();

                    connection.connect();

                    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                    String inputLine;
                    final StringBuilder response = new StringBuilder();
                    while ((inputLine = reader.readLine()) != null) {
                        response.append(inputLine);
                    }
                    ((Activity) context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Gson gson = new GsonBuilder()
                                    .registerTypeAdapter(PDUser.class, new PDUserDeserializer()).create();
                            PDUser user = gson.fromJson(response.toString(), PDUser.class);
                            callback.success(user);
                        }
                    });
                    connection.disconnect();
                } catch (final IOException e) {
                    e.printStackTrace();
                    ((Activity) context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            callback.failure(400, e);
                        }
                    });
                }
            }
        }).start();
    }

    /**
     * Connects a user's Facebook account - if already registered with another social medium
     *
     * @param userID
     * @param accessToken
     */
    public void connectFacebookAccount(@NonNull long userID, @NonNull String accessToken,
            @NonNull final PDAPICallback<PDUser> callback) {
        JsonObject facebookObject = new JsonObject();
        facebookObject.addProperty("id", userID);
        facebookObject.addProperty("access_token", accessToken);

        JsonObject userObject = new JsonObject();
        userObject.add("facebook", facebookObject);

        JsonObject json = new JsonObject();
        json.add("user", userObject);

        TypedInput body = new TypedByteArray(PDAPIConfig.PD_JSON_MIME_TYPE, json.toString().getBytes());

        Gson gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDUserDeserializer()).create();

        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.connectFacebookAccount(body, callback);
    }

    /**
     * Connect a users Twitter account
     *
     * @param userID     Twitter User ID
     * @param userToken  Twitter User Token
     * @param userSecret Twitter User Secret
     * @param callback   {@link PDAPICallback} for API result
     */
    public void connectWithTwitterAccount(@NonNull String userID, @NonNull String userToken,
            @NonNull String userSecret, @NonNull final PDAPICallback<PDUser> callback) {
        JsonObject twitterObject = new JsonObject();
        twitterObject.addProperty("social_id", userID);
        twitterObject.addProperty("access_token", userToken);
        twitterObject.addProperty("access_secret", userSecret);

        JsonObject userJson = new JsonObject();
        userJson.add("twitter", twitterObject);

        JsonObject json = new JsonObject();
        json.add("user", userJson);

        TypedInput body = new TypedByteArray(PDAPIConfig.PD_JSON_MIME_TYPE, json.toString().getBytes());

        Gson gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDTwitterUserDeserializer()).create();

        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.connectWithTwitterAccount(body, callback);
    }

    /**
     * Connect a users Instagram Account
     *
     * @param userId      Instagram User ID
     * @param accessToken Instagram Access Token
     * @param screenName  Users Screen Name
     * @param callback    {@link PDAPICallback} for API result
     */
    public void connectWithInstagramAccount(@NonNull String userId, @NonNull String accessToken,
            @NonNull String screenName, @NonNull final PDAPICallback<PDUser> callback) {
        JsonObject instagramObject = new JsonObject();
        instagramObject.addProperty("id", userId);
        instagramObject.addProperty("access_token", accessToken);
        instagramObject.addProperty("screen_name", screenName);

        JsonObject userJson = new JsonObject();
        userJson.add("instagram", instagramObject);

        JsonObject json = new JsonObject();
        json.add("user", userJson);

        TypedInput body = new TypedByteArray(PDAPIConfig.PD_JSON_MIME_TYPE, json.toString().getBytes());
        Gson gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDInstagramUserDeserializer()).create();

        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.connectWithInstagramAccount(body, callback);
    }

    /**
     *
     * @param instagramId       Instagram User ID
     * @param accessToken       Instagram Access Token
     * @param fullname          User's Full Name
     * @param userName          User's Screen Name
     * @param profilePicture    User's Profile Picture
     * @param callback          {@link PDAPICallback} for API Result
     */
    public void registerWithInstagramId(@NonNull String instagramId, @NonNull String accessToken,
            @NonNull String fullname, @NonNull String userName, @NonNull String profilePicture,
            @NonNull final PDAPICallback<PDUser> callback) {
        Gson gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDInstagramUserDeserializer()).create();

        Realm realm = Realm.getDefaultInstance();
        final PDRealmNonSocialUID uid = realm.where(PDRealmNonSocialUID.class).findFirst();
        final String uidString = uid == null ? null : uid.getUid();
        realm.close();

        JsonObject jsonBody = new JsonObject();

        JsonObject userBody = new JsonObject();

        JsonObject instaBody = new JsonObject();
        instaBody.addProperty("id", instagramId);
        instaBody.addProperty("access_token", accessToken);
        instaBody.addProperty("full_name", fullname);
        instaBody.addProperty("profile_picture", profilePicture);

        userBody.add("instagram", instaBody);

        userBody.addProperty("unique_identifier", uidString);

        jsonBody.add("user", userBody);

        TypedInput body = new TypedByteArray(PDAPIConfig.PD_JSON_MIME_TYPE, jsonBody.toString().getBytes());
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));

        api.registerUserWithInstagram(body, callback);

    }

    /**
     * Check if users Instagram access token is still valid
     *
     * @param accessToken Instagram Access token to check
     * @param callback    {@link PDAPICallback} for API result
     */
    public void checkInstagramAccessToken(@NonNull String accessToken,
            @NonNull final PDAPICallback<Boolean> callback) {
        OkHttpClient httpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url(PDAPIConfig.PD_INSTAGRAM_PATH + "/users/self?access_token=" + accessToken).get().build();
        httpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callback.failure(400, e);
            }

            @Override
            public void onResponse(Call call, okhttp3.Response response) throws IOException {
                ResponseBody body = response.body();
                callback.success(response.code() == 200);
                body.close();
            }
        });
    }

    /**
     * Disconnect users Twitter account
     *
     * @param accessToken  Twitter access token
     * @param accessSecret Twitter access secret
     * @param twitterId    Users Twitter account ID
     * @param callback     {@link PDAPICallback} for API result
     */
    public void disconnectTwitterAccount(String accessToken, String accessSecret, String twitterId,
            @NonNull final PDAPICallback<PDUser> callback) {
        JsonObject twitterJson = new JsonObject();
        twitterJson.addProperty("access_token", accessToken);
        twitterJson.addProperty("access_secret", accessSecret);
        twitterJson.addProperty("id", twitterId);

        JsonObject userJson = new JsonObject();
        userJson.add("twitter", twitterJson);

        JsonObject jsonBody = new JsonObject();
        jsonBody.add("user", userJson);

        TypedInput body = new TypedByteArray(PDAPIConfig.PD_JSON_MIME_TYPE, jsonBody.toString().getBytes());

        Gson gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDTwitterUserDeserializer()).create();

        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.disconnectSocialAccount(body, callback);
    }

    /**
     * Disconnect users Instagram Account
     *
     * @param accessToken Instagram access token
     * @param instagramId Users Instagram account ID
     * @param screenName  Users Instagram screen name
     * @param callback    {@link PDAPICallback} for API result
     */
    public void disconnectInstagramAccount(String accessToken, String instagramId, String screenName,
            @NonNull final PDAPICallback<PDUser> callback) {
        JsonObject twitterJson = new JsonObject();
        twitterJson.addProperty("access_token", accessToken);
        twitterJson.addProperty("screen_name", screenName);
        twitterJson.addProperty("id", instagramId);

        JsonObject userJson = new JsonObject();
        userJson.add("instagram", twitterJson);

        JsonObject jsonBody = new JsonObject();
        jsonBody.add("user", userJson);

        TypedInput body = new TypedByteArray(PDAPIConfig.PD_JSON_MIME_TYPE, jsonBody.toString().getBytes());

        Gson gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDInstagramUserDeserializer()).create();

        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.disconnectSocialAccount(body, callback);
    }

    public void disconnectFacebookAccount(@NonNull final String facebookAccessToken,
            @NonNull final String facebookUserID, @NonNull final PDAPICallback<PDUser> callback) {
        JsonObject facebookObject = new JsonObject();
        facebookObject.addProperty("id", facebookUserID);
        facebookObject.addProperty("access_token", facebookAccessToken);

        JsonObject userObject = new JsonObject();
        userObject.add("facebook", facebookObject);

        JsonObject json = new JsonObject();
        json.add("user", userObject);

        TypedInput body = new TypedByteArray(PDAPIConfig.PD_JSON_MIME_TYPE, json.toString().getBytes());

        Gson gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDUserDeserializer()).create();

        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.disconnectSocialAccount(body, callback);
    }

    /**
     * Update the Users Location and Device token
     *
     * @param id          - User ID
     * @param deviceToken - Device token
     * @param latitude    - Current latitude for user
     * @param longitude   - Current longitude for user
     * @param callback    {@link PDAPICallback} for API result
     */
    public void updateUserLocationAndDeviceToken(@NonNull String socialType, @NonNull String id,
            @NonNull String deviceToken, @NonNull String latitude, @NonNull String longitude,
            @NonNull final PDAPICallback<PDUser> callback) {
        Gson gson;
        if (socialType.equalsIgnoreCase(PDSocialUtils.SOCIAL_TYPE_FACEBOOK)) {
            gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDUserDeserializer()).create();
        } else if (socialType.equalsIgnoreCase(PDSocialUtils.SOCIAL_TYPE_TWITTER)) {
            gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDTwitterUserDeserializer()).create();
        } else if (socialType.equalsIgnoreCase(PDSocialUtils.SOCIAL_TYPE_INSTAGRAM)) {
            gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDInstagramUserDeserializer()).create();
        } else {
            gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDUserDeserializer()).create();
        }

        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));

        // Realm Instance
        Realm realm = Realm.getDefaultInstance();

        // Non Social UID
        PDRealmNonSocialUID uidRealm = realm.where(PDRealmNonSocialUID.class).findFirst();
        String uid = uidRealm == null ? null : uidRealm.getUid();

        // Third Party Token
        PDRealmThirdPartyToken thirdPartyTokenRealm = realm.where(PDRealmThirdPartyToken.class).findFirst();
        String thirdPartToken = thirdPartyTokenRealm == null ? null : thirdPartyTokenRealm.getToken();

        // Check if we have a PDReferral
        PDRealmReferral referral = realm.where(PDRealmReferral.class).findFirst();
        if (referral != null) {
            // Referral present, send with referral
            api.updateUserLocationAndDeviceTokenWithReferral("", id, PDAPIConfig.PLATFORM_VALUE, deviceToken,
                    latitude, longitude, uid, String.valueOf(referral.getSenderId()), referral.getType(),
                    referral.getSenderAppName(), String.valueOf(referral.getRequestId()), thirdPartToken,
                    new PDAPICallback<PDUser>() {
                        @Override
                        public void success(PDUser user) {
                            Realm realm = Realm.getDefaultInstance();
                            PDRealmReferral referral = realm.where(PDRealmReferral.class).findFirst();
                            realm.beginTransaction();
                            referral.deleteFromRealm();
                            realm.commitTransaction();
                            realm.close();
                            callback.success(user);
                        }

                        @Override
                        public void failure(int statusCode, Exception e) {
                            callback.failure(statusCode, e);
                        }
                    });
        } else {
            // No referrals, send as normal
            api.updateUserLocationAndDeviceToken("", id, PDAPIConfig.PLATFORM_VALUE, deviceToken, latitude,
                    longitude, uid, thirdPartToken, callback);
        }

        // Close Realm
        realm.close();
    }

    /**
     * Update the Users Location and Device token.
     *
     * @param context     Application {@link Context}
     * @param id          User ID
     * @param deviceToken Device token
     * @param latitude    Current latitude for user
     * @param longitude   Current longitude for user
     * @param callback    {@link PDAPICallback} for API result
     * @deprecated use {@link #updateUserLocationAndDeviceToken(String, String, String, String, String, PDAPICallback)} instead.
     */
    @Deprecated
    public void updateUserLocationAndDeviceToken(@NonNull final Context context, @NonNull final String id,
            @NonNull final String deviceToken, @NonNull final String latitude, @NonNull final String longitude,
            @NonNull final PDAPICallback<String> callback) {
        Realm realm = Realm.getDefaultInstance();
        final PDRealmNonSocialUID uid = realm.where(PDRealmNonSocialUID.class).findFirst();
        final String uidString = uid == null ? null : uid.getUid();
        realm.close();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    URL url = new URL(PDAPIConfig.PD_API_ENDPOINT + PDAPIConfig.PD_USERS_PATH + "/" + id);
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setDoOutput(true);
                    connection.setDoInput(true);
                    connection.setRequestMethod("PUT");
                    connection.setConnectTimeout(15000);
                    connection.setRequestProperty(PDAPIConfig.REQUEST_HEADER_API_KEY,
                            PopdeemSDK.getPopdeemAPIKey());
                    connection.setRequestProperty(PDAPIConfig.REQUEST_HEADER_USER_TOKEN, PDUtils.getUserToken());

                    ArrayList<AbstractMap.SimpleEntry<String, String>> params = new ArrayList<>();
                    params.add(new AbstractMap.SimpleEntry<>("user[platform]", PDAPIConfig.PLATFORM_VALUE));
                    params.add(new AbstractMap.SimpleEntry<>("user[device_token]", deviceToken));
                    params.add(new AbstractMap.SimpleEntry<>("user[latitude]", latitude));
                    params.add(new AbstractMap.SimpleEntry<>("user[longitude]", longitude));
                    if (uidString != null && !uidString.isEmpty()) {
                        params.add(new AbstractMap.SimpleEntry<>("user[unique_identifier]", uid.getUid()));
                    }

                    StringBuilder result = new StringBuilder();
                    boolean first = true;

                    for (AbstractMap.SimpleEntry<String, String> map : params) {
                        if (first) {
                            first = false;
                        } else {
                            result.append("&");
                        }

                        result.append(URLEncoder.encode(map.getKey(), "UTF-8"));
                        result.append("=");
                        result.append(URLEncoder.encode(map.getValue(), "UTF-8"));
                    }

                    OutputStream os = connection.getOutputStream();
                    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
                    writer.write(result.toString());
                    writer.flush();
                    writer.close();
                    os.close();

                    connection.connect();

                    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                    String inputLine;
                    final StringBuilder response = new StringBuilder();

                    while ((inputLine = reader.readLine()) != null) {
                        response.append(inputLine);
                    }

                    callback.success(response.toString());
                    ((Activity) context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            callback.success(response.toString());
                        }
                    });
                } catch (final IOException e) {
                    e.printStackTrace();
                    ((Activity) context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            callback.failure(400, e);
                        }
                    });
                }
            }
        }).start();
    }

    /**
     * Get details for User ID
     * (Method will be public once it is tested)
     *  @param id       User ID
     * @param callback {@link PDAPICallback} for API result
     */
    public void getUserDetailsForId(@NonNull String id, @NonNull PDAPICallback<JsonObject> callback) {
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), null);
        api.getUserDetailsForId(id, callback);
    }

    /**
     * Register user with Twitter
     * (Method will be public once it is tested)
     *
     * @param twitterAccessToken  - Twitter access token
     * @param twitterAccessSecret - Twitter access  secret
     * @param twitterID           - Twitter ID
     * @param callback            {@link PDAPICallback} for API result
     */
    public void registerUserwithTwitterParams(@NonNull String twitterAccessToken,
            @NonNull String twitterAccessSecret, @NonNull String twitterID,
            @NonNull PDAPICallback<PDUser> callback) {

        Gson gson = new GsonBuilder().registerTypeAdapter(PDUser.class, new PDTwitterUserDeserializer()).create();

        Realm realm = Realm.getDefaultInstance();
        final PDRealmNonSocialUID uid = realm.where(PDRealmNonSocialUID.class).findFirst();
        final String uidString = uid == null ? null : uid.getUid();
        realm.close();

        JsonObject jsonBody = new JsonObject();

        JsonObject userBody = new JsonObject();

        JsonObject twitterBody = new JsonObject();
        twitterBody.addProperty("id", twitterID);
        twitterBody.addProperty("access_token", twitterAccessToken);
        twitterBody.addProperty("access_secret", twitterAccessSecret);

        userBody.add("twitter", twitterBody);

        userBody.addProperty("unique_identifier", uidString);

        jsonBody.add("user", userBody);

        TypedInput body = new TypedByteArray(PDAPIConfig.PD_JSON_MIME_TYPE, jsonBody.toString().getBytes());
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));

        api.registerUserWithTwitterParams(body, callback);
    }

    /**
     * Get Popdeem Friends
     * (Method will be public once it is tested)
     *
     * @param id
     * @param callback {@link PDAPICallback} for API result
     */
    private void getPopdeemFriends(@NonNull String id, @NonNull PDAPICallback<JsonObject> callback) {
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), null);
        api.getPopdeemFriends(id, callback);
    }

    /**
     * Get Customer details
     * (Method will be public once it is tested)
     *
     * @param callback {@link PDAPICallback} for API result
     */
    public void getCustomer(@NonNull PDAPICallback<JsonObject> callback) {
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), null);
        api.getCustomer(callback);
    }

    /**
     * Get Brands
     *
     * @param callback {@link PDAPICallback} for API result
     */
    public void getBrands(@NonNull PDAPICallback<ArrayList<PDBrand>> callback) {
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(PDBrandsDeserializer.BRANDS_TYPE, new PDBrandsDeserializer()).create();
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.getBrands(callback);
    }

    /**
     * @param brandID
     * @param callback {@link PDAPICallback} for API result
     */
    public void getRewardsForBrandID(@NonNull String brandID,
            @NonNull PDAPICallback<ArrayList<PDReward>> callback) {
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(PDRewardsDeserializer.REWARDS_TYPE, new PDRewardsDeserializer()).create();
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.getRewardsForBrandID(brandID, callback);
    }

    /**
     * Get all rewards
     * <p>
     * Request all rewards which are relevant to user.
     * </p>
     *
     * @param callback {@link PDAPICallback} for API result
     */
    public void getAllRewards(@NonNull final PDAPICallback<ArrayList<PDReward>> callback) {
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(PDRewardsDeserializer.REWARDS_TYPE, new PDRewardsDeserializer()).create();
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.getAllRewards(callback);
    }

    /**
     * Claim a reward and post social action
     *
     * @param context             - @NonNull Application {@link Context}
     * @param facebookAccessToken - @NonNull Facebook Access Token {@link String}
     * @param rewardId            - Reward ID {@link String}
     * @param message             - Message {@link String}
     * @param taggedFriendsNames  - {@link String} {@link ArrayList} of tagged Friends.
     * @param taggedFriendsIds    - {@link String} {@link ArrayList} of tagged Friend IDs.
     * @param image               - {@link android.util.Base64} encoded {@link String}. The image data must be Base64 encoded. If left null, message will only be posted.
     * @param longitude           - Current longitude for user
     * @param latitude            - Current latitude for user
     * @param callback            {@link PDAPICallback} for API result
     */
    public void claimReward(@NonNull final Context context, final String facebookAccessToken,
            final String twitterAuthToken, final String twitterSecret, final String instagramAccessToken,
            @NonNull final String rewardId, @NonNull final String message, ArrayList<String> taggedFriendsNames,
            ArrayList<String> taggedFriendsIds, final String image, @NonNull final String longitude,
            @NonNull final String latitude, @NonNull final PDAPICallback<JsonObject> callback) {
        final Handler mainHandler = new Handler(context.getMainLooper());
        final String url = PDAPIConfig.PD_API_ENDPOINT + PDAPIConfig.PD_REWARDS_PATH + "/" + rewardId + "/claim";

        final OkHttpClient client = new OkHttpClient();
        FormBody.Builder bodyBuilder = new FormBody.Builder().add("message", message)
                .add("location[latitude]", latitude).add("location[longitude]", longitude);

        if (facebookAccessToken != null) {
            bodyBuilder.add("facebook[access_token]", facebookAccessToken);
            if (taggedFriendsIds != null && taggedFriendsNames != null
                    && taggedFriendsIds.size() == taggedFriendsNames.size() && taggedFriendsIds.size() > 0
                    && taggedFriendsNames.size() > 0) {
                for (int i = 0; i < taggedFriendsIds.size(); i++) {
                    String name = taggedFriendsNames.get(i);
                    String id = taggedFriendsIds.get(i);

                    bodyBuilder.add("facebook[associated_account_ids][][name]", name);
                    bodyBuilder.add("facebook[associated_account_ids][][id]", id);
                }
            }
        }
        if (twitterAuthToken != null) {
            bodyBuilder.add("twitter[access_token]", twitterAuthToken);
            bodyBuilder.add("twitter[access_secret]", twitterSecret);
        }
        if (instagramAccessToken != null) {
            bodyBuilder.add("instagram[access_token]", instagramAccessToken);
        }

        if (image != null) {
            bodyBuilder.add("file", image);
        }

        RequestBody body = bodyBuilder.build();

        final Request request = new Request.Builder().url(url)
                .addHeader(PDAPIConfig.REQUEST_HEADER_API_KEY, PopdeemSDK.getPopdeemAPIKey())
                .addHeader(PDAPIConfig.REQUEST_HEADER_USER_TOKEN, PDUtils.getUserToken()).post(body).build();

        client.newCall(request).enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callback.failure(RetrofitError.networkError(url, new IOException("Unexpected code " + e)));
            }

            @Override
            public void onResponse(Call call, final okhttp3.Response response) throws IOException {
                // Convert OkHTTP response to Retrofit Response
                final String responseBody = response.body().string();
                final JsonElement object = new JsonParser().parse(responseBody);
                final TypedInput bodyTypedInput = new TypedString(responseBody);
                final ArrayList<Header> headers = new ArrayList<>();

                Headers responseHeaders = response.headers();
                for (int i = 0; i < responseHeaders.size(); i++) {
                    headers.add(new Header(responseHeaders.name(i), responseHeaders.value(i)));
                }

                mainHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        callback.success(object.isJsonNull() ? null : object.getAsJsonObject(),
                                new Response(response.request().url().toString(), response.networkResponse().code(),
                                        response.networkResponse().message(), headers, bodyTypedInput));
                    }
                });
            }
        });
    }

    /**
     * Redeem Reward
     * <p>
     * Redeem a reward in the wallet
     * </p>
     *
     * @param rewardId - Reward ID
     * @param callback {@link PDAPICallback} for API result
     */
    public void redeemReward(String rewardId, @NonNull PDAPICallback<JsonObject> callback) {
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), null);
        api.redeemReward("", rewardId, callback);
    }

    /**
     * Get rewards currently in wallet
     * <p>
     * Request all rewards which are relevant to user.
     * </p>
     *
     * @param callback {@link PDAPICallback} for API result
     */
    public void getRewardsInWallet(@NonNull final PDAPICallback<JsonObject> callback) {
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(PDRewardsDeserializer.REWARDS_TYPE, new PDRewardsDeserializer()).create();
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.getRewardsInWallet(callback);
    }
    // public void getRewardsInWallet(@NonNull final PDAPICallback<ArrayList<PDReward>> callback) {
    //        Gson gson = new GsonBuilder()
    //                .registerTypeAdapter(PDRewardsDeserializer.REWARDS_TYPE, new PDRewardsDeserializer())
    //                .create();
    //        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
    //        api.getRewardsInWallet(callback);
    //    }

    /**
     * Verify that a user posted to Instagram to claim a reward.
     *
     * @param rewardId Reward ID to verify against
     * @param callback {@link PDAPICallback} for API result
     */
    public void verifyInstagramPostForReward(@NonNull String rewardId,
            @NonNull final PDAPICallback<JsonObject> callback) {
        Realm realm = Realm.getDefaultInstance();
        PDRealmUserDetails userDetails = realm.where(PDRealmUserDetails.class).findFirst();
        final String accessToken = userDetails.getUserInstagram().getAccessToken();
        realm.close();

        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), null);
        api.verifyInstagramPostForReward("", accessToken, rewardId, callback);
    }

    /**
     * Get Popdeem Messages
     *
     * @param callback {@link PDAPICallback} for API result
     */
    public void getPopdeemMessages(@NonNull PDAPICallback<ArrayList<PDMessage>> callback) {
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(PDMessagesDeserializer.MESSAGES_TYPE, new PDMessagesDeserializer()).create();
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.getPopdeemMessages(callback);
    }

    /**
     * Mark a message as read.
     *
     * @param messageId ID of message to mark as read
     * @param callback  {@link PDAPICallback} for API result
     */
    public void markMessageAsRead(String messageId, @NonNull PDAPICallback<PDBasicResponse> callback) {
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), null);
        api.markMessageAsRead("", messageId, callback);
    }

    /**
     * Get Feeds
     *
     * @param callback {@link PDAPICallback} for API result
     */
    public void getFeeds(@NonNull PDAPICallback<ArrayList<PDFeed>> callback) {
        Gson gson = new GsonBuilder().registerTypeAdapter(PDFeedsDeserializer.FEEDS_TYPE, new PDFeedsDeserializer())
                .create();
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), new GsonConverter(gson));
        api.getFeeds(callback);
    }

    /**
     * Deliver a custom action trigger for an event
     *
     * @param moment   Action configured on backend
     * @param callback {@link PDAPICallback} for API result
     */
    public void logMoment(@NonNull String moment, @NonNull PDAPICallback<PDBasicResponse> callback) {
        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), null);
        api.logMoment("", moment, callback);
    }

    /**
     * Get User Token Interceptor
     *
     * @return Interceptor with User Token Header or null if no token is saved.
     */
    private Interceptor getUserTokenInterceptor() {
        final String userToken = PDUtils.getUserToken();
        Log.i("User Token", "getUserTokenInterceptor: " + userToken);
        if (userToken == null) {
            return null;
        }

        return new Interceptor() {
            @Override
            public okhttp3.Response intercept(Chain chain) throws IOException {
                return chain.proceed(chain.request().newBuilder()
                        .addHeader(PDAPIConfig.REQUEST_HEADER_USER_TOKEN, userToken).build());
            }
        };
    }

    /**
     * Get Popdeem API interface
     *
     * @param interceptor - {@link Interceptor} used for request
     * @param converter   - {@link Converter} users for request response (optional)
     * @return - {@link PopdeemAPI} used for interacting with API
     */
    private PopdeemAPI getApiInterface(Interceptor interceptor, Converter converter) {
        RestAdapter restAdapter = buildRestAdapter(interceptor, converter);
        return restAdapter.create(PopdeemAPI.class);
    }

    /**
     * Build RestAdapter for request
     *
     * @param interceptor - {@link Interceptor} for request (optional)
     * @param converter   - {@link Converter} for Request (optional)
     * @return - {@link RestAdapter} used to create {@link PopdeemAPI}
     */
    private RestAdapter buildRestAdapter(Interceptor interceptor, Converter converter) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder().readTimeout(30, TimeUnit.SECONDS)
                .connectTimeout(30, TimeUnit.SECONDS).addInterceptor(PD_API_KEY_INTERCEPTOR);

        if (interceptor != null) {
            builder.addInterceptor(interceptor);
        }

        OkHttpClient okHttpClient = builder.build();

        RestAdapter.Builder adapterBuilder = new RestAdapter.Builder().setClient(new Ok3Client(okHttpClient))
                .setEndpoint(PDAPIConfig.PD_API_ENDPOINT).setLogLevel(RestAdapter.LogLevel.FULL);

        if (converter != null) {
            adapterBuilder.setConverter(converter);
        }

        return adapterBuilder.build();
    }

    /**
     * Background Scan
     *
     * @param rewardID - id of reward user is scanning for
     * @param network  - String of the network the user is scanning (facebook, instagram, twitter)
     * @param callback {@link PDAPICallback} for API result
     */
    public void scanSocialNetwork(String rewardID, String network,
            @NonNull final PDAPICallback<JsonObject> callback) {
        JsonObject jsonBody = new JsonObject();
        jsonBody.addProperty("network", network);

        TypedInput body = new TypedByteArray(PDAPIConfig.PD_JSON_MIME_TYPE, jsonBody.toString().getBytes());

        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), null);
        api.scanSocialNetwork(body, rewardID, callback);
    }

    /**
     * Claim Discovery
     */
    public void claimDiscovery(PDBGScanResponseModel post, String facebookAccessToken, String twitterAccessToken,
            String twitterAccessSecret, String instagramAccessToken, String latitude, String longitude,
            String locationID, String rewardID, Context context,
            @NonNull final PDAPICallback<JsonObject> callback) {

        JsonObject jsonBody = new JsonObject();

        //message
        jsonBody.addProperty("message", post.getText());

        //file
        jsonBody.addProperty("file", post.getMediaUrl());

        //post_key
        jsonBody.addProperty("post_key", post.getObjectID());

        //social network keys
        if (post.getNetwork().equalsIgnoreCase("facebook")) {
            JsonObject facebookObject = new JsonObject();
            facebookObject.addProperty("access_token", facebookAccessToken);
            jsonBody.add("facebook", facebookObject);
        } else if (post.getNetwork().equalsIgnoreCase("twitter")) {
            JsonObject twitterObject = new JsonObject();
            twitterObject.addProperty("access_token", twitterAccessToken);
            twitterObject.addProperty("access_secret", twitterAccessSecret);
            jsonBody.add("twitter", twitterObject);
        } else if (post.getNetwork().equalsIgnoreCase("instagram")) {
            JsonObject instagramObject = new JsonObject();
            instagramObject.addProperty("access_token", instagramAccessToken);
            jsonBody.add("instagram", instagramObject);
        }

        //location
        JsonObject locationObject = new JsonObject();
        locationObject.addProperty("latitude", latitude);
        locationObject.addProperty("longitude", longitude);
        locationObject.addProperty("id", locationID);

        jsonBody.add("location", locationObject);

        TypedInput body = new TypedByteArray(PDAPIConfig.PD_JSON_MIME_TYPE, jsonBody.toString().getBytes());

        PopdeemAPI api = getApiInterface(getUserTokenInterceptor(), null);
        api.claimDiscovery(body, rewardID, callback);
    }
}