Android Quickstart

Complete the steps described in the rest of this page, and in about ten minutes you'll have a simple Android application that makes requests to the Google Apps Script Execution API.


To run this quickstart, you'll need:

This quickstart will assume you are using the Android Studio IDE (as opposed to the stand-alone SDK Tools) and are comfortable finding, creating and editing files within a Studio project.

Step 1: Acquire a SHA1 fingerprint

In a terminal, run the folowing Keytool utility command to get the SHA1 fingerprint you will use to enable the API.

keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v

When asked for a keystore password, enter "android".

The Keytool prints the fingerprint to the shell. For example:

$ keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v
Enter keystore password: Type "android" if using debug.keystore
Alias name: androiddebugkey
Creation date: Dec 4, 2014
Entry type: PrivateKeyEntry
Certificate chain length: 1
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 503bd581
Valid from: Mon Aug 27 13:16:01 PDT 2012 until: Wed Aug 20 13:16:01 PDT 2042
Certificate fingerprints:
   MD5:  1B:2B:2D:37:E1:CE:06:8B:A0:F0:73:05:3C:A3:63:DD
   SHA1: D8:AA:43:97:59:EE:C5:95:26:6A:07:EE:1C:37:8E:F4:F0:C8:05:C8
   SHA256: F3:6F:98:51:9A:DF:C3:15:4E:48:4B:0F:91:E3:3C:6A:A0:97:DC:0A:3F:B2:D2:E1:FE:23:57:F5:EB:AC:13:30
   Signature algorithm name: SHA1withRSA
   Version: 3

Copy the SHA1 fingerprint, which is highlighted in the example above.

Step 2: Turn on the Google Apps Script Execution API

  1. Open your target Apps Script in the editor and select Resources > Developers Console Project.
  2. In the dialog that opens, click on the blue link (that starts with your script's name) at the top to open the console project associated with your script.
  3. The left sidebar should say API Manager. If it does not, click the icon in the upper-left to open a side panel and select API Manager. Select Library in the left sidebar.
  4. In the search bar under the Google APIs tab, enter "Google Apps Script Execution API". Click the same name in the list that appears. In the new tab that opens, click Enable API.
  5. Click Credentials in the left sidebar.
  6. Select the Credentials tab, click the Create credentials button and select OAuth client ID.
  7. Select the application type Android.
  8. Copy the SHA1 fingerprint from Step 1 into the Signing-certificate fingerprint field.
  9. In the Package name field, enter com.example.quickstart.
  10. Click the Create button.

Step 3: Create a new Android project

  1. Open Android Studio, and start a New Android Studio Project.
  2. In the New Project screen, name the application "Quickstart".
  3. Set Company Domain to "" and verify that the package name automatically produced matches the one you entered into the Developer Console in Step 2. Click Next.
  4. In the Target Android Devices screen, check the Phone and Tablet checkbox and choose a Minimum SDK of "API 11: Android 3.0 (Honeycomb)". Leave the other checkboxes unchecked. Click Next.
  5. In the Add an activity to Mobile screen, click Add No Activity.
  6. Click Finish.

At this point Android Studio will create and open the project.

Step 4: Prepare the project

In the Project sidebar you will see an expandable list of the default project files Android Studio has created. One of these is the "build.gradle" file associated with the "app" module (not the project).

  1. Open the app build.gradle file and replace its contents with the following:
  2. In the toolbar, select Tools > Android > Sync Project with Gradle Files. This will acquire and make available the libraries your project needs.
  3. Find and open the default src/main/AndroidManifest.xml file and replace its contents with the following code:
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android=""
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.GET_ACCOUNTS" />
            android:label="Google Apps Script Execution API Android Quickstart"
            android:theme="@style/AppTheme" >
                android:label="Google Apps Script Execution API Android Quickstart" >
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />

Step 5: Setup the sample

Create a new Java class called "MainActivity" and replace the contents of the new file with the following code. Be sure to replace the scriptId variable in getDataFromApi() with the ID of your target script:

package com.example.quickstart;


import java.util.Map;

import android.Manifest;
import android.accounts.AccountManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;

public class MainActivity extends Activity
    implements EasyPermissions.PermissionCallbacks {
    GoogleAccountCredential mCredential;
    private TextView mOutputText;
    private Button mCallApiButton;
    ProgressDialog mProgress;

    static final int REQUEST_ACCOUNT_PICKER = 1000;
    static final int REQUEST_AUTHORIZATION = 1001;
    static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
    static final int REQUEST_PERMISSION_GET_ACCOUNTS = 1003;

    private static final String BUTTON_TEXT = "Call Google Apps Script Execution API";
    private static final String PREF_ACCOUNT_NAME = "accountName";
    private static final String[] SCOPES = { "" };

     * Create the main activity.
     * @param savedInstanceState previously saved instance data.
    protected void onCreate(Bundle savedInstanceState) {
        LinearLayout activityLayout = new LinearLayout(this);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
        activityLayout.setPadding(16, 16, 16, 16);

        ViewGroup.LayoutParams tlp = new ViewGroup.LayoutParams(

        mCallApiButton = new Button(this);
        mCallApiButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {

        mOutputText = new TextView(this);
        mOutputText.setPadding(16, 16, 16, 16);
        mOutputText.setMovementMethod(new ScrollingMovementMethod());
                "Click the \'" + BUTTON_TEXT +"\' button to test the API.");

        mProgress = new ProgressDialog(this);
        mProgress.setMessage("Calling Google Apps Script Execution API ...");


        // Initialize credentials and service object.
        mCredential = GoogleAccountCredential.usingOAuth2(
                getApplicationContext(), Arrays.asList(SCOPES))
                .setBackOff(new ExponentialBackOff());

     * Extend the given HttpRequestInitializer (usually a credentials object)
     * with additional initialize() instructions.
     * @param requestInitializer the initializer to copy and adjust; typically
     *         a credential object.
     * @return an initializer with an extended read timeout.
    private static HttpRequestInitializer setHttpTimeout(
            final HttpRequestInitializer requestInitializer) {
        return new HttpRequestInitializer() {
            public void initialize(HttpRequest httpRequest)
                    throws {
                // This allows the API to call (and avoid timing out on)
                // functions that take up to 6 minutes to complete (the maximum
                // allowed script run time), plus a little overhead.

     * Attempt to call the API, after verifying that all the preconditions are
     * satisfied. The preconditions are: Google Play Services installed, an
     * account was selected and the device currently has online access. If any
     * of the preconditions are not satisfied, the app will prompt the user as
     * appropriate.
    private void getResultsFromApi() {
        if (! isGooglePlayServicesAvailable()) {
        } else if (mCredential.getSelectedAccountName() == null) {
        } else if (! isDeviceOnline()) {
            mOutputText.setText("No network connection available.");
        } else {
            new MakeRequestTask(mCredential).execute();

     * Attempts to set the account used with the API credentials. If an account
     * name was previously saved it will use that one; otherwise an account
     * picker dialog will be shown to the user. Note that the setting the
     * account to use with the credentials object requires the app to have the
     * GET_ACCOUNTS permission, which is requested here if it is not already
     * present. The AfterPermissionGranted annotation indicates that this
     * function will be rerun automatically whenever the GET_ACCOUNTS permission
     * is granted.
    private void chooseAccount() {
        if (EasyPermissions.hasPermissions(
                this, Manifest.permission.GET_ACCOUNTS)) {
            String accountName = getPreferences(Context.MODE_PRIVATE)
                    .getString(PREF_ACCOUNT_NAME, null);
            if (accountName != null) {
            } else {
                // Start a dialog from which the user can choose an account
        } else {
            // Request the GET_ACCOUNTS permission via a user dialog
                    "This app needs to access your Google account (via Contacts).",

     * Called when an activity launched here (specifically, AccountPicker
     * and authorization) exits, giving you the requestCode you started it with,
     * the resultCode it returned, and any additional data from it.
     * @param requestCode code indicating which activity result is incoming.
     * @param resultCode code indicating the result of the incoming
     *     activity result.
     * @param data Intent (containing result data) returned by incoming
     *     activity result.
    protected void onActivityResult(
            int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch(requestCode) {
                if (resultCode != RESULT_OK) {
                            "This app requires Google Play Services. Please install " +
                            "Google Play Services on your device and relaunch this app.");
                } else {
            case REQUEST_ACCOUNT_PICKER:
                if (resultCode == RESULT_OK && data != null &&
                        data.getExtras() != null) {
                    String accountName =
                    if (accountName != null) {
                        SharedPreferences settings =
                        SharedPreferences.Editor editor = settings.edit();
                        editor.putString(PREF_ACCOUNT_NAME, accountName);
                if (resultCode == RESULT_OK) {

     * Respond to requests for permissions at runtime for API 23 and above.
     * @param requestCode The request code passed in
     *     requestPermissions(, String, int, String[])
     * @param permissions The requested permissions. Never null.
     * @param grantResults The grant results for the corresponding permissions
     *     which is either PERMISSION_GRANTED or PERMISSION_DENIED. Never null.
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
                requestCode, permissions, grantResults, this);

     * Callback for when a permission is granted using the EasyPermissions
     * library.
     * @param requestCode The request code associated with the requested
     *         permission
     * @param list The requested permission list. Never null.
    public void onPermissionsGranted(int requestCode, List<String> list) {
        // Do nothing.

     * Callback for when a permission is denied using the EasyPermissions
     * library.
     * @param requestCode The request code associated with the requested
     *         permission
     * @param list The requested permission list. Never null.
    public void onPermissionsDenied(int requestCode, List<String> list) {
        // Do nothing.

     * Checks whether the device currently has a network connection.
     * @return true if the device has a network connection, false otherwise.
    private boolean isDeviceOnline() {
        ConnectivityManager connMgr =
                (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        return (networkInfo != null && networkInfo.isConnected());

     * Check that Google Play services APK is installed and up to date.
     * @return true if Google Play Services is available and up to
     *     date on this device; false otherwise.
    private boolean isGooglePlayServicesAvailable() {
        GoogleApiAvailability apiAvailability =
        final int connectionStatusCode =
        return connectionStatusCode == ConnectionResult.SUCCESS;

     * Attempt to resolve a missing, out-of-date, invalid or disabled Google
     * Play Services installation via a user dialog, if possible.
    private void acquireGooglePlayServices() {
        GoogleApiAvailability apiAvailability =
        final int connectionStatusCode =
        if (apiAvailability.isUserResolvableError(connectionStatusCode)) {

     * Display an error dialog showing that Google Play Services is missing
     * or out of date.
     * @param connectionStatusCode code describing the presence (or lack of)
     *     Google Play Services on this device.
    void showGooglePlayServicesAvailabilityErrorDialog(
            final int connectionStatusCode) {
        GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
        Dialog dialog = apiAvailability.getErrorDialog(

     * An asynchronous task that handles the Google Apps Script Execution API call.
     * Placing the API calls in their own task ensures the UI stays responsive.
    private class MakeRequestTask extends AsyncTask<Void, Void, List<String>> {
        private mService = null;
        private Exception mLastError = null;

        MakeRequestTask(GoogleAccountCredential credential) {
            HttpTransport transport = AndroidHttp.newCompatibleTransport();
            JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
            mService = new
                    transport, jsonFactory, setHttpTimeout(credential))
                    .setApplicationName("Google Apps Script Execution API Android Quickstart")

         * Background task to call Google Apps Script Execution API.
         * @param params no parameters needed for this task.
        protected List<String> doInBackground(Void... params) {
            try {
                return getDataFromApi();
            } catch (Exception e) {
                mLastError = e;
                return null;

         * Call the API to run an Apps Script function that returns a list
         * of folders within the user's root directory on Drive.
         * @return list of String folder names and their IDs
         * @throws IOException
        private List<String> getDataFromApi()
                throws IOException, GoogleAuthException {
            // ID of the script to call. Acquire this from the Apps Script editor,
            // under Publish > Deploy as API executable.
            String scriptId = "ENTER_YOUR_SCRIPT_ID_HERE";

            List<String> folderList = new ArrayList<String>();

            // Create an execution request object.
            ExecutionRequest request = new ExecutionRequest()

            // Make the request.
            Operation op =
                    mService.scripts().run(scriptId, request).execute();

            // Print results of request.
            if (op.getError() != null) {
                throw new IOException(getScriptError(op));
            if (op.getResponse() != null &&
                    op.getResponse().get("result") != null) {
                // The result provided by the API needs to be cast into
                // the correct type, based upon what types the Apps Script
                // function returns. Here, the function returns an Apps
                // Script Object with String keys and values, so must be
                // cast into a Java Map (folderSet).
                Map<String, String> folderSet =
                        (Map<String, String>)(op.getResponse().get("result"));

                for (String id: folderSet.keySet()) {
                            String.format("%s (%s)", folderSet.get(id), id));

            return folderList;

         * Interpret an error response returned by the API and return a String
         * summary.
         * @param op the Operation returning an error response
         * @return summary of error response, or null if Operation returned no
         *     error
        private String getScriptError(Operation op) {
            if (op.getError() == null) {
                return null;

            // Extract the first (and only) set of error details and cast as a Map.
            // The values of this map are the script's 'errorMessage' and
            // 'errorType', and an array of stack trace elements (which also need to
            // be cast as Maps).
            Map<String, Object> detail = op.getError().getDetails().get(0);
            List<Map<String, Object>> stacktrace =
                    (List<Map<String, Object>>)detail.get("scriptStackTraceElements");

            java.lang.StringBuilder sb =
                    new StringBuilder("\nScript error message: ");

            if (stacktrace != null) {
                // There may not be a stacktrace if the script didn't start
                // executing.
                sb.append("\nScript error stacktrace:");
                for (Map<String, Object> elem : stacktrace) {
                    sb.append("\n  ");
            return sb.toString();

        protected void onPreExecute() {

        protected void onPostExecute(List<String> output) {
            if (output == null || output.size() == 0) {
                mOutputText.setText("No results returned.");
            } else {
                output.add(0, "Data retrieved using the Google Apps Script Execution API:");
                mOutputText.setText(TextUtils.join("\n", output));

        protected void onCancelled() {
            if (mLastError != null) {
                if (mLastError instanceof GooglePlayServicesAvailabilityIOException) {
                            ((GooglePlayServicesAvailabilityIOException) mLastError)
                } else if (mLastError instanceof UserRecoverableAuthIOException) {
                            ((UserRecoverableAuthIOException) mLastError).getIntent(),
                } else {
                    mOutputText.setText("The following error occurred:\n"
                            + mLastError.getMessage());
            } else {
                mOutputText.setText("Request cancelled.");

Step 6: Run the app

  1. You can test the app by clicking the Run > Run app menu item.
  2. You will be prompted to select a connected device (recommended) or emulation to run the app on. If you choose to run on an emulation, make sure it is configured to use one of the with Google APIs system images. If you attempt to run the quickstart on a device that does not currently have Google Play services installed, the quickstart will produce a dialog from which you can install it.
  3. If running on an emulator, allow it to fully start and establish its network connection.
  4. If starting the emulator for the first time, you may need to unlock its screen. Regardless, the quickstart app should start automatically.
  5. The first time you run the app it will prompt you to specify an account. Select one of the ones suggested or select Add account and follow the instruction prompts to choose an account to connect to.
  6. After selecting an account, the app will prompt you to authorize access. Click OK to authorize.


  • Authorization information is stored with the app, so subsequent executions will not prompt for authorization.

Further reading


Unregistered Android application

When the OAuth dialog contains an entry that reads "Unregistered Android application" it means that the OAuth2 client ID you created in Step 2 cannot be found and Android is falling back to a default client. The default client won't be configured to use this API, so requests will fail with errors like accessNotConfigured or mention the default project number 608941808256.

To fix the issue, make sure the SHA1 fingerprint you retrieved in Step 1 and the applicationId listed in your build.gradle file match exactly with the values you set in the Google Developers console.

Send feedback about...

Apps Script
Apps Script