com.newtifry.android.remote.BackendClient.java Source code

Java tutorial

Introduction

Here is the source code for com.newtifry.android.remote.BackendClient.java

Source

/**
 * Newtifry for Android.
 * 
 * Copyright 2011 Daniel Foote
 *
 * 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.
 * 
 * This file is based off Google's Chrome to Phone code, although is somewhat different.
 * See http://code.google.com/p/chrometophone/source/browse/trunk/android/src/com/google/android/apps/chrometophone/AppEngineClient.java
 */

package com.newtifry.android.remote;

import com.newtifry.android.R;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.List;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
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.client.params.HttpClientParams;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.util.Log;

public class BackendClient {
    private static final String TAG = "Newtifry";

    private Context context;
    private String accountName;
    private static String backendName = "";
    private static String defaultBackendName = "";

    public BackendClient(Context context, String accountName) {
        this.context = context;
        this.accountName = accountName;
        if (BackendClient.defaultBackendName.equals("")) {
            PackageInfo pInfo;

            try {
                pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
                if (pInfo.versionCode == 999) {
                    BackendClient.defaultBackendName = "https://99-dot-" + context.getString(R.string.backend_url);
                } else {
                    BackendClient.defaultBackendName = "https://" + context.getString(R.string.backend_url);
                }
            } catch (NameNotFoundException e) {
                BackendClient.defaultBackendName = "https://" + context.getString(R.string.backend_url);
            }
        }
    }

    public BackendResponse request(BackendRequest request) throws Exception {
        try {
            // Add a 'format=json' to the params.
            request.add("format", "json");
            HttpResponse res;
            if (this.accountName.equals("")) {
                res = requestNoRetryNoAccount(request.getUri(), request.getParams());
            } else {
                res = requestNoRetry(request.getUri(), request.getParams(), false);
            }
            if (res.getStatusLine().getStatusCode() == 500) {
                res = requestNoRetry(request.getUri(), request.getParams(), true);
            }

            // Parse the response.
            BackendResponse response = new BackendResponse(request, res);

            return response;
        } catch (PendingAuthException ex) {
            // Parse the response.
            // TODO: This causes the request to fail and need a retry.
            BackendResponse response = new BackendResponse(request, ex.getMessage());

            return response;
        }
    }

    private HttpResponse requestNoRetryNoAccount(String urlPath, List<NameValuePair> params) throws Exception {
        // Make POST request
        DefaultHttpClient client = new DefaultHttpClient();

        URI uri = new URI(BackendClient.defaultBackendName + urlPath);
        HttpPost post = new HttpPost(uri);
        final HttpParams getParams = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(getParams, 5000);
        HttpConnectionParams.setSoTimeout(getParams, 5000);

        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8");
        post.setEntity(entity);
        post.setHeader("X-Same-Domain", "1"); // XSRF
        HttpResponse res = client.execute(post);
        return res;
    }

    private HttpResponse requestNoRetry(String urlPath, List<NameValuePair> params, boolean newToken)
            throws Exception {
        // Get auth token for account
        Account account = new Account(this.accountName, "com.google");
        String authToken = getAuthToken(this.context, account);
        if (authToken == null) {
            throw new PendingAuthException(this.accountName);
        }
        if (newToken) {
            // Invalidate the cached token
            AccountManager accountManager = AccountManager.get(this.context);
            accountManager.invalidateAuthToken(account.type, authToken);
            authToken = this.getAuthToken(this.context, account);
        }

        // Get ACSID cookie
        DefaultHttpClient client = new DefaultHttpClient();
        String continueURL = getBackendURL(); // this.backendName;
        URI uri = new URI(getBackendURL() /* this.backendName */ + "/_ah/login?continue="
                + URLEncoder.encode(continueURL, "UTF-8") + "&auth=" + authToken);
        HttpGet method = new HttpGet(uri);
        final HttpParams getParams = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(getParams, 5000);
        HttpConnectionParams.setSoTimeout(getParams, 5000);

        HttpClientParams.setRedirecting(getParams, false); // continue is not
        // used
        method.setParams(getParams);

        HttpResponse res = client.execute(method);
        Header[] headers = res.getHeaders("Set-Cookie");

        String ascidCookie = null;
        for (Header header : headers) {
            if (header.getValue().indexOf("ACSID=") >= 0) {
                // let's parse it
                String value = header.getValue();
                String[] pairs = value.split(";");
                ascidCookie = pairs[0];
            }
        }
        res.getEntity().consumeContent();
        // Make POST request
        uri = new URI(getBackendURL() /* this.backendName */ + urlPath);
        HttpPost post = new HttpPost(uri);
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8");
        post.setEntity(entity);
        post.setHeader("Cookie", ascidCookie);
        post.setHeader("X-Same-Domain", "1"); // XSRF
        res = client.execute(post);
        return res;
    }

    static public void setBackendURL(String url) {
        BackendClient.backendName = url;
    }

    static public String getBackendURL() {
        if (!"".equals(BackendClient.backendName)) {
            return BackendClient.backendName;
        } else {
            return BackendClient.defaultBackendName;
        }
    }

    private String getAuthToken(Context context, Account account) throws PendingAuthException {
        String authToken = null;
        AccountManager accountManager = AccountManager.get(context);
        try {
            AccountManagerFuture<Bundle> future = accountManager.getAuthToken(account, "ah", false, null, null);
            Bundle bundle = future.getResult();
            authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
            // User will be asked for "App Engine" permission.
            if (authToken == null) {
                // No authorization token - will need to ask permission from user.
                Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT);
                if (intent != null) {
                    // User input required
                    context.startActivity(intent);
                    throw new PendingAuthException("Asking user for permission.");
                }
            }
        } catch (OperationCanceledException e) {
            Log.d(TAG, e.getMessage());
        } catch (AuthenticatorException e) {
            Log.d(TAG, e.getMessage());
        } catch (IOException e) {
            Log.d(TAG, e.getMessage());
        }

        return authToken;
    }

    public class PendingAuthException extends Exception {
        private static final long serialVersionUID = 1L;

        public PendingAuthException(String message) {
            super(message);
        }
    }
}