Appcelerator Platform Services SDK for Android -- Appcelerator Cloud Services

The Appcelerator Platform Services (APS) SDK for Android provides APIs for your native Android application to access Appcelerator Cloud Services (ACS).

Getting the SDK

To download and start using the SDK, you first need to create new Android application in Dashboard. See Managing Native Applications in Dashboard for details on creating a new native application. After you create an application, a service key is generated that associates your application with all the Platform services. Dashboard also provides full instructions for enabling all Platform Services in your application. This guide will deal specifically with enabling and using Appcelerator Cloud Services in an Android application.

Running the APSCloudExample Application

The SDK ZIP file includes an Android sample project that demonstrates basic use of each of the Cloud APIs. To run the sample you first need to create a new application in Dashboard to obtain the necessary service application. You will then copy this key into the imported sample project's main Activity and then run the application.

To create the APSCloudExample application in Dashboard:

  1. Login to Appcelerator Dashboard.
  2. From the Orgs menu, select the organization to associate with the application.
  3. Click the Apps menu and select Add a Native App.
  4. In the dialog that appears
    • Type APSCloudExampleApp (or other name) in the Name field.
    • Select Android from the Platform menu.
    • Select any cateogory from the Category menu.
  5. Click Next and then click the Overview tab.
  6. Click the Services tab, then click Show Key under Cloud / Performance / Analytics.
  7. Select Development from the Environment menu, then click the clipboard icon to copy the key to your clipboard.

Next, you'll import the APSCloudExample project into Eclipse, copy the key from your clipboard into the application's main activity, and run the application.

To import the completed APSCloudExample project:

  1. In ADT, select File > Import > General > Existing Code into Workspace, then click Next.
  2. Click Browse and navigate to the appcelerator-sdk-android-1.0/examples/APSCloudExample folder, and click Open.
  3. Click Finish.
  4. Open src/main/java/com/appcelerator/apscloudexample/MainActivity.java.
  5. Locate the following line of code and replace << YOUR APP KEY >> with the application key you copied to your clipboard previously.

      String appKey = "<< YOUR APP KEY >>";
    
  6. Run the application on an Android device or emulator.

Once the application is running, try the following:

  • Create a new user by selecting Users > Create User. Enter a username, password and password confirmation and then click Create. If the user is created successfully, the following dialog is shown:

  • View the newly created user in Dashboard:
    1. Open Dashboard and select your application from the Apps menu.
    2. Select Cloud > Manage Data, then click Users in the Manage Data Object table. You should see the user you created listed in the Users table.

Enabling Cloud services in a new Project

Once you've created an application in Dashboard, downloaded the SDK and obtained your application service key, there are few steps to enable Cloud service in your Android project.

To enable the Cloud service in your project:

  1. Copy appcelerator-sdk-android-1.0.0.jar to your project's libs folder.
  2. Add the following permission to your project's AndroidManifest.xml file:

     <uses-permission android:name="android.permission.INTERNET"/>`    
    
  3. Import the APSServiceManager class into the project's main Activity:

     import com.appcelerator.aps.APSServiceManager;
    
  4. Call APSServiceManager.getInstance().enable(), passing it the application context and the application key provided by Dashboard:

      APSServiceManager.getInstance().enable(getApplicationContext(), "<<YOUR APP KEY>>");
    

    At this point, your application can begin making API calls.

Making API Calls and Handling Responses

The com.appcelerator.aps package contains a collection of classes whose methods map to individual REST API method endpoints. For example, the APSUsers.create() method corresponds to the /users/create.json method endpoint.

Alternatively, you can use the generic APSCloud.sendRequest() method to make REST calls directly against the Cloud APIs. For more information, see Making Generic REST API Calls.

Note: All Cloud API calls must be made on the UI (main) thread, and callbacks are executed on the UI thread.

Building Request Parameters

The first parameter of each Cloud API method is a HashMap object that contains the of parameters to send with the request. For example, the APSPhotos.show() method takes photo_id parameter whose value is, naturally, the ID of the photo to show.

// Create dictionary of parameters to be passed with the request
HashMap<String, Object> data = new HashMap<String, Object>();
data.put("photo_id", photoId);

APSPhotos.show(data, new APSResponseHandler() { 
     ... 
});

Handling Responses

The second parameter of each method call is an instance of APSResponseHandler, an interface that has the following signature:

public interface APSResponseHandler {
    void onResponse(final APSResponse e);
    void onException(final APSCloudException e);
}

The instance you specify must override the onResponse and onException methods. The onResponse method is invoked upon completion of a Cloud API call, and the onException handler is invoked if there is an exception while communicating with the ACS server.

The APSResponse object provides getter methods to access information about the response. For instance, the getSuccess() method returns a boolean indicating if the method call was successful or not; the getResponse() method returns a JSON-encoded object with the results of the method call.

@Override
public void onResponse(final APSResponse e) {
    if (e.getSuccess()) {
        // Read JSON response
        JSONObject res = e.getResponse();
    } else {
        // Log error message:
        Log.e("LOGIN", e.getMessage());            
    }
}

The onException() handler is invoked for any exceptions that occur during communication with the ACS server.

@Override
public void onException(APSCloudException e) {
    // Handle exception
    Log(e.getErrorType(), e.getErrorCode());
}

Example: APSUsers Login Call with Response Handler

The following example logs in an existing ACS user by their username and password. After a successful login, the application updates a TextView object with the user's ACS username.

HashMap<String, Object> data = new HashMap<String, Object>();
data.put("login", "username");
data.put("password", "password");

try {
    APSUsers.login(data, new APSResponseHandler() {
        @Override
        public void onResponse(final APSResponse e) {
            if (e.getSuccess()) {
                try {
                    JSONObject res = e.getResponse();
                    // Response returns an array containing a single user
                    JSONArray payload = res.getJSONArray("users");
                    res = payload.getJSONObject(0);
                    loginTextView.setText(res.getString("username"));
                } catch (Exception e) {
                    Log.e("LOGIN", "Error parsing JSON object: " + e.toString());
                }
            }
            else {
                Log.e("LOGIN", e.getMessage());
            }

        }
        @Override
        public void onException(APSCloudException e) {
            // Handle exception that occured                
        }
    });
} catch (APSClientError e) {
    Log.e("LOGIN", e.getErrorType());
}

Monitoring Request Progress

For Cloud API methods that involve uploading large files, such as APSPhotos.create() or APSFiles.create(), there is an overloaded version that takes an optional progressHandler parameter. This parameter takes a APSProgressHandler instance, which must provide an onProgress handler. This handler is periodically triggered as the file transfer continues, and is passed an integer between 0-100 indicating the current upload progress.

Example: APSFiles Create Call with Progress Handler

The following example uploads a file from the device (/res/raw/reference.pdf) to the ACS storage server. Since the method call requires that uploaded data be an instance of java.io.File, the application needs to copy the resource to a read-write directory before uploading it. Storing the file locally requires that the WRITE_EXTERNAL_STORAGE permission be included in your AndroidManifest.xml file.

The progress callback calls the setProgress() method on a ProgressBar object, displaying the status of the upload. After the request successfully completes, the application displays a toast notification.

HashMap<String, Object> data = new HashMap<String, Object>();
String filename = "reference.pdf";

// Need to copy the resource to a read-write directory to upload it 
if (!createExternalStoragePrivateFile(R.raw.reference, filename)) return;

File file = new File(currentActivity.getExternalFilesDir(null), filename);
data.put("file", file);
data.put("name", "Reference Manual");

try {
    APSFiles.create(data, new APSClient.APSResponseHandler() {
        @Override
        public void onResponse(final APSResponse e) {
            if (e.getSuccess()) {
                APSCloud.log("PUSH", "Successfully subscribed to push!");
                progressBar.setVisibility(View.GONE);
                Toast.makeText(currentActivity, "File uploaded!", Toast.LENGTH_SHORT).show();
            }
            else {
                Log.e("UPLOAD", e.getMessage());
            }
        }
    },
    new APSClient.APSProgressHandler() {
        @Override
        public void onProgress(final int percentProgress, final boolean upload) {
            if (currentActivity != null) {
                progressBar.setProgress(percentProgress);
        }
    });
} catch (APSClientError e) {
    Log.e("UPLOAD", e.getMessage());
}

// Helper function to copy a resource to external storage, modified from:
// http://developer.android.com/reference/android/content/Context.html#getExternalFilesDir(java.lang.String)

public static boolean createExternalStoragePrivateFile(int inputResource, String filename) {
    File file = new File(currentActivity.getExternalFilesDir(null), filename);
    try {
        InputStream is = currentActivity.getResources().openRawResource(inputResource);
        OutputStream os = new FileOutputStream(file);
        byte[] data = new byte[is.available()];
        is.read(data);
        os.write(data);
        is.close();
        os.close();
        return true;
    } catch (IOException e) {
        Log.w("ExternalStorage", "Error writing " + file, e);
        return false;
    }
}

Making Generic REST APIs Method Calls

The APSCloud.sendRequest() method lets you easily make REST API calls directly against ACS, rather than using the specialized classes (like APSUsers). In general, you should use the specialized classes as they provide an easier API. However, if new REST methods are deployed to the APS Cloud backend, this approach lets you immediately start using those methods without waiting for an update to the SDK.

To make a generic request, you call APSCloud.getInstance() to get a reference to the shared APSCloud object and call its sendRequest() method. For each call, you must specify the following:

  • REST API method endpoint relative to "api.cloud.appcelerator.com/v1". Method endpoints are listed in the corresponding entries in the REST API documentation.
  • The HTTP method to use.
  • Data to send with the request.

For example, to create a post, pass the sendRequest() method the following information:

  • REST API method endpoint: posts/create.json
  • The HTTP method to use: POST
  • Data to send with the request: at minimum, you must specify the content property.

The following uses the sendRequest() API to create a new Post object.

HashMap<String, Object> data = new HashMap<String, Object>();
data.put("title", "What's up?");
data.put("content", "The sun, the cloud, space...");

try {
    APSCloud.getInstance().sendRequest("posts/create.json", "POST", data, new APSClient.APSResponseHandler() {
        public void onResponse(final APSResponse e) {
            if (e.getSuccess()) {
                try {
                    JSONObject res = e.getResponse();
                    JSONArray payload = res.getJSONArray("posts");
                    res = payload.getJSONObject(0);
                    latestPost.setText(res.getString("title"));
                } catch (Exception err) {
                    Log.e("REST", "JSON Error: " + err.getMessage());
                }
            }
            else {
                Log.e("REST", e.getMessage());
            }
        }
    });
} catch (APSClientError e) {
    Log.e("REST", "Error: " + e.getMessage());
}

Working with Push Notifications

The APSPushNotifications class lets your application subscribe, send and receive push notifications. To use this class, you also need APSCloudPush class, which provides the underlying services to handle incoming push notifications.

Your application must call APSServiceManager.getInstance() before calling any methods on APSCloudPush, otherwise an exception will be thrown.

CloudPush sample application

The SDK includes the APSCloudPushExample application that demonstrates use of the APSPushNotifications and APSCloudPush APIs. To run the sample application, you'll first need to create an Android application in Dashboard (or use an existing application), and configure its push notification settings to include a GCM sender ID and application key. For more information, see Configuring push services for Android devices. APSCloudPush requires Google Play services, so you'll also need to add that library to your project's libs/ folder.

To import and run the APSCloudPushExample application:

  1. In ADT, select File > Import > General > Existing Projects into Workspace and click Browse.
  2. Navigate to the appcelerator-sdk-android-1.0.0/examples/APSCloudPushExample folder and click Open.
  3. Click Finish to import the project.
  4. Add the Google Play services to your project (see Android Project Requirements for instructions).
  5. In MainActivity.java, locate the following line and replace << YOUR APP KEY >> with the application key generated by Dashboard (see instructions):

      String appKey = "<< YOUR APP KEY >>";
    
  6. Run the application in an Android device or emulator.

Android Project Requirements for using APSCloudPush

Once you have configured your GCM settings in Dashboard there are some required configuration changes to your Android project to use APSCloudPush.

Add Google Play servicesAPSCloudPush requires that Google Play services be included in your application.

  1. Download Google Play Services.
  2. Create a folder in the root of the application named libs/, if one does not already exist.
  3. Add the google-play-services.jar to the libs/ folder. (This file can be located in $ANDROID_SDK/extras/google/google_play_services/libproject/google-play-services_lib/libs.) If you are using Android Developer Tools/Eclipse, also make sure the google-play-services.jar is exported from the Java Build Path.

AndroidManifest changes — The following changes be added to your project's AndroidManifest.xml to use APSCloudPush. Replace each occurence of "YOURAPPSPACKAGENAME" with the actual package name of your application.

  • Inside the <manifest/> element:

      <uses-permission android:name="android.permission.INTERNET"/>
      <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
      <uses-permission android:name="android.permission.WAKE_LOCK"/>
      <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
      <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
      <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
      <uses-permission android:name="android.permission.VIBRATE"/>
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
               android:maxSdkVersion="18" />
      <permission android:name="YOURAPPSPACKAGENAME.permission.C2D_MESSAGE"
                  android:protectionLevel="signature"/>
      <uses-permission android:name="YOURAPPSPACKAGENAME.permission.C2D_MESSAGE"/>
    
  • Inside the <application/> element:

      <receiver android:name="com.appcelerator.aps.IntentReceiver"/>
      <receiver
              android:name="com.appcelerator.aps.GCMReceiver"
              android:permission="com.google.android.c2dm.permission.SEND">
          <intent-filter>
              <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
              <category android:name="YOURAPPSPACKAGENAME"/>
          </intent-filter>
      </receiver>
      <receiver android:name="com.appcelerator.aps.PushBroadcastReceiver"
              android:permission="com.google.android.c2dm.permission.SEND">
          <intent-filter>
              <action android:name="android.intent.action.BOOT_COMPLETED"/>
              <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
              <action android:name="com.appcelerator.aps.intent.DEL_GROUPED_MSG" />
              <category android:name="YOURAPPSPACKAGENAME" />
          </intent-filter>
      </receiver>
      <receiver android:name="com.appcelerator.aps.PushBroadcastReceiver">
          <intent-filter>
              <action android:name="android.intent.action.PACKAGE_ADDED"/>
              <action android:name="android.intent.action.PACKAGE_REPLACED"/>
              <data android:scheme="package" android:path="YOURAPPSPACKAGENAME" />
          </intent-filter>
      </receiver>