com.xorcode.andtweet.net.ConnectionOAuth.java Source code

Java tutorial

Introduction

Here is the source code for com.xorcode.andtweet.net.ConnectionOAuth.java

Source

/*
 * Copyright (C) 2010 yvolk (Yuri Volkov), http://yurivolkov.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xorcode.andtweet.net;

import com.xorcode.andtweet.util.MyLog;

import oauth.signpost.OAuthConsumer;
import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;

import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONArray;
import org.json.JSONObject;

import android.content.SharedPreferences;
import android.net.Uri;
import android.util.Log;

import java.io.UnsupportedEncodingException;
import java.util.LinkedList;

/**
 * Handles connection to the Twitter REST API using OAuth
 * Based on "BLOA" example, Copyright 2010 - Brion N. Emde
 * 
 * @see <a
 *      href="http://github.com/brione/Brion-Learns-OAuth">Brion-Learns-OAuth</a>
 */
public class ConnectionOAuth extends Connection {
    private static final String TAG = ConnectionOAuth.class.getSimpleName();

    public static final String USER_TOKEN = "user_token";
    public static final String USER_SECRET = "user_secret";
    public static final String REQUEST_TOKEN = "request_token";
    public static final String REQUEST_SECRET = "request_secret";
    public static final String REQUEST_SUCCEEDED = "request_succeeded";
    public static final String TWITTER_REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token";
    public static final String TWITTER_ACCESS_TOKEN_URL = "https://api.twitter.com/oauth/access_token";
    public static final String TWITTER_AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize";
    private OAuthConsumer mConsumer = null;

    /**
     * Saved User token
     */
    private String mToken;

    /**
     * Saved User secret
     */
    private String mSecret;

    private HttpClient mClient;

    public ConnectionOAuth(SharedPreferences sp) {
        super(sp);

        HttpParams parameters = new BasicHttpParams();
        HttpProtocolParams.setVersion(parameters, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(parameters, HTTP.DEFAULT_CONTENT_CHARSET);
        HttpProtocolParams.setUseExpectContinue(parameters, false);
        HttpConnectionParams.setTcpNoDelay(parameters, true);
        HttpConnectionParams.setSocketBufferSize(parameters, 8192);

        SchemeRegistry schReg = new SchemeRegistry();
        schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        ClientConnectionManager tsccm = new ThreadSafeClientConnManager(parameters, schReg);
        mClient = new DefaultHttpClient(tsccm, parameters);

        mConsumer = new CommonsHttpOAuthConsumer(OAuthKeys.TWITTER_CONSUMER_KEY, OAuthKeys.TWITTER_CONSUMER_SECRET);
        loadSavedKeys(sp);
    }

    private void loadSavedKeys(SharedPreferences sp) {
        // We look for saved user keys
        if (sp.contains(ConnectionOAuth.USER_TOKEN) && sp.contains(ConnectionOAuth.USER_SECRET)) {
            mToken = sp.getString(ConnectionOAuth.USER_TOKEN, null);
            mSecret = sp.getString(ConnectionOAuth.USER_SECRET, null);
            if (!(mToken == null || mSecret == null)) {
                mConsumer.setTokenWithSecret(mToken, mSecret);
            }
        }
    }

    /**
     * @param token null means to clear the old values
     * @param secret
     */
    public void saveAuthInformation(SharedPreferences sp, String token, String secret) {
        synchronized (sp) {
            SharedPreferences.Editor editor = sp.edit();
            if (token == null) {
                editor.remove(ConnectionOAuth.USER_TOKEN);
                MyLog.d(TAG, "Clearing OAuth Token");
            } else {
                editor.putString(ConnectionOAuth.USER_TOKEN, token);
                MyLog.d(TAG, "Saving OAuth Token: " + token);
            }
            if (secret == null) {
                editor.remove(ConnectionOAuth.USER_SECRET);
                MyLog.d(TAG, "Clearing OAuth Secret");
            } else {
                editor.putString(ConnectionOAuth.USER_SECRET, secret);
                MyLog.d(TAG, "Saving OAuth Secret: " + secret);
            }
            editor.commit();
            // Keys changed so we have to reload them
            loadSavedKeys(sp);
        }
    }

    @Override
    public void clearAuthInformation(SharedPreferences sp) {
        saveAuthInformation(sp, null, null);
    }

    @Override
    public JSONObject createFavorite(long statusId) throws ConnectionException {
        StringBuilder url = new StringBuilder(FAVORITES_CREATE_BASE_URL);
        url.append(String.valueOf(statusId));
        url.append(EXTENSION);
        HttpPost post = new HttpPost(url.toString());
        return postRequest(post);
    }

    @Override
    public JSONObject destroyFavorite(long statusId) throws ConnectionException {
        StringBuilder url = new StringBuilder(FAVORITES_DESTROY_BASE_URL);
        url.append(String.valueOf(statusId));
        url.append(EXTENSION);
        HttpPost post = new HttpPost(url.toString());
        return postRequest(post);
    }

    @Override
    public JSONObject destroyStatus(long statusId) throws ConnectionException {
        StringBuilder url = new StringBuilder(STATUSES_DESTROY_URL);
        url.append(String.valueOf(statusId));
        url.append(EXTENSION);
        HttpPost post = new HttpPost(url.toString());
        return postRequest(post);
    }

    @Override
    public JSONArray getDirectMessages(long sinceId, int limit) throws ConnectionException {
        String url = DIRECT_MESSAGES_URL;
        return getTimeline(url, sinceId, 0, limit, 0);
    }

    @Override
    public JSONArray getFriendsTimeline(long sinceId, int limit) throws ConnectionException {
        String url = STATUSES_FRIENDS_TIMELINE_URL;
        return getTimeline(url, sinceId, 0, limit, 0);
    }

    private JSONObject getRequest(HttpGet get) throws ConnectionException {
        JSONObject jso = null;
        boolean ok = false;
        try {
            mConsumer.sign(get);
            String response = mClient.execute(get, new BasicResponseHandler());
            jso = new JSONObject(response);
            // Log.d(TAG, "authenticatedQuery: " + jso.toString(2));
            ok = true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ConnectionException(e.getLocalizedMessage());
        }
        if (!ok) {
            jso = null;
        }
        return jso;
    }

    private JSONObject getUrl(String url) throws ConnectionException {
        HttpGet get = new HttpGet(url);
        return getRequest(get);
    }

    /**
     * Universal method for several Timelines...
     * 
     * @param url URL predefined for this timeline
     * @param sinceId
     * @param maxId
     * @param limit
     * @param page
     * @return
     * @throws ConnectionException
     */
    private JSONArray getTimeline(String url, long sinceId, long maxId, int limit, int page)
            throws ConnectionException {
        setSinceId(sinceId);
        setLimit(limit);

        boolean ok = false;
        JSONArray jArr = null;
        try {
            Uri sUri = Uri.parse(url);
            Uri.Builder builder = sUri.buildUpon();
            if (getSinceId() != 0) {
                builder.appendQueryParameter("since_id", String.valueOf(getSinceId()));
            } else if (maxId != 0) { // these are mutually exclusive
                builder.appendQueryParameter("max_id", String.valueOf(maxId));
            }
            if (getLimit() != 0) {
                builder.appendQueryParameter("count", String.valueOf(getLimit()));
            }
            if (page != 0) {
                builder.appendQueryParameter("page", String.valueOf(page));
            }
            HttpGet get = new HttpGet(builder.build().toString());
            mConsumer.sign(get);
            String response = mClient.execute(get, new BasicResponseHandler());
            jArr = new JSONArray(response);
            ok = (jArr != null);
        } catch (NullPointerException e) {
            // It looks like a bug in the library, but we have to catch it 
            Log.e(TAG, "NullPointerException was caught, URL='" + url + "'");
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
            throw new ConnectionException(e.getLocalizedMessage());
        }
        if (MyLog.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "getTimeline '" + url + "' " + (ok ? "OK, " + jArr.length() + " statuses" : "FAILED"));
        }
        return jArr;
    }

    @Override
    public JSONArray getMentionsTimeline(long sinceId, int limit) throws ConnectionException {
        String url = STATUSES_MENTIONS_TIMELINE_URL;
        return getTimeline(url, sinceId, 0, limit, 0);
    }

    @Override
    public JSONObject rateLimitStatus() throws ConnectionException {
        return getUrl(ACCOUNT_RATE_LIMIT_STATUS_URL);
    }

    private JSONObject postRequest(HttpPost post) throws ConnectionException {
        JSONObject jso = null;
        boolean ok = false;
        try {
            // Maybe we'll need this:
            // post.setParams(...);

            // sign the request to authenticate
            mConsumer.sign(post);
            String response = mClient.execute(post, new BasicResponseHandler());
            jso = new JSONObject(response);
            ok = true;
        } catch (HttpResponseException e) {
            ConnectionException e2 = new ConnectionException(e.getStatusCode(), e.getLocalizedMessage());
            Log.w(TAG, e2.getLocalizedMessage());
            throw e2;
        } catch (Exception e) {
            // We don't catch other exceptions because in fact it's vary difficult to tell
            // what was a real cause of it. So let's make code clearer.
            e.printStackTrace();
            throw new ConnectionException(e.getLocalizedMessage());
        }
        if (!ok) {
            jso = null;
        }
        return jso;
    }

    @Override
    public JSONObject updateStatus(String message, long inReplyToId) throws ConnectionException {
        HttpPost post = new HttpPost(STATUSES_UPDATE_URL);
        LinkedList<BasicNameValuePair> out = new LinkedList<BasicNameValuePair>();
        out.add(new BasicNameValuePair("status", message));
        if (inReplyToId > 0) {
            out.add(new BasicNameValuePair("in_reply_to_status_id", String.valueOf(inReplyToId)));
        }
        try {
            post.setEntity(new UrlEncodedFormEntity(out, HTTP.UTF_8));
        } catch (UnsupportedEncodingException e) {
            Log.e(TAG, e.toString());
        }
        return postRequest(post);
    }

    /**
     * @see com.xorcode.andtweet.net.Connection#getCredentialsPresent()
     */
    @Override
    public boolean getCredentialsPresent(SharedPreferences sp) {
        boolean yes = false;
        if (sp.contains(ConnectionOAuth.USER_TOKEN) && sp.contains(ConnectionOAuth.USER_SECRET)) {
            mToken = sp.getString(ConnectionOAuth.USER_TOKEN, null);
            mSecret = sp.getString(ConnectionOAuth.USER_SECRET, null);
            if (!(mToken == null || mSecret == null)) {
                yes = true;
            }
        }
        return yes;
    }

    @Override
    public JSONObject verifyCredentials() throws ConnectionException {
        return getUrl(ACCOUNT_VERIFY_CREDENTIALS_URL);
    }
}