com.google.appinventor.components.runtime.FusiontablesControl.java Source code

Java tutorial

Introduction

Here is the source code for com.google.appinventor.components.runtime.FusiontablesControl.java

Source

// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the MIT License https://raw.github.com/mit-cml/app-inventor/master/mitlicense.txt
package com.google.appinventor.components.runtime;

import com.google.api.client.extensions.android2.AndroidHttp;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.googleapis.services.GoogleKeyInitializer;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.fusiontables.Fusiontables;
import com.google.api.services.fusiontables.Fusiontables.Query.Sql;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.DesignerProperty;
import com.google.appinventor.components.annotations.PropertyCategory;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.annotations.UsesLibraries;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.common.YaVersion;
import com.google.appinventor.components.runtime.util.ClientLoginHelper;
import com.google.appinventor.components.runtime.util.ErrorMessages;
import com.google.appinventor.components.runtime.util.IClientLoginHelper;
import com.google.appinventor.components.runtime.util.OAuth2Helper;
import com.google.appinventor.components.runtime.util.SdkLevel;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.util.Log;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;

/**
 * Appinventor fusiontables control.
 *
 * This version has been migrated from the Fusiontables SQL API to the Fusiontables V1.0 API.
 *
 * See <a href="https://developers.google.com/fusiontables/">https://developers.google.com/fusiontables/</a>
 * See <a href="https://developers.google.com/fusiontables/docs/v1/migration_guide">https://developers.google.com/fusiontables/</a>
 *
 * The main change occurs in the way API requests are authorized.  This version uses
 * OAuth 2.0 and makes use of OAuth2Helper. The helper uses the Google AccountManager
 * to acquire an access token that must be attached as the OAuth header in all
 * Fusiontable Http requests.
 *
 * Before a Fusiontable request can be made, the app must acquire an OAuth token.
 * This may involve the user logging in to their Gmail account (or not if they are already
 * logged in) and then being prompted to give the app permission to access the user's fusion
 * tables.
 *
 * Permission takes the form of an access token (called authToken), which must be
 * transmitted to the Fusiontables service as part of all Http requests.
 *
 */
@DesignerComponent(version = YaVersion.FUSIONTABLESCONTROL_COMPONENT_VERSION, description = "<p>A non-visible component that communicates with Google Fusion Tables. "
        + "Fusion Tables let you store, share, query and visualize data tables; "
        + "this component lets you query, create, and modify these tables.</p> " + "<p>This component uses the "
        + "<a href=\"https://developers.google.com/fusiontables/docs/v1/getting_started\" target=\"_blank\">Fusion Tables API V1.0</a>. "
        + "<p>In order to develop apps that use Fusiontables, you must obtain an API Key."
        + "<p>To get an API key, follow these instructions.</p> " + "<ol>"
        + "<li>Go to your <a href=\"https://code.google.com/apis/console/\" target=\"_blank\">Google APIs Console</a> and login if necessary.</li>"
        + "<li>Select the <i>Services</i> item from the menu on the left.</li>"
        + "<li>Choose the <i>Fusiontables</i> service from the list provided and turn it on.</li>"
        + "<li>Go back to the main menu and select the <i>API Access</i> item. </li>" + "</ol>"
        + "<p>Your API Key will be near the bottom of that pane in the section called \"Simple API Access\"."
        + "You will have to provide that key as the value for the <i>ApiKey</i> property in your Fusiontables app.</p>"
        + "<p>Once you have an API key, set the value of the <i>Query</i> property to a valid Fusiontables SQL query "
        + "and call <i>SendQuery</i> to execute the query.  App Inventor will send the query to the Fusion Tables "
        + "server and the <i>GotResult</i> block will fire when a result is returned from the server."
        + "Query results will be returned in CSV format, and "
        + "can be converted to list format using the \"list from csv table\" or "
        + "\"list from csv row\" blocks.</p>"
        + "<p>Note that you do not need to worry about UTF-encoding the query. "
        + "But you do need to make sure the query follows the syntax described in "
        + "<a href=\"https://developers.google.com/fusiontables/docs/v1/getting_started\" target=\"_blank\">the reference manual</a>, "
        + "which means that things like capitalization for names of columns matters, and "
        + "that single quotes must be used around column names if there are spaces in them.</p>", category = ComponentCategory.STORAGE, nonVisible = true, iconName = "images/fusiontables.png")
@SimpleObject
@UsesPermissions(permissionNames = "android.permission.INTERNET," + "android.permission.ACCOUNT_MANAGER,"
        + "android.permission.MANAGE_ACCOUNTS," + "android.permission.GET_ACCOUNTS,"
        + "android.permission.USE_CREDENTIALS")
@UsesLibraries(libraries = "fusiontables.jar," + "google-api-client-beta.jar,"
        + "google-api-client-android2-beta.jar," + "google-http-client-beta.jar,"
        + "google-http-client-android2-beta.jar," + "google-http-client-android3-beta.jar,"
        + "google-oauth-client-beta.jar," + "guava-14.0.1.jar")

public class FusiontablesControl extends AndroidNonvisibleComponent implements Component {
    private static final String LOG_TAG = "fusion";
    private static final String DIALOG_TEXT = "Choose an account to access FusionTables";
    private static final String FUSION_QUERY_URL = "http://www.google.com/fusiontables/api/query";
    public static final String FUSIONTABLES_POST = "https://www.googleapis.com/fusiontables/v1/tables";

    private static final String DEFAULT_QUERY = "show tables";
    private static final String FUSIONTABLES_SERVICE = "fusiontables";
    private static final int SERVER_TIMEOUT_MS = 30000;
    public static final String AUTHORIZATION_HEADER_PREFIX = "Bearer ";

    public static final String FUSIONTABLES_URL = "https://www.googleapis.com/fusiontables/v1/query";
    public static final String AUTH_TOKEN_TYPE_FUSIONTABLES = "oauth2:https://www.googleapis.com/auth/fusiontables";
    public static final String APP_NAME = "App Inventor";

    private String authTokenType = AUTH_TOKEN_TYPE_FUSIONTABLES;

    /**
     * The developer's Google API key,
     * See <a href="https://code.google.com/apis/console/">https://code.google.com/apis/console/</a>
     */
    private String apiKey;

    /**
     * The query to send to the Fusiontables service.
     */
    private String query;

    /**
     * String result of API query
     */
    private String queryResultStr;

    /**
     * Error message returned from API query
     */
    private String errorMessage = "Error on Fusiontables query";

    private final Activity activity;
    private final IClientLoginHelper requestHelper;

    public FusiontablesControl(ComponentContainer componentContainer) {
        super(componentContainer.$form());
        this.activity = componentContainer.$context();
        requestHelper = createClientLoginHelper(DIALOG_TEXT, FUSIONTABLES_SERVICE);
        query = DEFAULT_QUERY;

        if (SdkLevel.getLevel() < SdkLevel.LEVEL_ECLAIR) {
            showNoticeAndDie("Sorry. The Fusiontables component is not compatible with this phone.",
                    "This application must exit.", "Rats!");
        }

        // comment: The above code was originally
        //    Toast.makeText(activity,
        //        "Sorry. The Fusiontables component is not compatible with your phone. Exiting.",
        //        Toast.LENGTH_LONG).show();
        //    activity.finish();
        // I'm leaving this here for the edification of future developers.  The code does not work
        // because Toasts do not block: The activity will finish immediately, regardless of
        // the length of the toast, and the message will not be readable. The new version isn't
        // quite right either because the app will execute Screen.Initialize while the message
        // is being shown.

    }

    // show a notification and kill the app when the button is pressed
    private void showNoticeAndDie(String message, String title, String buttonText) {
        AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
        alertDialog.setTitle(title);
        // prevents the user from escaping the dialog by hitting the Back button
        alertDialog.setCancelable(false);
        alertDialog.setMessage(message);
        alertDialog.setButton(buttonText, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                activity.finish();
            }
        });
        alertDialog.show();
    }

    /**
     * Setter for the app developer's API key.
     */
    @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "")
    @SimpleProperty
    public void ApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    /**
     * Getter for the API key.
     * @return apiKey the apiKey
     */
    @SimpleProperty(description = "Your Google API Key. For help, click on the question"
            + "mark (?) next to the FusiontablesControl component in the Palette. ", category = PropertyCategory.BEHAVIOR)
    public String ApiKey() {
        return apiKey;
    }

    @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = DEFAULT_QUERY)
    @SimpleProperty
    public void Query(String query) {
        this.query = query;
    }

    @SimpleProperty(description = "The query to send to the Fusion Tables API. "
            + "<p>For legal query formats and examples, see the "
            + "<a href=\"https://developers.google.com/fusiontables/docs/v1/getting_started\" target=\"_blank\">Fusion Tables API v1.0 reference manual</a>.</p> "
            + "<p>Note that you do not need to worry about UTF-encoding the query. "
            + "But you do need to make sure it follows the syntax described in the reference manual, "
            + "which means that things like capitalization for names of columns matters, "
            + "and that single quotes need to be used around column names if there are spaces in them.</p> ", category = PropertyCategory.BEHAVIOR)
    public String Query() {
        return query;
    }

    /**
     * Calls QueryProcessor to execute the API request asynchronously, if
     * the user has already authenticated with the Fusiontables service.
     */
    @SimpleFunction(description = "Send the query to the Fusiontables server.")
    public void SendQuery() {
        new QueryProcessorV1(activity).execute(query);
    }

    //Deprecated  -- Won't work after 12/2012
    @SimpleFunction(description = "DEPRECATED. This block "
            + "will be deprecated by the end of 2012.  Use SendQuery.")
    public void DoQuery() {
        if (requestHelper != null) {
            new QueryProcessor().execute(query);
        } else {
            form.dispatchErrorOccurredEvent(this, "DoQuery",
                    ErrorMessages.ERROR_FUNCTIONALITY_NOT_SUPPORTED_FUSIONTABLES_CONTROL);
        }
    }

    @SimpleEvent(description = "Indicates that the Fusion Tables query has finished processing, "
            + "with a result.  The result of the query will generally be returned in CSV format, and "
            + "can be converted to list format using the \"list from csv table\" or "
            + "\"list from csv row\" blocks.")
    public void GotResult(String result) {
        // Invoke the application's "GotValue" event handler
        EventDispatcher.dispatchEvent(this, "GotResult", result);
    }

    // TODO(sharon): figure out why this isn't working
    // TODO(ralph): Looks like it's working for OAuth 2, Let's user switch accounts
    @SimpleFunction
    public void ForgetLogin() {
        OAuth2Helper.resetAccountCredential(activity);
    }

    // To be Deprecated, based on the old API
    private IClientLoginHelper createClientLoginHelper(String accountPrompt, String service) {
        if (SdkLevel.getLevel() >= SdkLevel.LEVEL_ECLAIR) {
            HttpClient httpClient = new DefaultHttpClient();
            HttpConnectionParams.setSoTimeout(httpClient.getParams(), SERVER_TIMEOUT_MS);
            HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), SERVER_TIMEOUT_MS);
            return new ClientLoginHelper(activity, service, accountPrompt, httpClient);
        }
        return null;
    }

    /**
     * Generate a FusionTables POST request
     */
    // To be deprecated, based on the old API
    private HttpUriRequest genFusiontablesQuery(String query) throws IOException {
        HttpPost request = new HttpPost(FUSION_QUERY_URL);
        ArrayList<BasicNameValuePair> pair = new ArrayList<BasicNameValuePair>(1);
        pair.add(new BasicNameValuePair("sql", query));
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pair, "UTF-8");
        entity.setContentType("application/x-www-form-urlencoded");
        request.setEntity(entity);
        return request;
    }

    /**
     * To be deprecated -- will no longer after 12/2012.
     * Sends the Fusiontables request asynchronously to the server and returns the result.
     * This version uses the Deprecated SQL API.
     */
    private class QueryProcessor extends AsyncTask<String, Void, String> {
        private ProgressDialog progress = null;

        @Override
        protected void onPreExecute() {
            progress = ProgressDialog.show(activity, "Fusiontables", "processing query...", true);
        }

        /**
         * Query the fusiontables server.
         * @return The resulant table, error page, or exception message.
         */
        @Override
        protected String doInBackground(String... params) {
            try {
                HttpUriRequest request = genFusiontablesQuery(params[0]);
                Log.d(LOG_TAG, "Fetching: " + params[0]);
                HttpResponse response = requestHelper.execute(request);
                ByteArrayOutputStream outstream = new ByteArrayOutputStream();
                response.getEntity().writeTo(outstream);
                Log.d(LOG_TAG, "Response: " + response.getStatusLine().toString());
                return outstream.toString();
            } catch (IOException e) {
                e.printStackTrace();
                return e.getMessage();
            }
        }

        /**
         * Got the results.  We could parse the CSV and do something useful with it.
         */

        @Override
        protected void onPostExecute(String result) {
            progress.dismiss();
            GotResult(result);
        }
    }

    /**
     * Executes a Fusiontable query with an OAuth 2.0 authenticated
     * request.  Requests are authenticated by attaching an
     * Authentication header to the Http request.  The header
     * takes the form 'Authentication Oauth <access_token>'.
     *
     * Requests take the form of SQL strings, using an Sql
     * object from the Google API Client library.  Apparently
     * the Sql object handles the decision of whether the request
     * should be a GET or a POST.  Queries such as 'show tables'
     * and 'select' are supposed to be GETs and queries such as
     * 'insert' are supposed to be POSTS.
     *
     * See <a href="https://developers.google.com/fusiontables/docs/v1/using">https://developers.google.com/fusiontables/docs/v1/using</a>
     *
     * @param query the raw SQL string used by App Inventor
     * @param authToken the OAuth 2.0 access token
     * @return the HttpResponse if the request succeeded, or null
     */
    public com.google.api.client.http.HttpResponse sendQuery(String query, String authToken) {
        Log.i(LOG_TAG, "executing " + query);
        com.google.api.client.http.HttpResponse response = null;

        // Create a Fusiontables service object (from Google API client lib)
        Fusiontables service = new Fusiontables.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(),
                new GoogleCredential()).setApplicationName("App Inventor Fusiontables/v1.0")
                        .setJsonHttpRequestInitializer(new GoogleKeyInitializer(ApiKey())).build();

        try {

            // Construct the SQL query and get a CSV result
            Sql sql = ((Fusiontables) service).query().sql(query);
            sql.put("alt", "csv");

            // Add the authToken to authentication header
            sql.setOauthToken(authToken);

            response = sql.executeUnparsed();

        } catch (GoogleJsonResponseException e) {
            e.printStackTrace();
            errorMessage = e.getMessage();
        } catch (IOException e) {
            e.printStackTrace();
            errorMessage = e.getMessage();
        }
        return response;
    }

    /**
     * Static utility method to prettify the HttpResponse. This version uses Google API
     * HttpResponse object, which is different than Apache's
     * @param response
     * @return resultString
     */
    public static String httpResponseToString(com.google.api.client.http.HttpResponse response) {
        String resultStr = "";
        if (response != null) {
            if (response.getStatusCode() != 200) {
                resultStr = response.getStatusCode() + " " + response.getStatusMessage();
            } else {
                try {
                    resultStr = parseResponse(response.getContent());
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return resultStr;
    }

    /**
     * Handles Apache Http Response.  Uses Apache's HttpResponse object, which is different
     * from Google's.
     * @param response
     * @return The result string
     */
    public static String httpApacheResponseToString(org.apache.http.HttpResponse response) {
        String resultStr = "";
        if (response != null) {
            if (response.getStatusLine().getStatusCode() != 200) {
                resultStr = response.getStatusLine().getStatusCode() + " "
                        + response.getStatusLine().getReasonPhrase();
            } else {
                try {
                    resultStr = parseResponse(response.getEntity().getContent());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return resultStr;
    }

    /**
     * Parses the input stream returned from Http query
     * @param input
     * @return The Result String
     */
    public static String parseResponse(InputStream input) {
        String resultStr = "";
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(input));

            StringBuilder sb = new StringBuilder();

            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line + "\n");
            }
            resultStr = sb.toString();
            Log.i(LOG_TAG, "resultStr = " + resultStr);
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return resultStr;
    }

    /**
     * Callback used for error reporting.
     * @param msg
     */
    public void handleOAuthError(String msg) {
        Log.i(LOG_TAG, "handleOAuthError: " + msg);
        errorMessage = msg;
    }

    /**
     * Parses SQL API Create query into v1.0 a JSon string which is then submitted as a POST request
     * E.g., parses "
     *   CREATE TABLE Notes (NoteField: STRING,  NoteLength: NUMBER, Date:DATETIME, Location:LOCATION)"
     * into :
     *  "CREATE TABLE " +
        "{\"columns\": [{\"name\": \"NoteField\",\"type\": \"STRING\"},{\"name\": \"NoteLength\",\"type\": \"NUMBER\"}," +
        "{\"name\": \"Location\",\"type\": \"LOCATION\"},{\"name\": \"Date\",\"type\": \"DATETIME\"}], " +
        "\"isExportable\": \"true\", \"name\": \"Notes\"}"
        
     * @param query
     * @return
     */
    private String parseSqlCreateQueryToJson(String query) {
        Log.i(LOG_TAG, "parsetoJSonSqlCreate :" + query);
        StringBuilder jsonContent = new StringBuilder();
        query = query.trim();
        String tableName = query.substring("create table".length(), query.indexOf('(')).trim();
        String columnsList = query.substring(query.indexOf('(') + 1, query.indexOf(')'));
        String[] columnSpecs = columnsList.split(",");
        jsonContent.append("{'columns':[");
        for (int k = 0; k < columnSpecs.length; k++) {
            String[] nameTypePair = columnSpecs[k].split(":");
            jsonContent
                    .append("{'name': '" + nameTypePair[0].trim() + "', 'type': '" + nameTypePair[1].trim() + "'}");
            if (k < columnSpecs.length - 1) {
                jsonContent.append(",");
            }
        }
        jsonContent.append("],");
        jsonContent.append("'isExportable':'true',");
        jsonContent.append("'name': '" + tableName + "'");
        jsonContent.append("}");

        jsonContent.insert(0, "CREATE TABLE ");

        Log.i(LOG_TAG, "result = " + jsonContent.toString());
        return jsonContent.toString();
    }

    /**
     * Method for handling 'create table' SQL queries. At this point that is
     * the only query that we support using a POST request.
     *
     * TODO: Generalize this for other queries that require POST.
     *
     * @param query -- a query of the form "create table <json encoded content>"
     * @param authToken -- Oauth 2.0 access token
     * @return
     */
    private String doPostRequest(String query, String authToken) {
        org.apache.http.HttpResponse response = null;
        String jsonContent = query.trim().substring("create table".length());
        Log.i(LOG_TAG, "Http Post content = " + jsonContent);

        // Set up the POST request

        StringEntity entity = null;
        HttpPost request = new HttpPost(FUSIONTABLES_POST + "?key=" + ApiKey()); // Fusiontables Uri
        try {
            entity = new StringEntity(jsonContent);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return "Error: " + e.getMessage();
        }
        entity.setContentType("application/json");
        request.addHeader("Authorization", AUTHORIZATION_HEADER_PREFIX + authToken);
        request.setEntity(entity);

        // Execute the request

        HttpClient client = new DefaultHttpClient();
        try {
            response = client.execute(request);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
            return "Error: " + e.getMessage();
        } catch (IOException e) {
            e.printStackTrace();
            return "Error: " + e.getMessage();
        }

        // Process the response
        // A valid response will have code=200 and contain a tableId value plus other stuff.
        // We just return the table id.
        int statusCode = response.getStatusLine().getStatusCode();
        if (response != null && statusCode == 200) {
            try {
                String jsonResult = FusiontablesControl.httpApacheResponseToString(response);
                JSONObject jsonObj = new JSONObject(jsonResult);
                if (jsonObj.has("tableId")) {
                    queryResultStr = "tableId," + jsonObj.get("tableId");
                } else {
                    queryResultStr = jsonResult;
                }

            } catch (IllegalStateException e) {
                e.printStackTrace();
                return "Error: " + e.getMessage();
            } catch (JSONException e) {
                e.printStackTrace();
                return "Error: " + e.getMessage();
            }
            Log.i(LOG_TAG, "Response code = " + response.getStatusLine());
            Log.i(LOG_TAG, "Query = " + query + "\nResultStr = " + queryResultStr);
            // queryResultStr = response.getStatusLine().toString();
        } else {
            Log.i(LOG_TAG, "Error: " + response.getStatusLine().toString());
            queryResultStr = response.getStatusLine().toString();
        }

        return queryResultStr;
    }

    /**
     * First uses OAuth2Helper to acquire an access token and then sends the
     * Fusiontables query asynchronously to the server and returns the result.
     *
     * This version uses the Fusion Tabes V1.0 API.
     */
    private class QueryProcessorV1 extends AsyncTask<String, Void, String> {
        private static final String TAG = "QueryProcessorV1";

        private final Activity activity; // The main list activity
        private final ProgressDialog dialog;

        /**
         * @param activity, needed to create a progress dialog
         */
        QueryProcessorV1(Activity activity) {
            Log.i(TAG, "Creating AsyncFusiontablesQuery");
            this.activity = activity;
            dialog = new ProgressDialog(activity);
        }

        @Override
        protected void onPreExecute() {
            dialog.setMessage("Fusiontables...");
            dialog.show();
        }

        /**
         * The Oauth handshake and the API request are both handled here.
         */
        @Override
        protected String doInBackground(String... params) {
            Log.i(TAG, "Starting doInBackground " + params[0]);
            String query = params[0];
            queryResultStr = "";

            // Get a fresh access token
            OAuth2Helper oauthHelper = new OAuth2Helper();
            String authToken = oauthHelper.getRefreshedAuthToken(activity, authTokenType);

            // Make the fusiontables query

            if (authToken != null) {

                // We handle CREATE TABLE as a special case
                if (query.toLowerCase().contains("create table")) {
                    queryResultStr = doPostRequest(parseSqlCreateQueryToJson(query), authToken);
                    return queryResultStr;
                } else {

                    // Execute all other queries
                    com.google.api.client.http.HttpResponse response = sendQuery(query, authToken);

                    // Process the response
                    if (response != null) {
                        queryResultStr = httpResponseToString(response);
                        Log.i(TAG, "Query = " + query + "\nResultStr = " + queryResultStr);
                    } else {
                        queryResultStr = errorMessage;
                        Log.i(TAG, "Error:  " + errorMessage);
                    }
                    return queryResultStr;
                }
            } else {
                return OAuth2Helper.getErrorMessage();
            }
        }

        /**
         * Fires the AppInventor GotResult() method
         */
        @Override
        protected void onPostExecute(String result) {
            Log.i(LOG_TAG, "Query result " + result);
            if (result == null) {
                result = "Error";
            }
            dialog.dismiss();
            GotResult(result);
        }
    }

}