You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by ro...@apache.org on 2014/11/13 02:40:46 UTC

[3/6] incubator-usergrid git commit: Merged ported Apigee android sdk to 1.0

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/21b60593/sdks/android/src/main/java/org/apache/usergrid/android/sdk/UGClient.java
----------------------------------------------------------------------
diff --git a/sdks/android/src/main/java/org/apache/usergrid/android/sdk/UGClient.java b/sdks/android/src/main/java/org/apache/usergrid/android/sdk/UGClient.java
new file mode 100755
index 0000000..61b069f
--- /dev/null
+++ b/sdks/android/src/main/java/org/apache/usergrid/android/sdk/UGClient.java
@@ -0,0 +1,3160 @@
+package org.apache.usergrid.android.sdk;
+
+import static org.apache.usergrid.android.sdk.utils.ObjectUtils.isEmpty;
+import static org.apache.usergrid.android.sdk.utils.UrlUtils.addQueryParams;
+import static org.apache.usergrid.android.sdk.utils.UrlUtils.encodeParams;
+import static org.apache.usergrid.android.sdk.utils.UrlUtils.path;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.Date;
+
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationListener;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+
+import org.apache.usergrid.android.sdk.URLConnectionFactory;
+import org.apache.usergrid.android.sdk.callbacks.ApiResponseCallback;
+import org.apache.usergrid.android.sdk.callbacks.ClientAsyncTask;
+import org.apache.usergrid.android.sdk.callbacks.GroupsRetrievedCallback;
+import org.apache.usergrid.android.sdk.callbacks.QueryResultsCallback;
+import org.apache.usergrid.android.sdk.entities.Activity;
+import org.apache.usergrid.android.sdk.entities.Collection;
+import org.apache.usergrid.android.sdk.entities.Device;
+import org.apache.usergrid.android.sdk.entities.Entity;
+import org.apache.usergrid.android.sdk.entities.Group;
+import org.apache.usergrid.android.sdk.entities.Message;
+import org.apache.usergrid.android.sdk.entities.User;
+import org.apache.usergrid.android.sdk.response.ApiResponse;
+import org.apache.usergrid.android.sdk.utils.DeviceUuidFactory;
+import org.apache.usergrid.android.sdk.utils.JsonUtils;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+
+
+/**
+ * The UGClient class for accessing the Usergrid API. Start by instantiating this
+ * class though the appropriate constructor. Most calls to the API will be handled
+ * by the methods in this class.
+ * 
+ * @see org.apache.usergrid.android.sdk.UGClient
+ * @see <a href="http://apigee.com/docs/app-services/content/installing-apigee-sdk-android">Usergrid SDK install guide</a>
+ */
+public class UGClient {
+
+    /**
+     * Most current version of the Usergrid Android SDK
+     */
+    public static final String SDK_VERSION  = "0.0.8";
+    /**
+     * Platform type of this SDK
+     */
+    public static final String SDK_TYPE     = "Android";
+
+    /**
+     * @y.exclude
+     */
+    public static final String OPTION_KEY_BASE_URL = "baseURL";
+
+    /**
+     * @y.exclude
+     */
+    public static boolean FORCE_PUBLIC_API = false;
+
+    /** 
+     * Public API
+     */
+    public static String PUBLIC_API_URL = "https://api.usergrid.com";
+
+    /** 
+     * Local API of standalone server
+     */
+    public static String LOCAL_STANDALONE_API_URL = "http://localhost:8080";
+
+    /**
+     * Local API of Tomcat server in Eclipse
+     */
+    public static String LOCAL_TOMCAT_API_URL = "http://localhost:8080/ROOT";
+
+    /**
+     * Local API
+     */
+    public static String LOCAL_API_URL = LOCAL_STANDALONE_API_URL;
+
+    /**
+     * Standard HTTP methods use in generic request methods
+     * @see apiRequest 
+     * @see doHttpRequest
+     */
+    protected static final String HTTP_METHOD_DELETE = "DELETE";
+    /**
+     * Standard HTTP methods use in generic request methods
+     * @see apiRequest 
+     * @see doHttpRequest
+     */
+    protected static final String HTTP_METHOD_GET    = "GET";
+    /**
+     * Standard HTTP methods use in generic request methods
+     * @see apiRequest 
+     * @see doHttpRequest
+     */
+    protected static final String HTTP_METHOD_POST   = "POST";
+    /**
+     * Standard HTTP methods use in generic request methods
+     * @see apiRequest 
+     * @see doHttpRequest
+     */
+    protected static final String HTTP_METHOD_PUT    = "PUT";
+    
+    protected static final String LOGGING_TAG    = "UGCLIENT";
+
+    private String apiUrl = PUBLIC_API_URL;
+
+    private String organizationId;
+    private String applicationId;
+    private String clientId;
+    private String clientSecret;
+
+    private User loggedInUser = null;
+
+    private String accessToken = null;
+
+    private String currentOrganization = null;
+    private URLConnectionFactory urlConnectionFactory = null;
+    
+    private LocationManager locationManager;
+    private UUID deviceID;
+    
+    /**
+    * Interface for EntityQuery and QueueQuery
+    */
+    public interface Query {
+
+        public ApiResponse getResponse();
+
+        public boolean more();
+
+        public Query next();
+
+    }
+
+    /**
+     * @y.exclude
+     */
+    public static boolean isUuidValid(UUID uuid) {
+    	return( uuid != null );
+    }
+
+    protected static String arrayToDelimitedString(String[] arrayOfStrings, String delimiter) {
+    	StringBuilder sb = new StringBuilder();
+    	
+    	for( int i = 0; i < arrayOfStrings.length; ++i ) {
+    		if( i > 0 ) {
+    			sb.append(delimiter);
+    		}
+    		
+    		sb.append(arrayOfStrings[i]);
+    	}
+    	
+    	return sb.toString();
+    }
+    
+    protected static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
+    	if (str == null) {
+    		return null;
+    	}
+
+    	StringTokenizer st = new StringTokenizer(str, delimiters);
+    	
+    	int numTokens = st.countTokens();
+    	List<String> listTokens;
+    	
+    	if( numTokens > 0 ) {
+
+    		listTokens = new ArrayList<String>(numTokens);
+
+    		while (st.hasMoreTokens()) {
+
+    			String token = st.nextToken();
+
+    			if (trimTokens) {
+    				token = token.trim();
+    			}
+
+    			if (!ignoreEmptyTokens || token.length() > 0) {
+    				listTokens.add(token);
+    			}
+    		}
+    	} else {
+    		listTokens = new ArrayList<String>();
+    	}
+    	
+    	return listTokens.toArray(new String[listTokens.size()]);
+    }
+    
+
+    /****************** CONSTRUCTORS ***********************/
+    /****************** CONSTRUCTORS ***********************/
+
+    /**
+     * @y.exclude
+     */
+    public UGClient() {
+        init();
+    }
+
+    /**
+     * Instantiate a data client for a specific app. This is used to call most 
+     * SDK methods.
+     * 
+     * @param  organizationId  the Usergrid organization name
+     * @param  applicationId  the Usergrid application id or name
+     */
+    public UGClient(String organizationId, String applicationId) {
+        init();
+        this.organizationId = organizationId;
+        this.applicationId = applicationId;        
+    }
+
+    /**
+     * Instantiate a data client for a specific app with a base URL other than the default
+     * api.usergrid.com. This is used to call most SDK methods.
+     * 
+     * @param  organizationId  the Usergrid organization name
+     * @param  applicationId  the Usergrid application id or name
+     * @param  baseURL  the base URL to use for all API calls
+     */
+    public UGClient(String organizationId, String applicationId, String baseURL) {
+        init();
+        this.organizationId = organizationId;
+        this.applicationId = applicationId;
+        
+        if( baseURL != null ) {
+        	this.setApiUrl(baseURL);
+        }
+    }
+
+    public void init() {
+    }
+    
+
+    /****************** ACCESSORS/MUTATORS ***********************/
+    /****************** ACCESSORS/MUTATORS ***********************/
+
+    /**
+     * Sets a new URLConnectionFactory object in the UGClient
+     *
+     * @param  urlConnectionFactory  a new URLConnectionFactory object
+     * @y.exclude
+     */
+    public void setUrlConnectionFactory(URLConnectionFactory urlConnectionFactory) {
+    	this.urlConnectionFactory = urlConnectionFactory;
+    }
+
+    /**
+     * @return the Usergrid API url (default: http://api.usergrid.com)
+     */
+    public String getApiUrl() {
+        return apiUrl;
+    }
+
+    /**
+     * Sets the base URL for API requests
+     *
+     * @param apiUrl the API base url to be set (default: http://api.usergrid.com)
+     */
+    public void setApiUrl(String apiUrl) {
+        this.apiUrl = apiUrl;
+    }
+
+    /**
+     * Sets the base URL for API requests and returns the updated UGClient object
+     *
+     * @param apiUrl the Usergrid API url (default: http://api.usergrid.com)
+     * @return UGClient object for method call chaining
+     */
+    public UGClient withApiUrl(String apiUrl) {
+        this.apiUrl = apiUrl;
+        return this;
+    }
+    
+    
+    /**
+     * Sets the Usergrid organization ID and returns the UGClient object
+     *
+     * @param  organizationId  the organizationId to set
+     * @return  the updated UGClient object
+     */
+    public UGClient withOrganizationId(String organizationId){
+        this.organizationId = organizationId;
+        return this;
+    }
+    
+    
+
+    /**
+     * Gets the current Usergrid organization ID set in the UGClient
+     *
+     * @return the current organizationId
+     */
+    public String getOrganizationId() {
+        return organizationId;
+    }
+
+    /**
+     * Sets the Usergrid organization ID
+     *
+     * @param  organizationId  the organizationId to set     
+     */
+    public void setOrganizationId(String organizationId) {
+        this.organizationId = organizationId;
+    }
+
+    /**
+     * Gets the current Usergrid application ID set in the UGClient
+     *
+     * @return the current organizationId or name
+     */
+    public String getApplicationId() {
+        return applicationId;
+    }
+
+    /**
+     * Sets the Usergrid application Id
+     *
+     * @param  applicationId  the application id or name
+     */
+    public void setApplicationId(String applicationId) {
+        this.applicationId = applicationId;
+    }
+   
+
+    /**
+     * Sets the Usergrid application ID and returns the UGClient object
+     *
+     * @param  applicationId  the application ID to set
+     * @return  the updated UGClient object
+     */
+    public UGClient withApplicationId(String applicationId) {
+        this.applicationId = applicationId;
+        return this;
+    }
+
+    /**
+     * Gets the application (not organization) client ID credential for making calls as the 
+     * application-owner. Not safe for most mobile use. 
+     * @return the client id 
+     */
+    public String getClientId() {
+        return clientId;
+    }
+
+    /**
+     * Sets the application (not organization) client ID credential, used for making 
+     * calls as the application-owner. Not safe for most mobile use.
+     * @param clientId the client id 
+     */
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    /**
+     * Sets the client ID credential in the UGClient object. Not safe for most mobile use.
+     *
+     * @param clientId the client key id
+     * @return UGClient object for method call chaining
+     */
+    public UGClient withClientId(String clientId) {
+        this.clientId = clientId;
+        return this;
+    }
+
+    /**
+     * Gets the application (not organization) client secret credential for making calls as the 
+     * application-owner. Not safe for most mobile use. 
+     * @return the client secret 
+     */
+    public String getClientSecret() {
+        return clientSecret;
+    }
+
+    /**
+     * Sets the application (not organization) client secret credential, used for making 
+     * calls as the application-owner. Not safe for most mobile use.
+     *
+     * @param clientSecret the client secret 
+     */
+    public void setClientSecret(String clientSecret) {
+        this.clientSecret = clientSecret;
+    }
+
+    /**
+     * Sets the client secret credential in the UGClient object. Not safe for most mobile use.
+     *
+     * @param clientSecret the client secret
+     * @return UGClient object for method call chaining
+     */
+    public UGClient withClientSecret(String clientSecret) {
+        this.clientSecret = clientSecret;
+        return this;
+    }
+
+    /**
+     * Gets the UUID of the logged-in user after a successful authorizeAppUser request
+     * @return the UUID of the logged-in user
+     */
+    public User getLoggedInUser() {
+        return loggedInUser;
+    }
+
+    /**
+     * Sets the UUID of the logged-in user. Usually not set by host application
+     * @param loggedInUser the UUID of the logged-in user
+     */
+    public void setLoggedInUser(User loggedInUser) {
+        this.loggedInUser = loggedInUser;
+    }
+
+    /**
+     * Gets the OAuth2 access token for the current logged-in user after a 
+     * successful authorize request
+     *
+     * @return the OAuth2 access token
+     */
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    /**
+     * Saves the OAuth2 access token in the UGClient after a successful authorize
+     * request. Usually not set by host application.
+     *
+     * @param accessToken an OAuth2 access token
+     */
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    /**
+     * Gets the current organization from UGClient 
+     *
+     * @return the currentOrganization
+     */
+    public String getCurrentOrganization() {
+        return currentOrganization;
+    }
+
+    /**     
+     * Sets the current organizanization from UGClient 
+     *
+     * @param currentOrganization The organization this data client should use.
+     */
+    public void setCurrentOrganization(String currentOrganization) {
+        this.currentOrganization = currentOrganization;
+    }
+
+    /****************** LOGGING ***********************/
+    /****************** LOGGING ***********************/
+
+
+    /**
+     * Logs a trace-level logging message with tag 'DATA_CLIENT'
+     *
+     * @param   logMessage  the message to log
+     */
+    public void logTrace(String logMessage) {
+        if( logMessage != null ) {
+            Log.v(LOGGING_TAG,logMessage);
+        }
+    }
+    
+    /**
+     * Logs a debug-level logging message with tag 'DATA_CLIENT'
+     *
+     * @param   logMessage  the message to log
+     */
+    public void logDebug(String logMessage) {
+        if( logMessage != null ) {
+            Log.d(LOGGING_TAG,logMessage);
+        }
+    }
+    
+    /**
+     * Logs an info-level logging message with tag 'DATA_CLIENT'
+     *
+     * @param   logMessage  the message to log
+     */
+    public void logInfo(String logMessage) {
+        if( logMessage != null ) {
+            Log.i(LOGGING_TAG,logMessage);
+        }
+    }
+    
+    /**
+     * Logs a warn-level logging message with tag 'DATA_CLIENT'
+     *
+     * @param   logMessage  the message to log
+     */
+    public void logWarn(String logMessage) {
+        if( logMessage != null ) {
+            Log.w(LOGGING_TAG,logMessage);
+        }
+    }
+    
+    /**
+     * Logs an error-level logging message with tag 'DATA_CLIENT'
+     *
+     * @param   logMessage  the message to log
+     */
+    public void logError(String logMessage) {
+        if( logMessage != null ) {
+            Log.e(LOGGING_TAG,logMessage);
+        }
+    }
+
+    /**
+     * Logs a debug-level logging message with tag 'DATA_CLIENT'
+     *
+     * @param   logMessage  the message to log
+     */
+    public void writeLog(String logMessage) {
+        if( logMessage != null ) {
+            //TODO: do we support different log levels in this class?
+            Log.d(LOGGING_TAG, logMessage);
+        }
+    }
+    
+    /****************** API/HTTP REQUEST ***********************/
+    /****************** API/HTTP REQUEST ***********************/
+
+    /**
+     *  Forms and initiates a raw synchronous http request and processes the response.
+     *
+     *  @param  httpMethod the HTTP method in the format: 
+     *      HTTP_METHOD_<method_name> (e.g. HTTP_METHOD_POST)
+     *  @param  params the URL parameters to append to the request URL
+     *  @param  data the body of the request
+     *  @param  segments  additional URL path segments to append to the request URL 
+     *  @return  ApiResponse object
+     */
+	public ApiResponse doHttpRequest(String httpMethod, Map<String, Object> params, Object data, String... segments) {
+		
+        ApiResponse response = null;
+		OutputStream out = null;
+		InputStream in = null;
+		HttpURLConnection conn = null;
+		
+		String urlAsString = path(apiUrl, segments);
+		
+		try {
+	        String contentType = "application/json";
+	        if (httpMethod.equals(HTTP_METHOD_POST) && isEmpty(data) && !isEmpty(params)) {
+	            data = encodeParams(params);
+	            contentType = "application/x-www-form-urlencoded";
+	        } else {
+	            urlAsString = addQueryParams(urlAsString, params);
+	        }
+
+			//logTrace("Invoking " + httpMethod + " to '" + urlAsString + "'");
+
+			URL url = new URL(urlAsString);
+			conn = (HttpURLConnection) url.openConnection();
+            
+			conn.setRequestMethod(httpMethod);
+			conn.setRequestProperty("Content-Type", contentType);
+			conn.setUseCaches(false);
+			
+			if  ((accessToken != null) && (accessToken.length() > 0)) {
+				String authStr = "Bearer " + accessToken;
+				conn.setRequestProperty("Authorization", authStr);
+			}
+
+			conn.setDoInput(true);
+			
+	        if (httpMethod.equals(HTTP_METHOD_POST) || httpMethod.equals(HTTP_METHOD_PUT)) {
+	            if (isEmpty(data)) {
+	                data = JsonNodeFactory.instance.objectNode();
+	            }
+	            
+	            String dataAsString = null;
+	            
+	            if ((data != null) && (!(data instanceof String))) {
+	            	ObjectMapper objectMapper = new ObjectMapper();
+	    			objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+	    			dataAsString = objectMapper.writeValueAsString(data);
+	            } else {
+	            	dataAsString = (String) data;
+	            }
+	            
+	    		//logTrace("Posting/putting data: '" + dataAsString + "'");
+
+				byte[] dataAsBytes = dataAsString.getBytes();
+
+				conn.setRequestProperty("Content-Length", Integer.toString(dataAsBytes.length));
+				conn.setDoOutput(true);
+
+				out = conn.getOutputStream();
+				out.write(dataAsBytes);
+				out.flush();
+				out.close();
+				out = null;
+	        }
+	        
+			in = conn.getInputStream();
+			if( in != null ) {
+				BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+				StringBuilder sb = new StringBuilder();
+				String line;
+				
+				while( (line = reader.readLine()) != null ) {
+					sb.append(line);
+					sb.append('\n');
+				}
+				
+				String responseAsString = sb.toString();
+
+				//logTrace("response from server: '" + responseAsString + "'");
+                ObjectMapper objectMapper = new ObjectMapper();
+                objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+                objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+                response = (ApiResponse) objectMapper.readValue(responseAsString, ApiResponse.class);
+				response.setRawResponse(responseAsString);
+
+				response.setUGClient(this);
+			} else {
+				response = null;
+				logTrace("no response body from server");
+			}
+
+			//final int responseCode = conn.getResponseCode();
+			//logTrace("responseCode from server = " + responseCode);
+		}
+		catch(Exception e) {
+			logError("Error " + httpMethod + " to '" + urlAsString + "'" );
+			if( e != null ) {
+				e.printStackTrace();
+				logError(e.getLocalizedMessage());
+			}
+			response = null;
+		}
+		catch(Throwable t) {
+			logError("Error " + httpMethod + " to '" + urlAsString + "'" );
+			if( t != null ) {
+				t.printStackTrace();
+				logError(t.getLocalizedMessage());
+			}
+			response = null;
+		}
+		finally {
+			try {
+				if( out != null ) {
+					out.close();
+				}
+			
+				if( in != null ) {
+					in.close();
+				}
+				
+				if( conn != null ) {
+					conn.disconnect();
+				}
+			} catch(Exception ignored) {
+			}
+		}
+		
+	    return response;
+	}
+
+
+    /**
+     * High-level synchronous API request. Implements the http request
+     * for most SDK methods by calling 
+     * {@link #doHttpRequest(String,Map,Object,String...)}
+     * 
+     *  @param  httpMethod the HTTP method in the format: 
+     *      HTTP_METHOD_<method_name> (e.g. HTTP_METHOD_POST)
+     *  @param  params the URL parameters to append to the request URL
+     *  @param  data the body of the request
+     *  @param  segments  additional URL path segments to append to the request URL 
+     *  @return  ApiResponse object
+     */
+    public ApiResponse apiRequest(String httpMethod,
+            Map<String, Object> params, Object data, String... segments) {
+        ApiResponse response = null;
+        
+        response = doHttpRequest(httpMethod, params, data, segments);
+        
+        if( (response == null) ) {
+        	logError("doHttpRequest returned null");
+        }
+        
+        return response;
+    }
+
+    protected void assertValidApplicationId() {
+        if (isEmpty(applicationId)) {
+            throw new IllegalArgumentException("No application id specified");
+        }
+    }
+
+    /****************** ROLES/PERMISSIONS ***********************/
+    /****************** ROLES/PERMISSIONS ***********************/
+
+    /**
+     * Assigns permissions to the specified user, group, or role.
+     * 
+     * @param entityType the entity type of the entity the permissions are being assigned to. 'user', 'group' and 'role' are valid.
+     * @param entityID the UUID of 'name' property of the entity the permissions are being assigned to.
+     * @param permissions a comma-separated list of the permissions to be assigned in the format: <operations>:<path>, e.g. get, put, post, delete: /users
+     * @throws IllegalArgumentException thrown if an entityType other than 'group' or 'user' is passed to the method
+     * @return ApiResponse object
+     */
+    public ApiResponse assignPermissions(String entityType, String entityID, String permissions) {
+
+        if (!entityType.substring(entityType.length() - 1 ).equals("s")) {
+            entityType += "s";
+        }
+        
+        if (!validateTypeForPermissionsAndRoles(entityType, "permission")) {
+            throw new IllegalArgumentException("Permissions can only be assigned to group, user, or role entities");
+        }
+
+        Map<String, Object> data = new HashMap<String, Object>();
+        if (permissions != null){
+            data.put("permission", permissions);
+        }
+
+        return apiRequest(HTTP_METHOD_POST, null, data, organizationId,  applicationId, entityType,
+                entityID, "permissions");
+
+    }
+
+    /**
+     * Assigns permissions to the specified user, group, or role. Executes asynchronously in
+     * background and the callbacks are called in the UI thread.
+     * 
+     * @param entityType the entity type of the entity the permissions are being assigned to. 'user', 'group' and 'role' are valid.
+     * @param entityID the UUID of 'name' property of the entity the permissions are being assigned to.
+     * @param permissions a comma-separated list of the permissions to be assigned in the format: <operations>:<path>, e.g. get, put, post, delete: /users     
+     * @param  callback  an ApiResponseCallback to handle the async response
+     */
+    public void assignPermissionsAsync(final String entityType,
+            final String entityID, final String permissions, final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return assignPermissions(entityType, entityID, permissions);
+            }
+        }).execute();
+    }
+
+    /**
+     * Removes permissions from the specified user, group or role.
+     * 
+     * @param entityType the entity type of the entity the permissions are being removed from. 'user', 'group' and 'role' are valid.
+     * @param entityID the UUID of 'name' property of the entity the permissions are being removed from.
+     * @param permissions a comma-separated list of the permissions to be removed in the format: <operations>:<path>, e.g. get, put, post, delete: /users
+     * @throws IllegalArgumentException thrown if an entityType other than 'group' or 'user' is passed to the method
+     * @return ApiResponse object
+     */
+    public ApiResponse removePermissions(String entityType, String entityID, String permissions) {
+
+        if (!validateTypeForPermissionsAndRoles(entityType, "permission")) {
+            throw new IllegalArgumentException("Permissions can only be assigned to group, user, or role entities");
+        }
+
+        Map<String, Object> params = new HashMap<String, Object>();
+        if (permissions != null){
+            params.put("permission", permissions);
+        }
+        
+        return apiRequest(HTTP_METHOD_DELETE, params, null, organizationId,  applicationId, entityType,
+                entityID, "permissions");
+
+    }
+
+    /**
+     * Removes permissions from the specified user, group or role. Executes asynchronously in
+     * background and the callbacks are called in the UI thread.
+     * 
+     * @param entityType the entity type of the entity the permissions are being removed from. 'user', 'group', and 'role' are valid.
+     * @param entityID the UUID of 'name' property of the entity the permissions are being removed from.
+     * @param permissions a comma-separated list of the permissions to be removed in the format: <operations>:<path>, e.g. get, put, post, delete: /users     
+     * @param  callback  an ApiResponseCallback to handle the async response
+     */
+    public void removePermissionsAsync(final String entityType,
+            final String entityID, final String permissions, final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return removePermissions(entityType, entityID, permissions);
+            }
+        }).execute();
+    }
+
+    /**
+     * Creates a new role and assigns permissions to it.
+     * 
+     * @param roleName the name of the new role
+     * @param permissions a comma-separated list of the permissions to be assigned in the format: <operations>:<path>, e.g. get, put, post, delete: /users
+     * @return ApiResponse object
+     */
+    public ApiResponse createRole(String roleName, String permissions) {
+
+        Map<String, Object> properties = new HashMap<String, Object>();
+        properties.put("type", "role");
+        properties.put("name", roleName);
+
+        ApiResponse response = this.createEntity(properties);
+
+        String uuid = null;
+
+        if (response.getEntityCount() == 1){
+            uuid = response.getFirstEntity().getUuid().toString();
+        }
+
+        return assignPermissions("role", uuid, permissions);
+
+    }
+
+    /**
+     * Creates a new role and assigns permissions to it.
+     * 
+     * @param roleName the name of the new role
+     * @param permissions a comma-separated list of the permissions to be assigned in the format: <operations>:<path>, e.g. get, put, post, delete: /users
+     * @param  callback  an ApiResponseCallback to handle the async response     
+     */
+    public void createRoleAsync(final String roleName, final String permissions, 
+                  final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return createRole(roleName, permissions);
+            }
+        }).execute();
+    }
+
+    /**
+     * Assigns a role to a user or group entity.
+     * 
+     * @param roleName the name of the role to be assigned to the entity
+     * @param entityType the entity type of the entity the role is being assigned to. 'user' and 'group' are valid.
+     * @param entityID the UUID or 'name' property of the entity the role is being assigned to.     
+     * @throws IllegalArgumentException thrown if an entityType other than 'group' or 'user' is passed to the method
+     * @return ApiResponse object
+     */
+    public ApiResponse assignRole(String roleName, String entityType, String entityID) {
+
+        if (!entityType.substring(entityType.length() - 1 ).equals("s")) {
+            entityType += "s";
+        }
+
+        if (!validateTypeForPermissionsAndRoles(entityType, "role")) {
+            throw new IllegalArgumentException("Permissions can only be assigned to a group or user");
+        }
+
+        return apiRequest(HTTP_METHOD_POST, null, null, organizationId,  applicationId, "roles", roleName, 
+                      entityType, entityID);
+
+    }
+
+    /**
+     * Assigns a role to a user or group entity. Executes asynchronously in
+     * background and the callbacks are called in the UI thread.
+     * 
+     * @param roleName the name of the role to be assigned to the entity
+     * @param entityType the entity type of the entity the role is being assigned to. 'user' and 'group' are valid.
+     * @param entityID the UUID or 'name' property of the entity the role is being removed from.     
+     * @param callback  an ApiResponseCallback to handle the async response
+     */
+    public void assignRoleAsync(final String roleName, final String entityType,
+            final String entityID, final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return assignRole(roleName, entityType, entityID);
+            }
+        }).execute();
+    }
+
+    /**
+     * Removes a role from a user or group entity.
+     * 
+     * @param roleName the name of the role to be removed from the entity
+     * @param entityType the entity type of the entity the role is being removed from. 'user' and 'group' are valid.
+     * @param entityID the UUID or 'name' property of the entity the role is being removed from.     
+     * @throws IllegalArgumentException thrown if an entityType other than 'group' or 'user' is passed to the method
+     * @return ApiResponse object
+     */
+    public ApiResponse removeRole(String roleName, String entityType, String entityID) {
+
+        if (!entityType.substring(entityType.length() - 1 ).equals("s")) {
+            entityType += "s";
+        }
+
+        if (!validateTypeForPermissionsAndRoles(entityType, "role")) {
+            throw new IllegalArgumentException("Permissions can only be removed from a group or user");
+        }
+
+        return apiRequest(HTTP_METHOD_DELETE, null, null, organizationId,  applicationId, "roles", roleName, 
+                      entityType, entityID);
+
+    }
+
+    /**
+     * Removes a role from a user or group entity. Executes asynchronously in
+     * background and the callbacks are called in the UI thread.
+     * 
+     * @param roleName the name of the role to be removed from the entity
+     * @param entityType the entity type of the entity the role is being removed from. 'user' and 'group' are valid.
+     * @param entityID the UUID or 'name' property of the entity the role is being removed from.     
+     * @param callback  an ApiResponseCallback to handle the async response
+     */
+    public void removeRoleAsync(final String roleName, final String entityType,
+            final String entityID, final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return removeRole(roleName, entityType, entityID);
+            }
+        }).execute();
+    }
+
+    /**
+     * Checks if a permission or role can be assigned to an entity
+     * @y.exclude
+     */
+    private Boolean validateTypeForPermissionsAndRoles(String type, String permissionOrRole){
+        ArrayList<String> validTypes = new ArrayList<String>();        
+        validTypes.add("groups");        
+        validTypes.add("users");
+        
+        if (permissionOrRole.equals("permission")){
+            validTypes.add("roles");
+        }
+
+        return validTypes.contains(type);
+    }
+
+    /****************** LOG IN/LOG OUT/OAUTH ***********************/
+    /****************** LOG IN/LOG OUT/OAUTH ***********************/
+
+    /**
+     * Logs the user in and get a valid access token.
+     * 
+     * @param usernameOrEmail the username or email associated with the user entity in Usergrid
+     * @param password the user's Usergrid password
+     * @return non-null ApiResponse if request succeeds, check getError() for
+     *         "invalid_grant" to see if access is denied.
+     */
+    public ApiResponse authorizeAppUser(String usernameOrEmail, String password) {
+        validateNonEmptyParam(usernameOrEmail, "email");
+        validateNonEmptyParam(password,"password");
+        assertValidApplicationId();
+        loggedInUser = null;
+        accessToken = null;
+        currentOrganization = null;
+        Map<String, Object> formData = new HashMap<String, Object>();
+        formData.put("grant_type", "password");
+        formData.put("username", usernameOrEmail);
+        formData.put("password", password);
+        ApiResponse response = apiRequest(HTTP_METHOD_POST, formData, null,
+                organizationId, applicationId, "token");
+        if (response == null) {
+            return response;
+        }
+        if (!isEmpty(response.getAccessToken()) && (response.getUser() != null)) {
+            loggedInUser = response.getUser();
+            accessToken = response.getAccessToken();
+            currentOrganization = null;
+            logInfo("authorizeAppUser(): Access token: " + accessToken);
+        } else {
+            logInfo("authorizeAppUser(): Response: " + response);
+        }
+        return response;
+    }
+
+	/**
+	 * Log the user in and get a valid access token. Executes asynchronously in
+	 * background and the callbacks are called in the UI thread.
+	 * 
+	 * @param  usernameOrEmail  the username or email associated with the user entity in Usergrid
+     * @param  password  the users Usergrid password
+     * @param  callback  an ApiResponseCallback to handle the async response
+	 */
+	public void authorizeAppUserAsync(final String usernameOrEmail,
+			final String password, final ApiResponseCallback callback) {
+		(new ClientAsyncTask<ApiResponse>(callback) {
+			@Override
+			public ApiResponse doTask() {
+				return authorizeAppUser(usernameOrEmail, password);
+			}
+		}).execute();
+	}
+
+    /**
+     * Change the password for the currently logged in user. You must supply the
+     * old password and the new password.
+     * 
+     * @param username the username or email address associated with the user entity in Usergrid
+     * @param oldPassword the user's old password
+     * @param newPassword the user's new password
+     * @return ApiResponse object
+     */
+    public ApiResponse changePassword(String username, String oldPassword,
+            String newPassword) {
+
+        Map<String, Object> data = new HashMap<String, Object>();
+        data.put("newpassword", newPassword);
+        data.put("oldpassword", oldPassword);
+
+        return apiRequest(HTTP_METHOD_POST, null, data, organizationId,  applicationId, "users",
+                username, "password");
+
+    }
+
+    public void changePasswordAsync(final String username, final String oldPassword,
+            final String newPassword, final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return changePassword(username, oldPassword, newPassword);
+            }
+        }).execute();
+    }
+
+    /**
+     * Log the user in with their numeric pin-code and get a valid access token.
+     * 
+     * @param  email  the email address associated with the user entity in Usergrid
+     * @param  pin  the pin associated with the user entity in Usergrid
+     * @return  non-null ApiResponse if request succeeds, check getError() for
+     *         "invalid_grant" to see if access is denied.
+     */
+    public ApiResponse authorizeAppUserViaPin(String email, String pin) {
+        validateNonEmptyParam(email, "email");
+        validateNonEmptyParam(pin, "pin");
+        assertValidApplicationId();
+        loggedInUser = null;
+        accessToken = null;
+        currentOrganization = null;
+        Map<String, Object> formData = new HashMap<String, Object>();
+        formData.put("grant_type", "pin");
+        formData.put("username", email);
+        formData.put("pin", pin);
+        ApiResponse response = apiRequest(HTTP_METHOD_POST, formData, null,
+                organizationId,  applicationId, "token");
+        if (response == null) {
+            return response;
+        }
+        if (!isEmpty(response.getAccessToken()) && (response.getUser() != null)) {
+            loggedInUser = response.getUser();
+            accessToken = response.getAccessToken();
+            currentOrganization = null;
+            logInfo("authorizeAppUser(): Access token: " + accessToken);
+        } else {
+            logInfo("authorizeAppUser(): Response: " + response);
+        }
+        return response;
+    }
+
+	/**
+	 * Log the user in with their numeric pin-code and get a valid access token.
+	 * Executes asynchronously in background and the callbacks are called in the
+	 * UI thread.
+	 * 
+	 * @param  email  the email address associated with the user entity in Usergrid
+     * @param  pin  the pin associated with the user entity in Usergrid     
+     * @param callback A callback for the async response.
+	 */
+	public void authorizeAppUserViaPinAsync(final String email,
+			final String pin, final ApiResponseCallback callback) {
+		(new ClientAsyncTask<ApiResponse>(callback) {
+			@Override
+			public ApiResponse doTask() {
+				return authorizeAppUserViaPin(email, pin);
+			}
+		}).execute();
+	}
+
+    /**
+     * Log the app in with it's application (not organization) client id and 
+     * client secret key. Not recommended for production apps. Executes asynchronously 
+     * in background and the callbacks are called in the UI thread.
+     * 
+     * @param  clientId  the Usergrid application's client ID 
+     * @param  clientSecret  the Usergrid application's client secret
+     * @param  callback  an ApiResponseCallback to handle the async response
+     */
+    public void authorizeAppClientAsync(final String clientId,
+            final String clientSecret, final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+
+            @Override
+            public ApiResponse doTask() {
+                return authorizeAppClient(clientId, clientSecret);
+            }
+        }).execute();
+    }
+
+    private void validateNonEmptyParam(Object param, String paramName) {
+        if ( isEmpty(param) ) {
+            throw new IllegalArgumentException(paramName + " cannot be null or empty");
+        }
+    }
+
+    /**
+     * Log the user in with their Facebook access token retrieved via Facebook
+     * OAuth. Sets the user's identifier and Usergrid OAuth2 access token in UGClient 
+     * if successfully authorized.
+     * 
+     * @param fb_access_token the valid OAuth token returned by Facebook     
+     * @return non-null ApiResponse if request succeeds, check getError() for
+     *         "invalid_grant" to see if access is denied.
+     */
+    public ApiResponse authorizeAppUserViaFacebook(String fb_access_token) {
+        validateNonEmptyParam(fb_access_token, "Facebook token");
+        assertValidApplicationId();
+        loggedInUser = null;
+        accessToken = null;
+        currentOrganization = null;
+        Map<String, Object> formData = new HashMap<String, Object>();
+        formData.put("fb_access_token", fb_access_token);
+        ApiResponse response = apiRequest(HTTP_METHOD_POST, formData, null,
+                organizationId,  applicationId, "auth", "facebook");
+        if (response == null) {
+            return response;
+        }
+        if (!isEmpty(response.getAccessToken()) && (response.getUser() != null)) {
+            loggedInUser = response.getUser();
+            accessToken = response.getAccessToken();
+            currentOrganization = null;
+            logInfo("authorizeAppUserViaFacebook(): Access token: "
+                    + accessToken);
+        } else {
+            logInfo("authorizeAppUserViaFacebook(): Response: "
+                    + response);
+        }
+        return response;
+    }
+    
+	/**
+     * Log the user in with their Facebook access token retrieved via Facebook
+     * OAuth. Sets the user's identifier and Usergrid OAuth2 access token in UGClient 
+     * if successfully authorized. Executes asynchronously in background and the 
+     * callbacks are called in the UI thread.
+     * 
+     * @param  fb_access_token the valid OAuth token returned by Facebook 
+     * @param  callback  an ApiResponseCallback to handle the async response          
+     */
+	public void authorizeAppUserViaFacebookAsync(final String fb_access_token,
+			final ApiResponseCallback callback) {
+		(new ClientAsyncTask<ApiResponse>(callback) {
+			@Override
+			public ApiResponse doTask() {
+				return authorizeAppUserViaFacebook(fb_access_token);
+			}
+		}).execute();
+	}
+
+    /**
+     * Log out a user and destroy the access token currently stored in UGClient 
+     * on the server and in the UGClient.
+     * 
+     * @param  username  The username to be logged out
+     * @return  non-null ApiResponse if request succeeds
+     */
+    public ApiResponse logOutAppUser(String username) {
+        String token = getAccessToken();
+        Map<String,Object> params = new HashMap<String,Object>();
+        params.put("token",token);
+        ApiResponse response = apiRequest(HTTP_METHOD_PUT, params, null,
+                organizationId,  applicationId, "users",username,"revoketoken?");
+        if (response == null) {
+            return response;
+        } else {
+            logInfo("logoutAppUser(): Response: " + response);
+            setAccessToken(null);
+        }
+        return response;
+    }
+
+    /**
+     * Log out a user and destroy the access token currently stored in UGClient 
+     * on the server and in the UGClient.
+     * Executes asynchronously in background and the callbacks are called in the
+     * UI thread.
+     * 
+     * @param  username  The username to be logged out
+     * @param  callback  an ApiResponseCallback to handle the async response     
+     */
+    public void logOutAppUserAsync(final String username, final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return logOutAppUser(username);
+            }
+        }).execute();
+    }
+
+   /**
+     * Destroy a specific user token on the server. The token will also be cleared 
+     * from the UGClient instance, if it matches the token provided.
+     * 
+     * @param username The username to be logged out
+     * @param token The access token to be destroyed on the server
+     * @return  non-null ApiResponse if request succeeds
+     */
+    public ApiResponse logOutAppUserForToken(String username, String token) {                
+        Map<String,Object> params = new HashMap<String,Object>();
+        params.put("token",token);
+        ApiResponse response = apiRequest(HTTP_METHOD_PUT, params, null,
+                organizationId,  applicationId, "users",username,"revoketoken?");
+        if (response == null) {
+            return response;
+        } else {
+            logInfo("logoutAppWithTokenUser(): Response: " + response);
+            if (token.equals(getAccessToken())) {
+                setAccessToken(null);
+            }
+        }
+        return response;
+    }
+
+    /**
+     * Destroy a specific user token on the server. The token will also be cleared 
+     * from the UGClient instance, if it matches the token provided.
+     * Executes asynchronously in background and the callbacks are called in the UI thread.
+     * 
+     * @param  username  The username to be logged out
+     * @param  token  The access token to be destroyed on the server   
+     * @param callback A callback for the async response  
+     */
+    public void logOutAppUserForTokenAsync(final String username, final String token, final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return logOutAppUserForToken(username, token);
+            }
+        }).execute();
+    }
+
+    /**
+     * Log out a user and destroy all associated tokens on the server.
+     * The token stored in UGClient will also be destroyed.
+     * 
+     * @param  username The username to be logged out
+     * @return  non-null ApiResponse if request succeeds
+     */
+    public ApiResponse logOutAppUserForAllTokens(String username) {
+        ApiResponse response = apiRequest(HTTP_METHOD_PUT, null, null,
+                organizationId,  applicationId, "users",username,"revoketokens");
+        if (response == null) {
+            return response;
+        } else {
+            logInfo("logoutAppUserForAllTokens(): Response: " + response);
+            setAccessToken(null);
+        }
+        return response;
+    }
+
+    /**
+     * Log out a user and destroy all associated tokens on the server.
+     * The token stored in UGClient will also be destroyed.
+     * Executes asynchronously in background and the callbacks are called in the UI thread.
+     * 
+     * @param  username  The username to be logged out
+     * @param callback A callback for the response
+     */
+    public void logOutAppUserForAllTokensAsync(final String username, final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return logOutAppUserForAllTokens(username);
+            }
+        }).execute();
+    }
+
+    /**
+     * Log the app in with it's application (not organization) client id and 
+     * client secret key. Not recommended for production apps.
+     * 
+     * @param  clientId  the Usergrid application's client ID 
+     * @param  clientSecret  the Usergrid application's client secret     
+     */
+    public ApiResponse authorizeAppClient(String clientId, String clientSecret) {
+        validateNonEmptyParam(clientId, "client identifier");
+        validateNonEmptyParam(clientSecret, "client secret");
+        assertValidApplicationId();
+        loggedInUser = null;
+        accessToken = null;
+        currentOrganization = null;
+        Map<String, Object> formData = new HashMap<String, Object>();
+        formData.put("grant_type", "client_credentials");
+        formData.put("client_id", clientId);
+        formData.put("client_secret", clientSecret);
+        ApiResponse response = apiRequest(HTTP_METHOD_POST, formData, null,
+                organizationId, applicationId, "token");
+        if (response == null) {
+            return response;
+        }
+        if (!isEmpty(response.getAccessToken())) {
+            loggedInUser = null;
+            accessToken = response.getAccessToken();
+            currentOrganization = null;
+            logInfo("authorizeAppClient(): Access token: "
+                    + accessToken);
+        } else {
+            logInfo("authorizeAppClient(): Response: " + response);
+        }
+        return response;
+    }
+
+
+    /****************** GENERIC ENTITY MANAGEMENT ***********************/
+    /****************** GENERIC ENTITY MANAGEMENT ***********************/
+
+    /**
+     * Create a new entity on the server.
+     * 
+     * @param entity
+     * @return an ApiResponse with the new entity in it.
+     */
+    public ApiResponse createEntity(Entity entity) {
+        assertValidApplicationId();
+        if (isEmpty(entity.getType())) {
+            throw new IllegalArgumentException("Missing entity type");
+        }
+        ApiResponse response = apiRequest(HTTP_METHOD_POST, null, entity,
+                organizationId, applicationId, entity.getType());
+        return response;
+    }
+
+    /**
+     * Create a new entity on the server from a set of properties. Properties
+     * must include a "type" property.
+     * 
+     * @param properties
+     * @return an ApiResponse with the new entity in it.
+     */
+    public ApiResponse createEntity(Map<String, Object> properties) {
+        assertValidApplicationId();
+        if (isEmpty(properties.get("type"))) {
+            throw new IllegalArgumentException("Missing entity type");
+        }
+        ApiResponse response = apiRequest(HTTP_METHOD_POST, null, properties,
+                organizationId, applicationId, properties.get("type").toString());
+        return response;
+    }
+    
+    /**
+  	 * Create a new entity on the server. Executes asynchronously in background
+  	 * and the callbacks are called in the UI thread.
+  	 * 
+  	 * @param entity An instance with data to use to create the entity
+  	 * @param callback A callback for the async response
+  	 */
+  	public void createEntityAsync(final Entity entity,
+  			final ApiResponseCallback callback) {
+  		(new ClientAsyncTask<ApiResponse>(callback) {
+  			@Override
+  			public ApiResponse doTask() {
+  				return createEntity(entity);
+  			}
+  		}).execute();
+  	}
+
+  	
+  	/**
+  	 * Create a new entity on the server from a set of properties. Properties
+  	 * must include a "type" property. Executes asynchronously in background and
+  	 * the callbacks are called in the UI thread.
+  	 * 
+  	 * @param properties The values to use, with keys as property names and values 
+  	 * as property values
+  	 * @param callback A callback for the async response
+  	 */
+  	public void createEntityAsync(final Map<String, Object> properties,
+  			final ApiResponseCallback callback) {
+  		(new ClientAsyncTask<ApiResponse>(callback) {
+  			@Override
+  			public ApiResponse doTask() {
+  				return createEntity(properties);
+  			}
+  		}).execute();
+  	}
+  	
+  	/**
+  	 * Create a set of entities on the server from an ArrayList. Each item in the array
+  	 * contains a set of properties that define a entity.
+  	 * 
+  	 * @param type The type of entities to create.
+  	 * @param entities A list of maps where keys are entity property names and values
+  	 * are property values.
+  	 * @return An instance with response data from the server.
+  	 */
+  	public ApiResponse createEntities(String type, ArrayList<Map<String, Object>> entities) {
+        assertValidApplicationId();                
+        if (isEmpty(type)) {
+            throw new IllegalArgumentException("Missing entity type");
+        }
+        ApiResponse response = apiRequest(HTTP_METHOD_POST, null, entities,
+   		     organizationId, applicationId, type);	           		
+   		return response;	
+    }
+    
+    /**
+  	 * Create a set of entities on the server from an ArrayList. Each item in the array
+  	 * contains a set of properties that define a entity. Executes asynchronously in 
+  	 * background and the callbacks are called in the UI thread.
+  	 * 
+  	 * @param type The type of entities to create.
+  	 * @param entities A list of maps where keys are entity property names and values
+  	 * are property values.
+  	 * @param callback A callback for the async response
+  	 */
+    public void createEntitiesAsync(final String type, final ArrayList<Map<String, Object>> entities,
+  			final ApiResponseCallback callback) {
+  		(new ClientAsyncTask<ApiResponse>(callback) {
+  			@Override
+  			public ApiResponse doTask() {
+  				return createEntities(type, entities);
+  			}
+  		}).execute();
+  	}
+
+    /**
+     * Creates an object instance that corresponds to the provided entity type.
+     * Supported object types are Activity, Device, Group, Message, and User.
+     * All other types will return a generic Entity instance with no type assigned.
+     *
+     * @param  type  the entity type of which to create an object instance
+     * @return  an object instance that corresponds to the type provided
+    */
+    public Entity createTypedEntity(String type) {
+        Entity entity = null;
+        
+        if( Activity.isSameType(type) ) {
+            entity = new Activity(this);
+        } else if( Device.isSameType(type) ) {
+            entity = new Device(this);
+        } else if( Group.isSameType(type) ) {
+            entity = new Group(this);
+        } else if( Message.isSameType(type) ) {
+            entity = new Message(this);
+        } else if( User.isSameType(type) ) {
+            entity = new User(this);
+        } else {
+            entity = new Entity(this);
+        }
+        
+        return entity;
+    }
+
+    /**
+     * Requests all entities of specified type that match the provided query string.
+     *
+     * @param  type  the entity type to be retrieved
+     * @param  queryString  a query string to send with the request
+     * @return  a non-null ApiResponse object if successful
+    */
+    public ApiResponse getEntities(String type,String queryString)
+    {
+        Map<String, Object> params = null;
+
+        if (queryString.length() > 0) {
+            params = new HashMap<String, Object>();
+            params.put("ql", queryString);
+        }
+        
+        return apiRequest(HTTP_METHOD_GET, // method
+                            params, // params
+                            null, // data
+                            organizationId,
+                            applicationId,
+                            type);
+    }
+    
+    /**
+     * Asynchronously requests all entities of specified type that match the provided query string.
+     *
+     * @param  type  the entity type to be retrieved
+     * @param  queryString  a query string to send with the request
+     * @param  callback an ApiResponseCallback to handle the async response
+    */
+    public void getEntitiesAsync(final String type,
+            final String queryString, final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return getEntities(type, queryString);
+            }
+        }).execute();
+    }
+
+    /**
+     * Update an existing entity on the server.
+     * 
+     * @param entityID the entity to update
+     * @param updatedProperties the new properties
+     * @return an ApiResponse with the updated entity in it.
+     */
+    public ApiResponse updateEntity(String entityID, Map<String, Object> updatedProperties) {
+    	assertValidApplicationId();
+    	if (isEmpty(updatedProperties.get("type"))) {
+            throw new IllegalArgumentException("Missing entity type");
+    	}
+    	ApiResponse response = apiRequest(HTTP_METHOD_PUT, null, updatedProperties,
+    			organizationId, applicationId, updatedProperties.get("type").toString(), entityID);
+    	return response;
+    }
+
+    
+    /**
+     * Update an existing entity on the server. Properties
+     * must include a "type" property. Executes asynchronously in background and
+     * the callbacks are called in the UI thread.
+     *
+     * @param entityID the entity to update
+     * @param updatedProperties the new properties
+     * @param callback A callback for the async response
+     */
+    public void updateEntityAsync(final String entityID, final Map<String, Object> updatedProperties,
+                                      final ApiResponseCallback callback) {
+          (new ClientAsyncTask<ApiResponse>(callback) {
+        	  @Override
+        	  public ApiResponse doTask() {
+        		  return updateEntity(entityID, updatedProperties);
+        	  }
+          }).execute();
+    }
+
+    /**
+     * Updates the password associated with a user entity
+     *
+     * @param  usernameOrEmail  the username or email address associated with the user entity
+     * @param  oldPassword  the user's old password
+     * @param  newPassword  the user's new password
+     * @return an ApiResponse with the updated entity in it.
+     */
+    public ApiResponse updateUserPassword(String usernameOrEmail, String oldPassword, String newPassword) {
+    	Map<String,Object> updatedProperties = new HashMap<String,Object>();
+    	updatedProperties.put("oldpassword", oldPassword);
+    	updatedProperties.put("newpassword", newPassword);
+    	return apiRequest(HTTP_METHOD_POST, null, updatedProperties,
+    			organizationId, applicationId, "users", usernameOrEmail);
+    }
+
+    
+    /**
+     * Remove an existing entity on the server.
+     * 
+     * @param entityType the collection of the entity
+     * @param entityID the specific entity to delete
+     * @return an ApiResponse indicating whether the removal was successful
+     */
+    public ApiResponse removeEntity(String entityType, String entityID) {
+    	assertValidApplicationId();
+    	if (isEmpty(entityType)) {
+            throw new IllegalArgumentException("Missing entity type");
+    	}
+    	ApiResponse response = apiRequest(HTTP_METHOD_DELETE, null, null,
+    			organizationId, applicationId, entityType, entityID);
+    	return response;
+    }
+    
+    /**
+     * Remove an existing entity on the server.
+     * Executes asynchronously in background and
+     * the callbacks are called in the UI thread.
+     *
+     * @param entityType the collection of the entity
+     * @param entityID the specific entity to delete
+     * @param callback A callback with the async response
+     */
+    public void removeEntityAsync(final String entityType, final String entityID,
+    								final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+            	return removeEntity(entityType, entityID);
+    		}
+    	}).execute();
+    }
+    
+    /**
+     * Perform a query request and return a query object. The Query object
+     * provides a simple way of dealing with result sets that need to be
+     * iterated or paged through.
+     * 
+     * See {@link #doHttpRequest(String,Map,Object,String...)} for
+     * more on the parameters.
+     * 
+     * @param httpMethod The HTTP method to use in the query
+     * @param params Query parameters.
+     * @param data The request body.
+     * @param segments Additional URL path segments to append to the request URL
+     * @return An instance representing query results
+     */
+    public Query queryEntitiesRequest(String httpMethod,
+            Map<String, Object> params, Object data, String... segments) {
+        ApiResponse response = apiRequest(httpMethod, params, data, segments);
+        return new EntityQuery(response, httpMethod, params, data, segments);
+    }
+
+    /**
+     * Perform a query request and return a query object. The Query object
+     * provides a simple way of dealing with result sets that need to be
+     * iterated or paged through. Executes asynchronously in background and the
+     * callbacks are called in the UI thread.
+     * 
+     * See {@link #doHttpRequest(String,Map,Object,String...)} for
+     * more on the parameters.
+     * 
+     * @param callback A callback for the async response
+     * @param httpMethod The HTTP method to use in the query
+     * @param params Query parameters.
+     * @param data The request body.
+     * @param segments Additional URL path segments to append to the request URL
+     */
+    public void queryEntitiesRequestAsync(final QueryResultsCallback callback,
+            final String httpMethod, final Map<String, Object> params,
+            final Object data, final String... segments) {
+        (new ClientAsyncTask<Query>(callback) {
+            @Override
+            public Query doTask() {
+                return queryEntitiesRequest(httpMethod, params, data, segments);
+            }
+        }).execute();
+    }
+
+    /**
+     * Query object for handling the response from certain query requests
+     * @y.exclude
+     */
+    private class EntityQuery implements Query {
+        final String httpMethod;
+        final Map<String, Object> params;
+        final Object data;
+        final String[] segments;
+        final ApiResponse response;
+
+        private EntityQuery(ApiResponse response, String httpMethod,
+                Map<String, Object> params, Object data, String[] segments) {
+            this.response = response;
+            this.httpMethod = httpMethod;
+            this.params = params;
+            this.data = data;
+            this.segments = segments;
+        }
+
+        private EntityQuery(ApiResponse response, EntityQuery q) {
+            this.response = response;
+            httpMethod = q.httpMethod;
+            params = q.params;
+            data = q.data;
+            segments = q.segments;
+        }
+
+        /**
+         * Gets the API response from the last request
+         *
+         * @return an ApiResponse object
+         */
+        public ApiResponse getResponse() {
+            return response;
+        }
+
+        /**
+         * Checks if there are more results available based on whether a 
+         * 'cursor' property was present in the last result set.
+         *
+         * @return true if the server indicates more results are available
+         */
+        public boolean more() {
+            if ((response != null) && (response.getCursor() != null)
+                    && (response.getCursor().length() > 0)) {
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Performs a request for the next set of results based on the cursor
+         * from the last result set.
+         * 
+         * @return query that contains results and where to get more.
+         */
+        public Query next() {
+            if (more()) {
+                Map<String, Object> nextParams = null;
+                if (params != null) {
+                    nextParams = new HashMap<String, Object>(params);
+                } else {
+                    nextParams = new HashMap<String, Object>();
+                }
+                nextParams.put("cursor", response.getCursor());
+                ApiResponse nextResponse = apiRequest(httpMethod, nextParams, data,
+                        segments);
+                return new EntityQuery(nextResponse, this);
+            }
+            return null;
+        }
+
+    }
+
+
+    /****************** USER ENTITY MANAGEMENT ***********************/
+    /****************** USER ENTITY MANAGEMENT ***********************/
+
+    /**
+     * Creates a user entity.
+     * 
+     * @param  username  required. The username to be associated with the user entity.
+     * @param  name  the user's full name. Can be null.
+     * @param  email  the user's email address.
+     * @param  password  the user's password
+     * @return  an ApiResponse object
+     */
+    public ApiResponse createUser(String username, String name, String email,
+            String password) {
+        Map<String, Object> properties = new HashMap<String, Object>();
+        properties.put("type", "user");
+        if (username != null) {
+            properties.put("username", username);
+        }
+        if (name != null) {
+            properties.put("name", name);
+        }
+        if (email != null) {
+            properties.put("email", email);
+        }
+        if (password != null) {
+            properties.put("password", password);
+        }
+        return createEntity(properties);
+    }
+
+	/**
+	 * Creates a user. Executes asynchronously in background and the callbacks
+	 * are called in the UI thread.
+	 * 
+	 * @param  username required. The username to be associated with the user entity.
+     * @param  name  the user's full name. Can be null.
+     * @param  email  the user's email address.
+     * @param  password  the user's password.
+	 * @param  callback  an ApiResponse callback for handling the async response.
+	 */
+	public void createUserAsync(final String username, final String name,
+			final String email, final String password,
+			final ApiResponseCallback callback) {
+		(new ClientAsyncTask<ApiResponse>(callback) {
+			@Override
+			public ApiResponse doTask() {
+				return createUser(username, name, email, password);
+			}
+		}).execute();
+	}
+
+    /**
+     * Retrieves the /users collection.
+     * 
+     * @return a Query object
+     */
+    public Query queryUsers() {
+        Query q = queryEntitiesRequest(HTTP_METHOD_GET, null, null,
+                organizationId,  applicationId, "users");
+        return q;
+    }
+    
+    /**
+     * Retrieves the /users collection. Executes asynchronously in
+     * background and the callbacks are called in the UI thread.
+     * 
+     * @param  callback  a QueryResultsCallback object to handle the async response
+     */
+    public void queryUsersAsync(QueryResultsCallback callback) {
+        queryEntitiesRequestAsync(callback, HTTP_METHOD_GET, null, null,
+                organizationId, applicationId, "users");
+    }
+
+
+    /**
+     * Performs a query of the users collection using the provided query command.
+     * For example: "name contains 'ed'".
+     * 
+     * @param  ql  the query to execute
+     * @return  a Query object
+     */
+    public Query queryUsers(String ql) {
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("ql", ql);
+        Query q = queryEntitiesRequest(HTTP_METHOD_GET, params, null,organizationId,
+                applicationId, "users");
+        return q;
+    }
+
+    /**
+     * Perform a query of the users collection using the provided query command.
+     * For example: "name contains 'ed'". Executes asynchronously in background
+     * and the callbacks are called in the UI thread.
+     * 
+     * @param  ql  the query to execute
+     * @param  callback  a QueryResultsCallback object to handle the async response
+     */
+    public void queryUsersAsync(String ql, QueryResultsCallback callback) {
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("ql", ql);
+        queryEntitiesRequestAsync(callback, HTTP_METHOD_GET, params, null, 
+                organizationId, applicationId, "users");
+    }
+    
+    /**
+     * Perform a query of the users collection within the specified distance of
+     * the specified location and optionally using the provided additional query.
+     * For example: "name contains 'ed'".
+     * 
+     * @param  distance  distance from the location in meters
+     * @param  latitude  the latitude of the location to measure from
+     * @param  longitude  the longitude of the location to measure from
+     * @param  ql  an optional additional query to send with the request
+     * @return  a Query object
+     */
+    public Query queryUsersWithinLocation(float distance, float latitude,
+            float longitude, String ql) {
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("ql",
+                this.makeLocationQL(distance, latitude, longitude, ql));
+        Query q = queryEntitiesRequest(HTTP_METHOD_GET, params, null, organizationId,
+                applicationId, "users");
+        return q;
+    }
+
+    
+    /****************** GROUP ENTITY MANAGEMENT ***********************/
+    /****************** GROUP ENTITY MANAGEMENT ***********************/
+
+    /**
+     * Creates a group with the specified group path. Group paths can be slash
+     * ("/") delimited like file paths for hierarchical group relationships.
+     * 
+     * @param  groupPath  the path to use for the new group.
+     * @return  an ApiResponse object
+     */
+    public ApiResponse createGroup(String groupPath) {
+        return createGroup(groupPath, null);
+    }
+
+    /**
+     * Creates a group with the specified group path and group title. Group
+     * paths can be slash ("/") delimited like file paths for hierarchical group
+     * relationships.
+     * 
+     * @param  groupPath  the path to use for the new group
+     * @param  groupTitle  the title to use for the new group
+     * @return  an ApiResponse object
+     */
+    public ApiResponse createGroup(String groupPath, String groupTitle) {
+     return createGroup(groupPath, groupTitle, null);  
+    }
+    
+    /**
+     * Create a group with a path, title and name. Group
+     * paths can be slash ("/") delimited like file paths for hierarchical group
+     * relationships.
+     *
+     * @param  groupPath  the path to use for the new group
+     * @param  groupTitle  the title to use for the new group
+     * @param  groupName  the name to use for the new group
+     * @return  an ApiResponse object
+     */
+    public ApiResponse createGroup(String groupPath, String groupTitle, String groupName){
+        Map<String, Object> data = new HashMap<String, Object>();
+        data.put("type", "group");
+        data.put("path", groupPath);
+        
+        if (groupTitle != null) {
+            data.put("title", groupTitle);
+        }
+        
+        if(groupName != null){
+            data.put("name", groupName);
+        }
+        
+        return apiRequest(HTTP_METHOD_POST, null, data,  organizationId, applicationId, "groups");
+    }
+
+    /**
+     * Creates a group with the specified group path. Group paths can be slash
+     * ("/") delimited like file paths for hierarchical group relationships.
+     * Executes asynchronously in background and the callbacks are called in the
+     * UI thread.
+     * 
+     * @param  groupPath  the path to use for the new group.
+     * @param  callback  an ApiResponseCallback object to handle the async response
+     */
+    public void createGroupAsync(String groupPath,
+            final ApiResponseCallback callback) {
+        createGroupAsync(groupPath, null, null);
+    }
+
+    /**
+     * Creates a group with the specified group path and group title. Group
+     * paths can be slash ("/") deliminted like file paths for hierarchical
+     * group relationships. Executes asynchronously in background and the
+     * callbacks are called in the UI thread.
+     * 
+     * @param  groupPath  the path to use for the new group.
+     * @param  groupTitle  the title to use for the new group.
+     * @param  callback  an ApiResponseCallback object to handle the async response
+     */
+    public void createGroupAsync(final String groupPath,
+            final String groupTitle, final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return createGroup(groupPath, groupTitle);
+            }
+        }).execute();
+    }
+
+    /**
+     * Gets the groups associated with a user entity
+     * 
+     * @param  userId  the UUID of the user entity
+     * @return  a map with the group path as the key and a Group object as the value
+     */
+    public Map<String, Group> getGroupsForUser(String userId) {
+        ApiResponse response = apiRequest(HTTP_METHOD_GET, null, null,
+                organizationId, applicationId, "users", userId, "groups");
+        Map<String, Group> groupMap = new HashMap<String, Group>();
+        if (response != null) {
+            List<Group> groups = response.getEntities(Group.class);
+            for (Group group : groups) {
+                groupMap.put(group.getPath(), group);
+            }
+        }
+        return groupMap;
+    }
+
+	/**
+	 * Gets the groups associated with a user entity. Executes asynchronously in background and
+	 * the callbacks are called in the UI thread.
+	 * 
+     * @param  userId  the UUID of the user entity
+	 * @param  callback  a GroupsRetrievedCallback object to handle the async response
+	 */
+	public void getGroupsForUserAsync(final String userId,
+			final GroupsRetrievedCallback callback) {
+		(new ClientAsyncTask<Map<String, Group>>(callback) {
+			@Override
+			public Map<String, Group> doTask() {
+				return getGroupsForUser(userId);
+			}
+		}).execute();
+	}
+
+    /**
+     * Gets the user entities associated with the specified group.
+     * 
+     * @param  groupId  UUID of the group entity
+     * @return  a Query object with the results of the query
+     */
+    public Query queryUsersForGroup(String groupId) {
+        Query q = queryEntitiesRequest(HTTP_METHOD_GET, null, null, organizationId,
+                applicationId, "groups", groupId, "users");
+        return q;
+    }
+
+    /**
+     * Gets the user entities associated with the specified group. Executes 
+     * asynchronously in background and the callbacks are called in the UI thread.
+     * 
+     * @param  groupId  UUID of the group entity
+     * @param  callback a QueryResultsCallback object to handle the async response
+     */
+    public void queryUsersForGroupAsync(String groupId,
+            QueryResultsCallback callback) {
+        queryEntitiesRequestAsync(callback, HTTP_METHOD_GET, null, null,
+                getApplicationId(), "groups", groupId, "users");
+    }
+
+    /**
+     * Connects a user entity to the specified group entity.
+     * 
+     * @param  userId  UUID of the user entity
+     * @param  groupId  UUID of the group entity 
+     * @return  an ApiResponse object
+     */
+    public ApiResponse addUserToGroup(String userId, String groupId) {
+        return apiRequest(HTTP_METHOD_POST, null, null, organizationId,  applicationId, "groups",
+                groupId, "users", userId);
+    }
+
+    /**
+     * Connects a user entity to the specified group entity. Executes asynchronously in
+     * background and the callbacks are called in the UI thread.
+     * 
+     * @param  userId  UUID of the user entity
+     * @param  groupId  UUID of the group entity 
+     * @param  callback  an ApiResponseCallback object to handle the async response
+     */
+    public void addUserToGroupAsync(final String userId, final String groupId,
+            final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return addUserToGroup(userId, groupId);
+            }
+        }).execute();
+    }
+
+    /**
+     * Disconnects a user entity from the specified group entity.
+     * 
+     * @param  userId  UUID of the user entity
+     * @param  groupId  UUID of the group entity 
+     * @return  an ApiResponse object
+     */
+    public ApiResponse removeUserFromGroup(String userId, String groupId) {
+        return apiRequest(HTTP_METHOD_DELETE, null, null, organizationId,  applicationId, "groups",
+                groupId, "users", userId);
+    }
+
+    /**
+     * Disconnects a user entity from the specified group entity. Executes asynchronously in
+     * background and the callbacks are called in the UI thread.
+     * 
+     * @param  userId  UUID of the user entity
+     * @param  groupId  UUID of the group entity 
+     * @param  callback  an ApiResponseCallback object to handle the async response
+     */
+    public void removeUserFromGroupAsync(final String userId, final String groupId,
+            final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return removeUserFromGroup(userId, groupId);
+            }
+        }).execute();
+    }
+
+    /****************** ACTIVITY ENTITY MANAGEMENT ***********************/
+    /****************** ACTIVITY ENTITY MANAGEMENT ***********************/
+
+    /**
+     * Get a user's activity feed. Returned as a query to ease paging.
+     * 
+     * @param  userId  UUID of user entity
+     * @return  a Query object
+     */
+    public Query queryActivityFeedForUser(String userId) {
+        Query q = queryEntitiesRequest(HTTP_METHOD_GET, null, null,
+                organizationId, applicationId, "users", userId, "feed");
+        return q;
+    }
+    
+	/**
+	 * Get a user's activity feed. Returned as a query to ease paging. Executes
+	 * asynchronously in background and the callbacks are called in the UI
+	 * thread.
+	 * 
+	 * 
+	 * @param  userId  UUID of user entity
+	 * @param  callback  a QueryResultsCallback object to handle the async response
+	 */
+	public void queryActivityFeedForUserAsync(final String userId, final QueryResultsCallback callback) {
+		(new ClientAsyncTask<Query>(callback) {
+			@Override
+			public Query doTask() {
+				return queryActivityFeedForUser(userId);
+			}
+		}).execute();
+	}
+
+
+    /**
+     * Posts an activity to a user entity's activity stream. Activity must already be created.
+     * 
+     * @param userId 
+     * @param activity 
+     * @return An instance with the server response
+     */
+    public ApiResponse postUserActivity(String userId, Activity activity) {
+        return apiRequest(HTTP_METHOD_POST, null, activity,  organizationId, applicationId, "users",
+                userId, "activities");
+    }
+
+    /**
+     * Creates and posts an activity to a user entity's activity stream.
+     * 
+     * @param verb
+     * @param title
+     * @param content
+     * @param category
+     * @param user
+     * @param object
+     * @param objectType
+     * @param objectName
+     * @param objectContent
+     * @return
+     */
+    public ApiResponse postUserActivity(String verb, String title,
+            String content, String category, User user, Entity object,
+            String objectType, String objectName, String objectContent) {
+        Activity activity = Activity.newActivity(this, verb, title, content,
+                category, user, object, objectType, objectName, objectContent);
+        return postUserActivity(user.getUuid().toString(), activity);
+    }
+
+	/**
+	 * Creates and posts an activity to a user. Executes asynchronously in
+	 * background and the callbacks are called in the UI thread.
+	 * 
+	 * @param verb
+	 * @param title
+	 * @param content
+	 * @param category
+	 * @param user
+	 * @param object
+	 * @param objectType
+	 * @param objectName
+	 * @param objectContent
+	 * @param callback
+	 */
+	public void postUserActivityAsync(final String verb, final String title,
+			final String content, final String category, final User user,
+			final Entity object, final String objectType,
+			final String objectName, final String objectContent,
+			final ApiResponseCallback callback) {
+		(new ClientAsyncTask<ApiResponse>(callback) {
+			@Override
+			public ApiResponse doTask() {
+				return postUserActivity(verb, title, content, category, user,
+						object, objectType, objectName, objectContent);
+			}
+		}).execute();
+	}
+
+    /**
+     * Posts an activity to a group. Activity must already be created.
+     * 
+     * @param groupId
+     * @param activity
+     * @return
+     */
+    public ApiResponse postGroupActivity(String groupId, Activity activity) {
+        return apiRequest(HTTP_METHOD_POST, null, activity, organizationId, applicationId, "groups",
+                groupId, "activities");
+    }
+
+    /**
+     * Creates and posts an activity to a group.
+     * 
+     * @param groupId
+     * @param verb
+     * @param title
+     * @param content
+     * @param category
+     * @param user
+     * @param object
+     * @param objectType
+     * @param objectName
+     * @param objectContent
+     * @return
+     */
+    public ApiResponse postGroupActivity(String groupId, String verb, String title,
+            String content, String category, User user, Entity object,
+            String objectType, String objectName, String objectContent) {
+        Activity activity = Activity.newActivity(this, verb, title, content,
+                category, user, object, objectType, objectName, objectContent);
+        return postGroupActivity(groupId, activity);
+    }
+
+	/**
+	 * Creates and posts an activity to a group. Executes asynchronously in
+	 * background and the callbacks are called in the UI thread.
+	 * 
+	 * @param groupId
+	 * @param verb
+	 * @param title
+	 * @param content
+	 * @param category
+	 * @param user
+	 * @param object
+	 * @param objectType
+	 * @param objectName
+	 * @param objectContent
+	 * @param callback
+	 */
+	public void postGroupActivityAsync(final String groupId, final String verb, final String title,
+			final String content, final String category, final User user,
+			final Entity object, final String objectType,
+			final String objectName, final String objectContent,
+			final ApiResponseCallback callback) {
+		(new ClientAsyncTask<ApiResponse>(callback) {
+			@Override
+			public ApiResponse doTask() {
+				return postGroupActivity(groupId, verb, title, content, category, user,
+						object, objectType, objectName, objectContent);
+			}
+		}).execute();
+	}
+
+    /**
+     * Post an activity to the stream.
+     * 
+     * @param activity
+     * @return
+     */
+    public ApiResponse postActivity(Activity activity) {
+        return createEntity(activity);
+    }
+
+    /**
+     * Creates and posts an activity to a group.
+     * 
+     * @param verb
+     * @param title
+     * @param content
+     * @param category
+     * @param user
+     * @param object
+     * @param objectType
+     * @param objectName
+     * @param objectContent
+     * @return
+     */
+    public ApiResponse postActivity(String verb, String title,
+            String content, String category, User user, Entity object,
+            String objectType, String objectName, String objectContent) {
+        Activity activity = Activity.newActivity(this, verb, title, content,
+                category, user, object, objectType, objectName, objectContent);
+        return createEntity(activity);
+    }
+    
+    /**
+     * Get a group's activity feed. Returned as a query to ease paging.
+     *      
+     * @return
+     */
+    public Query queryActivity() {
+        Query q = queryEntitiesRequest(HTTP_METHOD_GET, null, null,
+               organizationId, applicationId, "activities");
+        return q;
+    }
+
+    
+
+    /**
+     * Get a group's activity feed. Returned as a query to ease paging.
+     * 
+     * @param groupId
+     * @return
+     */
+    public Query queryActivityFeedForGroup(String groupId) {
+        Query q = queryEntitiesRequest(HTTP_METHOD_GET, null, null,
+                organizationId,  applicationId, "groups", groupId, "feed");
+        return q;
+    }
+
+    /**
+     * Get a group's activity feed. Returned as a query to ease paging. Executes
+     * asynchronously in background and the callbacks are called in the UI
+     * thread.
+     * 
+     * 
+     * @param groupId
+     * @param callback
+     */
+    public void queryActivityFeedForGroupAsync(final String groupId,
+            final QueryResultsCallback callback) {
+        (new ClientAsyncTask<Query>(callback) {
+            @Override
+            public Query doTask() {
+                return queryActivityFeedForGroup(groupId);
+            }
+        }).execute();
+    }
+    
+
+    /****************** ENTITY CONNECTIONS ***********************/
+    /****************** ENTITY CONNECTIONS ***********************/
+
+    /**
+     * Connect two entities together.
+     * 
+     * @param connectingEntityType The type of the first entity.
+     * @param connectingEntityId The ID of the first entity.
+     * @param connectionType The type of connection between the entities.
+     * @param connectedEntityId The ID of the second entity.
+     * @return An instance with the server's response.
+     */
+    public ApiResponse connectEntities(String connectingEntityType,
+            String connectingEntityId, String connectionType,
+            String connectedEntityId) {
+        return apiRequest(HTTP_METHOD_POST, null, null,  organizationId, applicationId,
+                connectingEntityType, connectingEntityId, connectionType,
+                connectedEntityId);
+    }
+    
+    /**
+     * Connect two entities together
+     * 
+     * @param connectorType The type of the first entity in the connection.
+     * @param connectorID The first entity's ID.
+     * @param connectionType The type of connection to make.
+     * @param connecteeType The type of the second entity.
+     * @param connecteeID The second entity's ID
+     * @return An instance with the server's response.
+     */
+    public ApiResponse connectEntities(String connectorType,
+    		String connectorID,
+    		String connectionType,
+    		String connecteeType,
+    		String connecteeID) {
+		return apiRequest(HTTP_METHOD_POST, null, null, organizationId, applicationId,
+				connectorType, connectorID, connectionType, connecteeType, connecteeID);
+    }
+
+
+	/**
+	 * Connect two entities together. Executes asynchronously in background and
+	 * the callbacks are called in the UI thread.
+	 * 
+     * @param connectingEntityType The type of the first entity.
+     * @param connectingEntityId The UUID or 'name' property of the first entity.
+     * @param connectionType The type of connection between the entities.
+     * @param connectedEntityId The UUID of the second entity.
+	 * @param callback A callback with the async response.
+	 */
+	public void connectEntitiesAsync(final String connectingEntityType,
+			final String connectingEntityId, final String connectionType,
+			final String connectedEntityId, final ApiResponseCallback callback) {
+		(new ClientAsyncTask<ApiResponse>(callback) {
+			@Override
+			public ApiResponse doTask() {
+				return connectEntities(connectingEntityType,
+						connectingEntityId, connectionType, connectedEntityId);
+			}
+		}).execute();
+	}
+
+    /**
+     * Connect two entities together. Allows the 'name' of the connected entity
+     * to be specified but requires the type also be specified. Executes asynchronously 
+     * in background and the callbacks are called in the UI thread.
+     * 
+     * @param connectingEntityType The type of the first entity.
+     * @param connectingEntityId The UUID or 'name' property of the first entity.
+     * @param connectionType The type of connection between the entities.
+     * @param connectedEntityType The type of connection between the entities.
+     * @param connectedEntityId The UUID or 'name' property of the second entity.
+     * @param callback A callback with the async response.
+     */
+    public void connectEntitiesAsync(final String connectingEntityType,
+            final String connectingEntityId, final String connectionType,
+            final String connectedEntityType, final String connectedEntityId, 
+            final ApiResponseCallback callback) {
+        (new ClientAsyncTask<ApiResponse>(callback) {
+            @Override
+            public ApiResponse doTask() {
+                return connectEntities(connectingEntityType,
+                        connectingEntityId, connectionType, connectedEntityType, connectedEntityId);
+            }
+        }).execute();
+    }
+	
+    /**
+     * Disconnect two entities.
+     * 
+     * @param connectingEntityType The collection name or UUID of the first entity.
+     * @param connectingEntityId The name or UUID of the first entity.
+     * @param connectionType The type of connection between the entities.     
+     * @param connectedEntityId The name or UUID of the second entity.
+     * @return An instance with the server's response.
+     */
+    public ApiResponse disconnectEntities(String connectingEntityType,
+            String connectingEntityId, String connectionType,
+            String connectedEntity

<TRUNCATED>