com.github.gorbin.asne.instagram.InstagramSocialNetwork.java Source code

Java tutorial

Introduction

Here is the source code for com.github.gorbin.asne.instagram.InstagramSocialNetwork.java

Source

/*******************************************************************************
 * Copyright (c) 2014 Evgeny Gorbin
 *
 * 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.github.gorbin.asne.instagram;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.TextUtils;

import com.github.gorbin.asne.core.AccessToken;
import com.github.gorbin.asne.core.OAuthActivity;
import com.github.gorbin.asne.core.OAuthSocialNetwork;
import com.github.gorbin.asne.core.SocialNetworkAsyncTask;
import com.github.gorbin.asne.core.SocialNetworkException;
import com.github.gorbin.asne.core.listener.OnCheckIsFriendCompleteListener;
import com.github.gorbin.asne.core.listener.OnLoginCompleteListener;
import com.github.gorbin.asne.core.listener.OnPostingCompleteListener;
import com.github.gorbin.asne.core.listener.OnRequestAccessTokenCompleteListener;
import com.github.gorbin.asne.core.listener.OnRequestAddFriendCompleteListener;
import com.github.gorbin.asne.core.listener.OnRequestDetailedSocialPersonCompleteListener;
import com.github.gorbin.asne.core.listener.OnRequestGetFriendsCompleteListener;
import com.github.gorbin.asne.core.listener.OnRequestRemoveFriendCompleteListener;
import com.github.gorbin.asne.core.listener.OnRequestSocialPersonCompleteListener;
import com.github.gorbin.asne.core.listener.OnRequestSocialPersonsCompleteListener;
import com.github.gorbin.asne.core.persons.SocialPerson;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.UUID;

import javax.net.ssl.HttpsURLConnection;

/**
 * Class for Instagram social network integration
 *
 * @author Evgeny Gorbin (gorbin.e.o@gmail.com)
 */
public class InstagramSocialNetwork extends OAuthSocialNetwork {
    /*** Social network ID in asne modules, should be unique*/
    public static final int ID = 7;
    private static final String SAVE_STATE_KEY_OAUTH_TOKEN = "InstagramSocialNetwork.SAVE_STATE_KEY_OAUTH_TOKEN";
    private static final String SAVE_STATE_KEY_OAUTH_REQUEST_TOKEN = "InstagramSocialNetwork.SAVE_STATE_KEY_OAUTH_SECRET";
    // max 16 bit to use in startActivityForResult
    private static final int REQUEST_AUTH = UUID.randomUUID().hashCode() & 0xFFFF;
    private static final String INSTAGRAM_TOKENURL = "https://api.instagram.com/oauth/access_token";
    private static final String INSTAGRAM_APIURL = "https://api.instagram.com/v1";
    private static final String ERROR_CODE = "InstagramSocialNetwork.ERROR_CODE";
    //    private final String INSTAGRAM_CALLBACK_URL = "oauth://ASNE";
    private String mAuthURLString;
    private String mTokenURLString;
    private String mClientId;
    private String mClientSecret;
    private String mRedirectURL;
    private boolean mRestart = false;
    private Bundle mRequestBundle;

    //TODO: refactor to use an init that is shared by constructors
    public InstagramSocialNetwork(Fragment fragment, String clientId, String clientSecret, String redirectURL,
            String scope) {
        super(fragment);

        this.mClientId = clientId;
        this.mClientSecret = clientSecret;
        this.mRedirectURL = redirectURL;

        if (TextUtils.isEmpty(clientId) || TextUtils.isEmpty(clientSecret)) {
            throw new IllegalArgumentException("clientId and clientSecret are invalid");
        }
        if (scope == null) {
            scope = "basic";
        }
        String INSTAGRAM_AUTHURL = "https://api.instagram.com/oauth/authorize/";
        mAuthURLString = INSTAGRAM_AUTHURL + "?client_id=" + clientId + "&redirect_uri=" + redirectURL
                + "&response_type=code&display=touch&scope=" + scope;

        mTokenURLString = INSTAGRAM_TOKENURL + "?client_id=" + clientId + "&client_secret=" + clientSecret
                + "&redirect_uri=" + redirectURL + "&grant_type=authorization_code";
    }

    //    public InstagramSocialNetwork(Fragment fragment, Context context, String clientId, String clientSecret, String redirectURL, String scope) {
    //        super(fragment, context);
    //
    //        this.mClientId = clientId;
    //        this.mClientSecret = clientSecret;
    //        this.mRedirectURL = redirectURL;
    //
    //        if (TextUtils.isEmpty(clientId) || TextUtils.isEmpty(clientSecret)) {
    //            throw new IllegalArgumentException("clientId and clientSecret are invalid");
    //        }
    //        if(scope == null) {
    //            scope = "basic";
    //        }
    //        String INSTAGRAM_AUTHURL = "https://api.instagram.com/oauth/authorize/";
    //        authURLString = INSTAGRAM_AUTHURL + "?client_id=" + clientId + "&redirect_uri="
    //                + redirectURL + "&response_type=code&display=touch&scope=" + scope;
    //
    //        tokenURLString = INSTAGRAM_TOKENURL + "?client_id=" + clientId + "&client_secret="
    //                + clientSecret + "&redirect_uri=" + redirectURL + "&grant_type=authorization_code";
    //    }

    /**
     * Check is social network connected
     * @return true if connected to Instagram and false if not
     */
    @Override
    public boolean isConnected() {
        String accessToken = mSharedPreferences.getString(SAVE_STATE_KEY_OAUTH_TOKEN, null);
        String requestToken = mSharedPreferences.getString(SAVE_STATE_KEY_OAUTH_REQUEST_TOKEN, null);
        return accessToken != null && requestToken != null;
    }

    /**
     * Make login request - authorize in Instagram social network
     * @param onLoginCompleteListener listener to trigger when Login complete
     */
    @Override
    public void requestLogin(OnLoginCompleteListener onLoginCompleteListener) {
        super.requestLogin(onLoginCompleteListener);
        initInstagramLogin();
    }

    private void initInstagramLogin() {
        Intent intent = new Intent(mSocialNetworkManager.getActivity(), OAuthActivity.class)
                .putExtra(OAuthActivity.PARAM_CALLBACK, mRedirectURL)
                .putExtra(OAuthActivity.PARAM_URL_TO_LOAD, mAuthURLString);
        mSocialNetworkManager.getActivity().startActivityForResult(intent, REQUEST_AUTH);
    }

    /**
     * Logout from Instagram social network
     */
    @Override
    public void logout() {
        mSharedPreferences.edit().remove(SAVE_STATE_KEY_OAUTH_TOKEN).remove(SAVE_STATE_KEY_OAUTH_REQUEST_TOKEN)
                .apply();
    }

    /**
     * Get id of Instagram social network
     * @return Social network id for Instagram = 7
     */
    @Override
    public int getID() {
        return ID;
    }

    /**
     * Method to get AccessToken of Instagram social network
     * @return {@link com.github.gorbin.asne.core.AccessToken}
     */
    @Override
    public AccessToken getAccessToken() {
        return new AccessToken(mSharedPreferences.getString(SAVE_STATE_KEY_OAUTH_TOKEN, null), null);
    }

    /**
     * Request {@link com.github.gorbin.asne.core.AccessToken} of Instagram social network that you can get from onRequestAccessTokenCompleteListener
     * @param onRequestAccessTokenCompleteListener listener for {@link com.github.gorbin.asne.core.AccessToken} request
     */
    @Override
    public void requestAccessToken(OnRequestAccessTokenCompleteListener onRequestAccessTokenCompleteListener) {
        super.requestAccessToken(onRequestAccessTokenCompleteListener);
        ((OnRequestAccessTokenCompleteListener) mLocalListeners.get(REQUEST_ACCESS_TOKEN))
                .onRequestAccessTokenComplete(getID(),
                        new AccessToken(mSharedPreferences.getString(SAVE_STATE_KEY_OAUTH_TOKEN, null), null));
    }

    /**
     * Request current user {@link com.github.gorbin.asne.core.persons.SocialPerson}
     * @param onRequestSocialPersonCompleteListener listener for {@link com.github.gorbin.asne.core.persons.SocialPerson} request
     */
    @Override
    public void requestCurrentPerson(OnRequestSocialPersonCompleteListener onRequestSocialPersonCompleteListener) {
        super.requestCurrentPerson(onRequestSocialPersonCompleteListener);
        executeRequest(new RequestGetSocialPersonAsyncTask(), null, REQUEST_GET_CURRENT_PERSON);
    }

    /**
     * Request {@link com.github.gorbin.asne.core.persons.SocialPerson} by user id
     * @param userID id of Instagram user
     * @param onRequestSocialPersonCompleteListener listener for {@link com.github.gorbin.asne.core.persons.SocialPerson} request
     */
    @Override
    public void requestSocialPerson(String userID,
            OnRequestSocialPersonCompleteListener onRequestSocialPersonCompleteListener) {
        super.requestSocialPerson(userID, onRequestSocialPersonCompleteListener);
        if (TextUtils.isEmpty(userID)) {
            throw new SocialNetworkException("userID can't be null or empty");
        }
        Bundle args = new Bundle();
        args.putString(RequestGetSocialPersonAsyncTask.PARAM_USER_ID, userID);
        executeRequest(new RequestGetSocialPersonAsyncTask(), args, REQUEST_GET_PERSON);
    }

    /**
     * Request ArrayList of {@link com.github.gorbin.asne.core.persons.SocialPerson} by array of userIds
     * @param userID array of Instagram users id
     * @param onRequestSocialPersonsCompleteListener listener for array of {@link com.github.gorbin.asne.core.persons.SocialPerson} request
     */
    @Override
    public void requestSocialPersons(String[] userID,
            OnRequestSocialPersonsCompleteListener onRequestSocialPersonsCompleteListener) {
        super.requestSocialPersons(userID, onRequestSocialPersonsCompleteListener);
        Bundle args = new Bundle();
        args.putStringArray(RequestSocialPersonsAsyncTask.PARAM_USER_ID, userID);
        executeRequest(new RequestSocialPersonsAsyncTask(), args, REQUEST_GET_PERSONS);
    }

    /**
     * Request user {@link com.github.gorbin.asne.instagram.InstagramPerson} by userId - detailed user data
     * @param userId id of Instagram user
     * @param onRequestDetailedSocialPersonCompleteListener listener for {@link com.github.gorbin.asne.instagram.InstagramPerson} request
     */
    @Override
    public void requestDetailedSocialPerson(String userId,
            OnRequestDetailedSocialPersonCompleteListener onRequestDetailedSocialPersonCompleteListener) {
        super.requestDetailedSocialPerson(userId, onRequestDetailedSocialPersonCompleteListener);
        Bundle args = new Bundle();
        if (userId != null) {
            args.putString(RequestGetDetailedPersonAsyncTask.PARAM_USER_ID, userId);
        } else {
            args.putString(RequestGetDetailedPersonAsyncTask.PARAM_USER_ID, "self");
        }
        executeRequest(new RequestGetDetailedPersonAsyncTask(), args, REQUEST_GET_DETAIL_PERSON);
    }

    private SocialPerson getSocialPerson(SocialPerson socialPerson, JSONObject jsonResponse) throws JSONException {
        if (jsonResponse.has("id")) {
            socialPerson.id = jsonResponse.getString("id");
        }
        if (jsonResponse.has("username")) {
            socialPerson.name = jsonResponse.getString("username");
            socialPerson.profileURL = "http://www.instagram.com/" + jsonResponse.getString("username");
        }
        if (jsonResponse.has("profile_picture")) {
            socialPerson.avatarURL = jsonResponse.getString("profile_picture");
        }
        return socialPerson;
    }

    private InstagramPerson getDetailedSocialPerson(InstagramPerson instagramPerson, JSONObject jsonResponse)
            throws JSONException {
        getSocialPerson(instagramPerson, jsonResponse);
        if (jsonResponse.has("bio")) {
            instagramPerson.bio = jsonResponse.getString("bio");
        }
        if (jsonResponse.has("website")) {
            instagramPerson.website = jsonResponse.getString("website");
        }
        if (jsonResponse.has("full_name")) {
            instagramPerson.fullName = jsonResponse.getString("full_name");
        }
        if (jsonResponse.has("counts")) {
            if (jsonResponse.getJSONObject("counts").has("media")) {
                instagramPerson.media = jsonResponse.getJSONObject("counts").getInt("media");
            }
            if (jsonResponse.getJSONObject("counts").has("followed_by")) {
                instagramPerson.followedBy = jsonResponse.getJSONObject("counts").getInt("followed_by");
            }
            if (jsonResponse.getJSONObject("counts").has("follows")) {
                instagramPerson.follows = jsonResponse.getJSONObject("counts").getInt("follows");
            }
        }
        return instagramPerson;
    }

    /**
     * Post message to social network
     * @param message  message that should be shared
     * @param onPostingCompleteListener listener for posting request
     */
    @Override
    public void requestPostMessage(String message, OnPostingCompleteListener onPostingCompleteListener) {
        super.requestPostMessage(message, onPostingCompleteListener);
        throw new SocialNetworkException("requestPostMessage isn't allowed for InstagramSocialNetwork");
    }

    /**
     * Post photo to social network
     * @param photo photo that should be shared
     * @param message message that should be shared with photo
     * @param onPostingCompleteListener listener for posting request
     */
    @Override
    public void requestPostPhoto(File photo, String message, OnPostingCompleteListener onPostingCompleteListener) {
        super.requestPostPhoto(photo, message, onPostingCompleteListener);
        String instagramPackage = "com.instagram.android";
        String errorMessage = "You should install Instagram app first";
        if (isPackageInstalled(instagramPackage, mSocialNetworkManager.getActivity())) {
            Intent normalIntent = new Intent(Intent.ACTION_SEND);
            normalIntent.setType("image/*");
            normalIntent.setPackage(instagramPackage);
            File media = new File(photo.getAbsolutePath());
            Uri uri = Uri.fromFile(media);
            normalIntent.putExtra(Intent.EXTRA_STREAM, uri);
            normalIntent.putExtra(Intent.EXTRA_TEXT, message);
            mSocialNetworkManager.getActivity().startActivity(normalIntent);
        } else {
            mLocalListeners.get(REQUEST_POST_PHOTO).onError(getID(), REQUEST_POST_PHOTO, errorMessage, null);
        }
        mLocalListeners.remove(REQUEST_POST_PHOTO);
    }

    /**
     * Not supported via Instagram api.
     * @throws com.github.gorbin.asne.core.SocialNetworkException
     * @param bundle bundle containing information that should be shared(Bundle constants in {@link com.github.gorbin.asne.core.SocialNetwork})
     * @param message message that should be shared with bundle
     * @param onPostingCompleteListener listener for posting request
     */
    @Override
    public void requestPostLink(Bundle bundle, String message,
            OnPostingCompleteListener onPostingCompleteListener) {
        throw new SocialNetworkException("requestPostLink isn't allowed for InstagramSocialNetwork");
    }

    /**
     * Not supported via Instagram api.
     * @throws com.github.gorbin.asne.core.SocialNetworkException
     * @param bundle bundle containing information that should be shared(Bundle constants in {@link com.github.gorbin.asne.core.SocialNetwork})
     * @param onPostingCompleteListener listener for posting request
     */
    @Override
    public void requestPostDialog(Bundle bundle, OnPostingCompleteListener onPostingCompleteListener) {
        throw new SocialNetworkException("requestPostDialog isn't allowed for InstagramSocialNetwork");
    }

    /**
     * Check if user by id is friend of current user
     * @param userID user id that should be checked as friend of current user
     * @param onCheckIsFriendCompleteListener listener for checking friend request
     */
    @Override
    public void requestCheckIsFriend(String userID,
            OnCheckIsFriendCompleteListener onCheckIsFriendCompleteListener) {
        super.requestCheckIsFriend(userID, onCheckIsFriendCompleteListener);
        Bundle args = new Bundle();
        args.putString(RequestCheckIsFriendAsyncTask.PARAM_USER_ID, userID);
        executeRequest(new RequestCheckIsFriendAsyncTask(), args, REQUEST_CHECK_IS_FRIEND);
    }

    /**
     * Get current user friends list
     * @param onRequestGetFriendsCompleteListener listener for getting list of current user friends
     */
    @Override
    public void requestGetFriends(OnRequestGetFriendsCompleteListener onRequestGetFriendsCompleteListener) {
        super.requestGetFriends(onRequestGetFriendsCompleteListener);
        executeRequest(new RequestGetFriendsAsyncTask(), null, REQUEST_GET_FRIENDS);
    }

    /**
     * Invite friend by id to current user
     * @param userID id of user that should be invited
     * @param onRequestAddFriendCompleteListener listener for invite result
     */
    @Override
    public void requestAddFriend(String userID,
            OnRequestAddFriendCompleteListener onRequestAddFriendCompleteListener) {
        super.requestAddFriend(userID, onRequestAddFriendCompleteListener);
        Bundle args = new Bundle();
        args.putString(RequestAddFriendAsyncTask.PARAM_USER_ID, userID);
        executeRequest(new RequestAddFriendAsyncTask(), args, REQUEST_ADD_FRIEND);
    }

    /**
     * Remove friend by id from current user friends
     * @param userID user id that should be removed from friends
     * @param onRequestRemoveFriendCompleteListener listener to remove friend request response
     */
    @Override
    public void requestRemoveFriend(String userID,
            OnRequestRemoveFriendCompleteListener onRequestRemoveFriendCompleteListener) {
        super.requestRemoveFriend(userID, onRequestRemoveFriendCompleteListener);
        Bundle args = new Bundle();
        args.putString(RequestAddFriendAsyncTask.PARAM_USER_ID, userID);
        executeRequest(new RequestRemoveFriendAsyncTask(), args, REQUEST_REMOVE_FRIEND);
    }

    /**
     * Overrided for Instagram support
     * @param requestCode The integer request code originally supplied to startActivityForResult(), allowing you to identify who this result came from.
     * @param resultCode The integer result code returned by the child activity through its setResult().
     * @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
     */
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        int sanitizedRequestCode = requestCode & 0xFFFF;
        if (sanitizedRequestCode != REQUEST_AUTH)
            return;
        super.onActivityResult(requestCode, resultCode, data);

        Uri uri = data != null ? data.getData() : null;

        if (uri != null && uri.toString().startsWith(mRedirectURL)) {
            String parts[] = uri.toString().split("=");
            String verifier = parts[1];
            RequestLogin2AsyncTask requestLogin2AsyncTask = new RequestLogin2AsyncTask();
            mRequests.put(REQUEST_LOGIN2, requestLogin2AsyncTask);
            Bundle args = new Bundle();
            args.putString(RequestLogin2AsyncTask.PARAM_VERIFIER, verifier);
            requestLogin2AsyncTask.execute(args);
        } else {
            if (mLocalListeners.get(REQUEST_LOGIN) != null) {
                mLocalListeners.get(REQUEST_LOGIN).onError(getID(), REQUEST_LOGIN, "incorrect URI returned: " + uri,
                        null);
                mLocalListeners.remove(REQUEST_LOGIN);
            }
        }
    }

    /**
     * Cancel login request
     */
    @Override
    public void cancelLoginRequest() {
        super.cancelLoginRequest();
    }

    private String streamToString(InputStream is) {
        try {
            StringBuilder outString = new StringBuilder();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            String read = reader.readLine();
            while (read != null) {
                outString.append(read);
                read = reader.readLine();
            }
            return outString.toString();
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    private String checkInputStream(HttpURLConnection connection) {
        String errorType = null, code = null, errorMessage = null;
        InputStream inputStream = connection.getErrorStream();
        String response = streamToString(inputStream);
        try {
            JSONObject jsonObject = (JSONObject) new JSONTokener(response).nextValue();
            JSONObject jsonResponse = jsonObject.getJSONObject("meta");
            if (jsonResponse.has("error_type")) {
                errorType = jsonResponse.getString("error_type");
            }
            if (jsonResponse.has("code")) {
                code = jsonResponse.getString("code");
            }
            if (jsonResponse.has("error_message")) {
                errorMessage = jsonResponse.getString("error_message");
            }
            return "ERROR TYPE: " + errorType + " ERROR CODE: " + code + " ERROR MESSAGE: " + errorMessage;
        } catch (JSONException e) {
            return e.getMessage();
        }
    }

    private void checkConnectionErrors(HttpURLConnection connection) throws Exception {
        if (connection.getResponseCode() >= 400) {
            throw new Exception(checkInputStream(connection));
        }
    }

    private boolean checkTokenError(Bundle result) {
        if (result != null && result.containsKey(ERROR_CODE) && result.getString(ERROR_CODE).contains("400")
                && result.getString(ERROR_CODE).contains("OAuth")) {
            mRestart = true;
            mRequestBundle = result;
            mRequestBundle.remove(ERROR_CODE);
            mRequestBundle.remove(SocialNetworkAsyncTask.RESULT_ERROR);
            initInstagramLogin();
            return true;
        }
        return false;
    }

    private boolean checkRequests() {
        boolean queryRequests = false;
        for (String request : mRequests.keySet()) {
            if (request.equals(REQUEST_LOGIN)) {
                break;
            }
            queryRequests = true;
        }
        return queryRequests;
    }

    private void checkException(Exception e, Bundle result) {
        if (e.getMessage().contains("ERROR CODE") && e.getMessage().contains("OAuth")) {
            result.putString(ERROR_CODE, e.getMessage());
        } else {
            result.putString(SocialNetworkAsyncTask.RESULT_ERROR, e.getMessage());
        }
    }

    private void restartRequests() {
        mRestart = false;
        if (mLocalListeners.containsKey(REQUEST_GET_CURRENT_PERSON)) {
            mRequests.remove(REQUEST_GET_CURRENT_PERSON);
            executeRequest(new RequestGetSocialPersonAsyncTask(), mRequestBundle, REQUEST_GET_CURRENT_PERSON);
        } else if (mLocalListeners.containsKey(REQUEST_GET_PERSON)) {
            mRequests.remove(REQUEST_GET_PERSON);
            executeRequest(new RequestGetSocialPersonAsyncTask(), mRequestBundle, REQUEST_GET_PERSON);
        } else if (mLocalListeners.containsKey(REQUEST_GET_DETAIL_PERSON)) {
            mRequests.remove(REQUEST_GET_DETAIL_PERSON);
            executeRequest(new RequestGetDetailedPersonAsyncTask(), mRequestBundle, REQUEST_GET_DETAIL_PERSON);
        } else if (mLocalListeners.containsKey(REQUEST_GET_PERSONS)) {
            mRequests.remove(REQUEST_GET_PERSONS);
            executeRequest(new RequestSocialPersonsAsyncTask(), mRequestBundle, REQUEST_GET_PERSONS);
        } else if (mRequests.containsKey(REQUEST_CHECK_IS_FRIEND)) {
            mRequests.remove(REQUEST_CHECK_IS_FRIEND);
            executeRequest(new RequestCheckIsFriendAsyncTask(), mRequestBundle, REQUEST_CHECK_IS_FRIEND);
        } else if (mRequests.containsKey(REQUEST_GET_FRIENDS)) {
            mRequests.remove(REQUEST_GET_FRIENDS);
            executeRequest(new RequestGetFriendsAsyncTask(), mRequestBundle, REQUEST_GET_FRIENDS);
        } else if (mRequests.containsKey(REQUEST_ADD_FRIEND)) {
            mRequests.remove(REQUEST_ADD_FRIEND);
            executeRequest(new RequestAddFriendAsyncTask(), mRequestBundle, REQUEST_ADD_FRIEND);
        } else if (mRequests.containsKey(REQUEST_REMOVE_FRIEND)) {
            mRequests.remove(REQUEST_REMOVE_FRIEND);
            executeRequest(new RequestGetFriendsAsyncTask(), mRequestBundle, REQUEST_REMOVE_FRIEND);
        }
    }

    private boolean isPackageInstalled(String packagename, Context context) {
        PackageManager pm = context.getPackageManager();
        try {
            pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
            return true;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    private class RequestLogin2AsyncTask extends SocialNetworkAsyncTask {
        public static final String PARAM_VERIFIER = "Login2AsyncTask.PARAM_VERIFIER";

        private static final String RESULT_ACCESS_TOKEN = "Login2AsyncTask.RESULT_TOKEN";
        private static final String RESULT_REQUEST_TOKEN = "Login2AsyncTask.RESULT_SECRET";
        private static final String RESULT_USER_ID = "Login2AsyncTask.RESULT_USER_ID";

        @Override
        protected Bundle doInBackground(Bundle... params) {
            String verifier = params[0].getString(PARAM_VERIFIER);

            Bundle result = new Bundle();
            try {
                URL url = new URL(mTokenURLString);
                HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
                httpsURLConnection.setRequestMethod("POST");
                httpsURLConnection.setDoInput(true);
                httpsURLConnection.setDoOutput(true);
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(
                        httpsURLConnection.getOutputStream());
                outputStreamWriter.write("client_id=" + mClientId + "&client_secret=" + mClientSecret
                        + "&grant_type=authorization_code" + "&redirect_uri=" + mRedirectURL + "&code=" + verifier);
                outputStreamWriter.flush();
                String response = streamToString(httpsURLConnection.getInputStream());
                JSONObject jsonObject = (JSONObject) new JSONTokener(response).nextValue();

                String accessToken = jsonObject.getString("access_token");
                String id = jsonObject.getJSONObject("user").getString("id");
                result.putString(RESULT_ACCESS_TOKEN, accessToken);
                result.putString(RESULT_REQUEST_TOKEN, verifier);
                result.putString(RESULT_USER_ID, id);
            } catch (Exception e) {
                result.putString(RESULT_ERROR, e.getMessage());
            }

            return result;
        }

        @Override
        protected void onPostExecute(Bundle result) {
            if (!handleRequestResult(result, REQUEST_LOGIN) && !checkRequests()) {
                return;
            }

            mSharedPreferences.edit().putString(SAVE_STATE_KEY_OAUTH_TOKEN, result.getString(RESULT_ACCESS_TOKEN))
                    .putString(SAVE_STATE_KEY_OAUTH_REQUEST_TOKEN, result.getString(RESULT_REQUEST_TOKEN)).apply();
            mRequests.remove(REQUEST_LOGIN2);
            if (mLocalListeners.get(REQUEST_LOGIN) != null && !mRestart) {
                ((OnLoginCompleteListener) mLocalListeners.get(REQUEST_LOGIN)).onLoginSuccess(getID());
            }
            restartRequests();
        }

    }

    private class RequestGetSocialPersonAsyncTask extends SocialNetworkAsyncTask {
        public static final String PARAM_USER_ID = "RequestGetPersonAsyncTask.PARAM_USER_ID";
        public static final String CURRENT = "RequestGetPersonAsyncTask.CURRENT";

        @Override
        protected Bundle doInBackground(Bundle... params) {
            Bundle args = params[0];
            Bundle result = new Bundle(args);
            String userID;
            String token = mSharedPreferences.getString(SAVE_STATE_KEY_OAUTH_TOKEN, null);
            if (args.containsKey(PARAM_USER_ID)) {
                userID = args.getString(PARAM_USER_ID);
                result.putBoolean(CURRENT, false);
            } else {
                userID = "self";
                result.putBoolean(CURRENT, true);
            }
            String urlString = INSTAGRAM_APIURL + "/users/" + userID + "/?access_token=" + token;
            try {
                URL url = new URL(urlString);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                checkConnectionErrors(connection);

                InputStream inputStream = connection.getInputStream();
                String response = streamToString(inputStream);
                JSONObject jsonObject = (JSONObject) new JSONTokener(response).nextValue();
                JSONObject jsonResponse = jsonObject.getJSONObject("data");

                SocialPerson socialPerson = new SocialPerson();
                getSocialPerson(socialPerson, jsonResponse);
                result.putParcelable(REQUEST_GET_PERSON, socialPerson);
            } catch (Exception e) {
                checkException(e, result);
            }
            return result;
        }

        @Override
        protected void onPostExecute(Bundle result) {
            if (checkTokenError(result)) {
                return;
            }
            SocialPerson socialPerson = result.getParcelable(REQUEST_GET_PERSON);
            if (result.containsKey(CURRENT) && result.getBoolean(CURRENT)) {
                if (!handleRequestResult(result, REQUEST_GET_CURRENT_PERSON))
                    return;
                ((OnRequestSocialPersonCompleteListener) mLocalListeners.get(REQUEST_GET_CURRENT_PERSON))
                        .onRequestSocialPersonSuccess(getID(), socialPerson);
            } else {
                if (!handleRequestResult(result, REQUEST_GET_PERSON))
                    return;
                ((OnRequestSocialPersonCompleteListener) mLocalListeners.get(REQUEST_GET_PERSON))
                        .onRequestSocialPersonSuccess(getID(), socialPerson);
            }
        }
    }

    private class RequestGetDetailedPersonAsyncTask extends SocialNetworkAsyncTask {
        public static final String PARAM_USER_ID = "RequestGetPersonAsyncTask.PARAM_USER_ID";

        @Override
        protected Bundle doInBackground(Bundle... params) {
            Bundle args = params[0];
            Bundle result = new Bundle(args);
            String userID = args.getString(PARAM_USER_ID);
            String token = mSharedPreferences.getString(SAVE_STATE_KEY_OAUTH_TOKEN, null);

            String urlString = INSTAGRAM_APIURL + "/users/" + userID + "/?access_token=" + token;

            try {
                URL url = new URL(urlString);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                checkConnectionErrors(connection);
                InputStream inputStream = connection.getInputStream();
                String response = streamToString(inputStream);
                JSONObject jsonObject = (JSONObject) new JSONTokener(response).nextValue();
                JSONObject jsonResponse = jsonObject.getJSONObject("data");

                InstagramPerson instagramPerson = new InstagramPerson();
                getDetailedSocialPerson(instagramPerson, jsonResponse);
                result.putParcelable(REQUEST_GET_DETAIL_PERSON, instagramPerson);
            } catch (Exception e) {
                checkException(e, result);
            }
            return result;
        }

        @Override
        protected void onPostExecute(Bundle result) {
            if (checkTokenError(result)) {
                return;
            }
            if (!handleRequestResult(result, REQUEST_GET_DETAIL_PERSON))
                return;
            InstagramPerson instagramPerson = result.getParcelable(REQUEST_GET_DETAIL_PERSON);
            ((OnRequestDetailedSocialPersonCompleteListener) mLocalListeners.get(REQUEST_GET_DETAIL_PERSON))
                    .onRequestDetailedSocialPersonSuccess(getID(), instagramPerson);
        }
    }

    private class RequestSocialPersonsAsyncTask extends SocialNetworkAsyncTask {
        public static final String PARAM_USER_ID = "RequestGetPersonAsyncTask.PARAM_USER_ID";
        private static final String RESULT_USERS_ARRAY = "RequestPersonAsyncTask.RESULT_USERS_ARRAY";

        @Override
        protected Bundle doInBackground(Bundle... params) {
            Bundle args = params[0];
            Bundle result = new Bundle(args);
            String token = mSharedPreferences.getString(SAVE_STATE_KEY_OAUTH_TOKEN, null);
            String[] userIDs = args.getStringArray(PARAM_USER_ID);
            ArrayList<SocialPerson> socialPersons = new ArrayList<SocialPerson>();

            for (String userID : userIDs) {
                String urlString = INSTAGRAM_APIURL + "/users/" + userID + "/?access_token=" + token;
                try {
                    URL url = new URL(urlString);
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    checkConnectionErrors(connection);
                    InputStream inputStream = connection.getInputStream();
                    String response = streamToString(inputStream);
                    JSONObject jsonObject = (JSONObject) new JSONTokener(response).nextValue();
                    JSONObject jsonResponse = jsonObject.getJSONObject("data");
                    SocialPerson socialPerson = new SocialPerson();
                    getSocialPerson(socialPerson, jsonResponse);
                    socialPersons.add(socialPerson);
                } catch (Exception e) {
                    checkException(e, result);
                }
            }
            result.putParcelableArrayList(RESULT_USERS_ARRAY, socialPersons);
            return result;
        }

        @Override
        protected void onPostExecute(Bundle result) {
            if (checkTokenError(result)) {
                return;
            }
            ArrayList<SocialPerson> arraylist = result.getParcelableArrayList(RESULT_USERS_ARRAY);
            if (!handleRequestResult(result, REQUEST_GET_PERSONS))
                return;
            ((OnRequestSocialPersonsCompleteListener) mLocalListeners.get(REQUEST_GET_PERSONS))
                    .onRequestSocialPersonsSuccess(getID(), arraylist);
        }
    }

    private class RequestGetFriendsAsyncTask extends SocialNetworkAsyncTask {
        public static final String RESULT_GET_FRIENDS = "RESULT_GET_FRIENDS";
        public static final String RESULT_GET_FRIENDS_ID = "RESULT_GET_FRIENDS_ID";

        @Override
        protected Bundle doInBackground(Bundle... params) {
            Bundle result = new Bundle();
            String token = mSharedPreferences.getString(SAVE_STATE_KEY_OAUTH_TOKEN, null);
            String urlString = INSTAGRAM_APIURL + "/users/self/follows/?access_token=" + token;
            ArrayList<SocialPerson> socialPersons = new ArrayList<SocialPerson>();
            ArrayList<String> ids = new ArrayList<String>();
            try {
                getAllFriends(urlString, socialPersons, ids);
                result.putStringArray(RESULT_GET_FRIENDS_ID, ids.toArray(new String[ids.size()]));
                result.putParcelableArrayList(RESULT_GET_FRIENDS, socialPersons);
            } catch (Exception e) {
                checkException(e, result);
            }

            return result;
        }

        private void getAllFriends(String urlString, final ArrayList<SocialPerson> socialPersons,
                final ArrayList<String> ids) throws Exception {
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            checkConnectionErrors(connection);
            InputStream inputStream = connection.getInputStream();
            String response = streamToString(inputStream);
            JSONObject jsonObject = (JSONObject) new JSONTokener(response).nextValue();

            JSONObject jsonPagination;
            String nextToken = null;
            if (jsonObject.has("pagination")) {
                jsonPagination = jsonObject.getJSONObject("pagination");
                if (jsonPagination.has("next_url")) {
                    nextToken = jsonPagination.getString("next_url");
                }
            }
            JSONArray jsonResponse = jsonObject.getJSONArray("data");
            int length = jsonResponse.length();
            for (int i = 0; i < length; i++) {
                SocialPerson socialPerson = new SocialPerson();
                getSocialPerson(socialPerson, jsonResponse.getJSONObject(i));
                socialPersons.add(socialPerson);
                ids.add(jsonResponse.getJSONObject(i).getString("id"));
            }

            if ((nextToken != null) && (!TextUtils.isEmpty(nextToken))) {
                getAllFriends(nextToken, socialPersons, ids);
            }
        }

        @Override
        protected void onPostExecute(Bundle result) {
            if (checkTokenError(result)) {
                return;
            }
            if (!handleRequestResult(result, REQUEST_GET_FRIENDS, result.getStringArray(RESULT_GET_FRIENDS_ID)))
                return;

            ((OnRequestGetFriendsCompleteListener) mLocalListeners.get(REQUEST_GET_FRIENDS))
                    .onGetFriendsIdComplete(getID(), result.getStringArray(RESULT_GET_FRIENDS_ID));
            ArrayList<SocialPerson> socialPersons = result.getParcelableArrayList(RESULT_GET_FRIENDS);
            ((OnRequestGetFriendsCompleteListener) mLocalListeners.get(REQUEST_GET_FRIENDS))
                    .onGetFriendsComplete(getID(), socialPersons);
        }
    }

    private class RequestCheckIsFriendAsyncTask extends SocialNetworkAsyncTask {
        public static final String PARAM_USER_ID = "PARAM_USER_ID";

        public static final String RESULT_IS_FRIEND = "RESULT_IS_FRIEND";
        public static final String RESULT_REQUESTED_ID = "RESULT_REQUESTED_ID";

        @Override
        protected Bundle doInBackground(Bundle... params) {
            Bundle args = params[0];
            Bundle result = new Bundle(args);
            String userID = args.getString(PARAM_USER_ID);
            String token = mSharedPreferences.getString(SAVE_STATE_KEY_OAUTH_TOKEN, null);
            String urlString = INSTAGRAM_APIURL + "/users/" + userID + "/relationship/?access_token=" + token;
            result.putString(RESULT_REQUESTED_ID, userID);
            try {
                URL url = new URL(urlString);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                checkConnectionErrors(connection);
                InputStream inputStream = connection.getInputStream();
                String response = streamToString(inputStream);
                JSONObject jsonObject = (JSONObject) new JSONTokener(response).nextValue();

                JSONObject jsonResponse = jsonObject.getJSONObject("data");
                String outgoing_status = jsonResponse.getString("outgoing_status");
                if (outgoing_status.equals("follows")) {
                    result.putBoolean(RESULT_IS_FRIEND, true);
                } else {
                    result.putBoolean(RESULT_IS_FRIEND, false);
                }
            } catch (Exception e) {
                checkException(e, result);
            }
            return result;
        }

        @Override
        protected void onPostExecute(Bundle result) {
            if (checkTokenError(result)) {
                return;
            }
            if (!handleRequestResult(result, REQUEST_CHECK_IS_FRIEND, result.getString(RESULT_REQUESTED_ID)))
                return;

            ((OnCheckIsFriendCompleteListener) mLocalListeners.get(REQUEST_CHECK_IS_FRIEND))
                    .onCheckIsFriendComplete(getID(), "" + result.getString(RESULT_REQUESTED_ID),
                            result.getBoolean(RESULT_IS_FRIEND));
        }
    }

    private class RequestAddFriendAsyncTask extends SocialNetworkAsyncTask {
        public static final String PARAM_USER_ID = "PARAM_USER_ID";

        public static final String RESULT_REQUESTED_ID = "RESULT_REQUESTED_ID";

        @Override
        protected Bundle doInBackground(Bundle... params) {
            Bundle args = params[0];
            Bundle result = new Bundle(args);
            String userID = args.getString(PARAM_USER_ID);
            String token = mSharedPreferences.getString(SAVE_STATE_KEY_OAUTH_TOKEN, null);
            String urlString = INSTAGRAM_APIURL + "/users/" + userID + "/relationship/?access_token=" + token;
            String parameters = "action=follow";

            try {
                URL url = new URL(urlString);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setDoOutput(true);
                connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                connection.setRequestMethod("POST");
                OutputStreamWriter request = new OutputStreamWriter(connection.getOutputStream());
                request.write(parameters);
                request.flush();
                request.close();
                checkConnectionErrors(connection);
                InputStream inputStream = connection.getInputStream();
                String response = streamToString(inputStream);
                JSONObject jsonObject = (JSONObject) new JSONTokener(response).nextValue();

                JSONObject jsonResponse = jsonObject.getJSONObject("data");
                String outgoing_status = jsonResponse.getString("outgoing_status");
                if (outgoing_status.equals("follows") || outgoing_status.equals("requested")) {
                    result.putString(RESULT_REQUESTED_ID, userID);
                } else {
                    result.putString(RESULT_ERROR, "REQUEST_ADD_FRIEND Error");
                }
            } catch (Exception e) {
                checkException(e, result);
            }
            return result;
        }

        @Override
        protected void onPostExecute(Bundle result) {
            if (checkTokenError(result)) {
                return;
            }
            if (!handleRequestResult(result, REQUEST_ADD_FRIEND, result.getString(RESULT_REQUESTED_ID)))
                return;

            ((OnRequestAddFriendCompleteListener) mLocalListeners.get(REQUEST_ADD_FRIEND))
                    .onRequestAddFriendComplete(getID(), "" + result.getString(RESULT_REQUESTED_ID));
        }
    }

    private class RequestRemoveFriendAsyncTask extends SocialNetworkAsyncTask {
        public static final String PARAM_USER_ID = "PARAM_USER_ID";

        public static final String RESULT_REQUESTED_ID = "RESULT_REQUESTED_ID";

        @Override
        protected Bundle doInBackground(Bundle... params) {
            Bundle args = params[0];
            Bundle result = new Bundle(args);
            String userID = args.getString(PARAM_USER_ID);
            String token = mSharedPreferences.getString(SAVE_STATE_KEY_OAUTH_TOKEN, null);
            String urlString = INSTAGRAM_APIURL + "/users/" + userID + "/relationship/?access_token=" + token;
            String parameters = "action=unfollow";

            try {
                URL url = new URL(urlString);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setDoOutput(true);
                connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                connection.setRequestMethod("POST");
                OutputStreamWriter request = new OutputStreamWriter(connection.getOutputStream());
                request.write(parameters);
                request.flush();
                request.close();
                checkConnectionErrors(connection);
                InputStream inputStream = connection.getInputStream();
                String response = streamToString(inputStream);
                JSONObject jsonObject = (JSONObject) new JSONTokener(response).nextValue();

                JSONObject jsonResponse = jsonObject.getJSONObject("data");
                String outgoing_status = jsonResponse.getString("outgoing_status");
                if (outgoing_status.equals("none")) {
                    result.putString(RESULT_REQUESTED_ID, userID);
                } else {
                    result.putString(RESULT_ERROR, "REQUEST_ADD_FRIEND Error");
                }
            } catch (Exception e) {
                checkException(e, result);
            }
            return result;
        }

        @Override
        protected void onPostExecute(Bundle result) {
            if (checkTokenError(result)) {
                return;
            }
            if (!handleRequestResult(result, REQUEST_REMOVE_FRIEND, result.getString(RESULT_REQUESTED_ID)))
                return;

            ((OnRequestRemoveFriendCompleteListener) mLocalListeners.get(REQUEST_REMOVE_FRIEND))
                    .onRequestRemoveFriendComplete(getID(), "" + result.getString(RESULT_REQUESTED_ID));
        }
    }
}