New version of SampleSyncAdapter sample code that allows local editing

The changes made were
pretty sweeping. The biggest addition was to allow on-device contact
creation/editing, and supporting 2-way sync to the sample server that
runs in Google App Engine.

The client-side sample code also includes examples of how to support
the user of AuthTokens (instead of always sending username/password
to the server), how to change a contact's picture, and how to set
IM-style status messages for each contact.

I also greatly simplified the server code so that instead of mimicking
both an addressbook and an IM-style status update system for multiple
users, it really just simulates an addressbook for a single user. The
server code also includes a cron job that (once a week) blows away the
contact database, so that it's relatively self-cleaning.

Change-Id: I017f1d3f9320a02fe05a20f1613846963107145e
This commit is contained in:
John Evans
2011-04-04 13:38:01 -07:00
parent 3f2b06f3c5
commit 15ef1a8091
45 changed files with 3159 additions and 1237 deletions

View File

@@ -1,23 +1,27 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.example.android.samplesync.client;
import android.accounts.Account;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import com.example.android.samplesync.authenticator.AuthenticatorActivity;
@@ -37,11 +41,19 @@ import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
@@ -52,31 +64,26 @@ import java.util.TimeZone;
* Provides utility methods for communicating with the server.
*/
final public class NetworkUtilities {
/** The tag used to log to adb console. **/
/** The tag used to log to adb console. */
private static final String TAG = "NetworkUtilities";
/** The Intent extra to store password. **/
public static final String PARAM_PASSWORD = "password";
/** The Intent extra to store username. **/
/** POST parameter name for the user's account name */
public static final String PARAM_USERNAME = "username";
public static final String PARAM_UPDATED = "timestamp";
public static final String USER_AGENT = "AuthenticationService/1.0";
public static final int REGISTRATION_TIMEOUT_MS = 30 * 1000; // ms
public static final String BASE_URL = "https://samplesyncadapter.appspot.com";
/** POST parameter name for the user's password */
public static final String PARAM_PASSWORD = "password";
/** POST parameter name for the user's authentication token */
public static final String PARAM_AUTH_TOKEN = "authtoken";
/** POST parameter name for the client's last-known sync state */
public static final String PARAM_SYNC_STATE = "syncstate";
/** POST parameter name for the sending client-edited contact info */
public static final String PARAM_CONTACTS_DATA = "contacts";
/** Timeout (in ms) we specify for each http request */
public static final int HTTP_REQUEST_TIMEOUT_MS = 30 * 1000;
/** Base URL for the v2 Sample Sync Service */
public static final String BASE_URL = "https://samplesyncadapter2.appspot.com";
/** URI for authentication service */
public static final String AUTH_URI = BASE_URL + "/auth";
public static final String FETCH_FRIEND_UPDATES_URI = BASE_URL + "/fetch_friend_updates";
public static final String FETCH_STATUS_URI = BASE_URL + "/fetch_status";
private static HttpClient mHttpClient;
/** URI for sync service */
public static final String SYNC_CONTACTS_URI = BASE_URL + "/sync";
private NetworkUtilities() {
}
@@ -84,224 +91,188 @@ final public class NetworkUtilities {
/**
* Configures the httpClient to connect to the URL provided.
*/
public static void maybeCreateHttpClient() {
if (mHttpClient == null) {
mHttpClient = new DefaultHttpClient();
final HttpParams params = mHttpClient.getParams();
HttpConnectionParams.setConnectionTimeout(params, REGISTRATION_TIMEOUT_MS);
HttpConnectionParams.setSoTimeout(params, REGISTRATION_TIMEOUT_MS);
ConnManagerParams.setTimeout(params, REGISTRATION_TIMEOUT_MS);
}
public static HttpClient getHttpClient() {
HttpClient httpClient = new DefaultHttpClient();
final HttpParams params = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(params, HTTP_REQUEST_TIMEOUT_MS);
HttpConnectionParams.setSoTimeout(params, HTTP_REQUEST_TIMEOUT_MS);
ConnManagerParams.setTimeout(params, HTTP_REQUEST_TIMEOUT_MS);
return httpClient;
}
/**
* Executes the network requests on a separate thread.
*
* @param runnable The runnable instance containing network mOperations to
* be executed.
* Connects to the SampleSync test server, authenticates the provided
* username and password.
*
* @param username The server account username
* @param password The server account password
* @return String The authentication token returned by the server (or null)
*/
public static Thread performOnBackgroundThread(final Runnable runnable) {
final Thread t = new Thread() {
@Override
public void run() {
try {
runnable.run();
} finally {
}
}
};
t.start();
return t;
}
/**
* Connects to the Voiper server, authenticates the provided username and
* password.
*
* @param username The user's username
* @param password The user's password
* @param handler The hander instance from the calling UI thread.
* @param context The context of the calling Activity.
* @return boolean The boolean result indicating whether the user was
* successfully authenticated.
*/
public static boolean authenticate(String username, String password, Handler handler,
final Context context) {
public static String authenticate(String username, String password) {
final HttpResponse resp;
final ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair(PARAM_USERNAME, username));
params.add(new BasicNameValuePair(PARAM_PASSWORD, password));
HttpEntity entity = null;
final HttpEntity entity;
try {
entity = new UrlEncodedFormEntity(params);
} catch (final UnsupportedEncodingException e) {
// this should never happen.
throw new AssertionError(e);
throw new IllegalStateException(e);
}
Log.i(TAG, "Authenticating to: " + AUTH_URI);
final HttpPost post = new HttpPost(AUTH_URI);
post.addHeader(entity.getContentType());
post.setEntity(entity);
maybeCreateHttpClient();
try {
resp = mHttpClient.execute(post);
resp = getHttpClient().execute(post);
String authToken = null;
if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Successful authentication");
InputStream istream = (resp.getEntity() != null) ? resp.getEntity().getContent()
: null;
if (istream != null) {
BufferedReader ireader = new BufferedReader(new InputStreamReader(istream));
authToken = ireader.readLine().trim();
}
sendResult(true, handler, context);
return true;
}
if ((authToken != null) && (authToken.length() > 0)) {
Log.v(TAG, "Successful authentication");
return authToken;
} else {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Error authenticating" + resp.getStatusLine());
}
sendResult(false, handler, context);
return false;
Log.e(TAG, "Error authenticating" + resp.getStatusLine());
return null;
}
} catch (final IOException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "IOException when getting authtoken", e);
}
sendResult(false, handler, context);
return false;
Log.e(TAG, "IOException when getting authtoken", e);
return null;
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getAuthtoken completing");
}
Log.v(TAG, "getAuthtoken completing");
}
}
/**
* Sends the authentication response from server back to the caller main UI
* thread through its handler.
*
* @param result The boolean holding authentication result
* @param handler The main UI thread's handler instance.
* @param context The caller Activity's context.
* Perform 2-way sync with the server-side contacts. We send a request that
* includes all the locally-dirty contacts so that the server can process
* those changes, and we receive (and return) a list of contacts that were
* updated on the server-side that need to be updated locally.
*
* @param account The account being synced
* @param authtoken The authtoken stored in the AccountManager for this
* account
* @param serverSyncState A token returned from the server on the last sync
* @param dirtyContacts A list of the contacts to send to the server
* @return A list of contacts that we need to update locally
*/
private static void sendResult(final Boolean result, final Handler handler,
final Context context) {
if (handler == null || context == null) {
return;
public static List<RawContact> syncContacts(
Account account, String authtoken, long serverSyncState, List<RawContact> dirtyContacts)
throws JSONException, ParseException, IOException, AuthenticationException {
// Convert our list of User objects into a list of JSONObject
List<JSONObject> jsonContacts = new ArrayList<JSONObject>();
for (RawContact rawContact : dirtyContacts) {
jsonContacts.add(rawContact.toJSONObject());
}
handler.post(new Runnable() {
public void run() {
((AuthenticatorActivity) context).onAuthenticationResult(result);
}
});
}
/**
* Attempts to authenticate the user credentials on the server.
*
* @param username The user's username
* @param password The user's password to be authenticated
* @param handler The main UI thread's handler instance.
* @param context The caller Activity's context
* @return Thread The thread on which the network mOperations are executed.
*/
public static Thread attemptAuth(final String username, final String password,
final Handler handler, final Context context) {
// Create a special JSONArray of our JSON contacts
JSONArray buffer = new JSONArray(jsonContacts);
final Runnable runnable = new Runnable() {
public void run() {
authenticate(username, password, handler, context);
}
};
// run on background thread.
return NetworkUtilities.performOnBackgroundThread(runnable);
}
// Create an array that will hold the server-side contacts
// that have been changed (returned by the server).
final ArrayList<RawContact> serverDirtyList = new ArrayList<RawContact>();
/**
* Fetches the list of friend data updates from the server
*
* @param account The account being synced.
* @param authtoken The authtoken stored in AccountManager for this account
* @param lastUpdated The last time that sync was performed
* @return list The list of updates received from the server.
*/
public static List<User> fetchFriendUpdates(Account account, String authtoken, Date lastUpdated)
throws JSONException, ParseException, IOException, AuthenticationException {
final ArrayList<User> friendList = new ArrayList<User>();
// Prepare our POST data
final ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair(PARAM_USERNAME, account.name));
params.add(new BasicNameValuePair(PARAM_PASSWORD, authtoken));
if (lastUpdated != null) {
final SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
params.add(new BasicNameValuePair(PARAM_UPDATED, formatter.format(lastUpdated)));
params.add(new BasicNameValuePair(PARAM_AUTH_TOKEN, authtoken));
params.add(new BasicNameValuePair(PARAM_CONTACTS_DATA, buffer.toString()));
if (serverSyncState > 0) {
params.add(new BasicNameValuePair(PARAM_SYNC_STATE, Long.toString(serverSyncState)));
}
Log.i(TAG, params.toString());
HttpEntity entity = null;
entity = new UrlEncodedFormEntity(params);
final HttpPost post = new HttpPost(FETCH_FRIEND_UPDATES_URI);
HttpEntity entity = new UrlEncodedFormEntity(params);
// Send the updated friends data to the server
Log.i(TAG, "Syncing to: " + SYNC_CONTACTS_URI);
final HttpPost post = new HttpPost(SYNC_CONTACTS_URI);
post.addHeader(entity.getContentType());
post.setEntity(entity);
maybeCreateHttpClient();
final HttpResponse resp = mHttpClient.execute(post);
final HttpResponse resp = getHttpClient().execute(post);
final String response = EntityUtils.toString(resp.getEntity());
if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
// Succesfully connected to the samplesyncadapter server and
// authenticated.
// Extract friends data in json format.
final JSONArray friends = new JSONArray(response);
// Our request to the server was successful - so we assume
// that they accepted all the changes we sent up, and
// that the response includes the contacts that we need
// to update on our side...
final JSONArray serverContacts = new JSONArray(response);
Log.d(TAG, response);
for (int i = 0; i < friends.length(); i++) {
friendList.add(User.valueOf(friends.getJSONObject(i)));
for (int i = 0; i < serverContacts.length(); i++) {
RawContact rawContact = RawContact.valueOf(serverContacts.getJSONObject(i));
if (rawContact != null) {
serverDirtyList.add(rawContact);
}
}
} else {
if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
Log.e(TAG, "Authentication exception in fetching remote contacts");
Log.e(TAG, "Authentication exception in sending dirty contacts");
throw new AuthenticationException();
} else {
Log.e(TAG, "Server error in fetching remote contacts: " + resp.getStatusLine());
Log.e(TAG, "Server error in sending dirty contacts: " + resp.getStatusLine());
throw new IOException();
}
}
return friendList;
return serverDirtyList;
}
/**
* Fetches status messages for the user's friends from the server
*
* @param account The account being synced.
* @param authtoken The authtoken stored in the AccountManager for the
* account
* @return list The list of status messages received from the server.
* Download the avatar image from the server.
*
* @param avatarUrl the URL pointing to the avatar image
* @return a byte array with the raw JPEG avatar image
*/
public static List<User.Status> fetchFriendStatuses(Account account, String authtoken)
throws JSONException, ParseException, IOException, AuthenticationException {
final ArrayList<User.Status> statusList = new ArrayList<User.Status>();
final ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair(PARAM_USERNAME, account.name));
params.add(new BasicNameValuePair(PARAM_PASSWORD, authtoken));
HttpEntity entity = null;
entity = new UrlEncodedFormEntity(params);
final HttpPost post = new HttpPost(FETCH_STATUS_URI);
post.addHeader(entity.getContentType());
post.setEntity(entity);
maybeCreateHttpClient();
final HttpResponse resp = mHttpClient.execute(post);
final String response = EntityUtils.toString(resp.getEntity());
if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
// Succesfully connected to the samplesyncadapter server and
// authenticated.
// Extract friends data in json format.
final JSONArray statuses = new JSONArray(response);
for (int i = 0; i < statuses.length(); i++) {
statusList.add(User.Status.valueOf(statuses.getJSONObject(i)));
}
} else {
if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
Log.e(TAG, "Authentication exception in fetching friend status list");
throw new AuthenticationException();
} else {
Log.e(TAG, "Server error in fetching friend status list");
throw new IOException();
}
public static byte[] downloadAvatar(final String avatarUrl) {
// If there is no avatar, we're done
if (TextUtils.isEmpty(avatarUrl)) {
return null;
}
return statusList;
try {
Log.i(TAG, "Downloading avatar: " + avatarUrl);
// Request the avatar image from the server, and create a bitmap
// object from the stream we get back.
URL url = new URL(avatarUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
try {
final BitmapFactory.Options options = new BitmapFactory.Options();
final Bitmap avatar = BitmapFactory.decodeStream(connection.getInputStream(),
null, options);
// Take the image we received from the server, whatever format it
// happens to be in, and convert it to a JPEG image. Note: we're
// not resizing the avatar - we assume that the image we get from
// the server is a reasonable size...
Log.i(TAG, "Converting avatar to JPEG");
ByteArrayOutputStream convertStream = new ByteArrayOutputStream(
avatar.getWidth() * avatar.getHeight() * 4);
avatar.compress(Bitmap.CompressFormat.JPEG, 95, convertStream);
convertStream.flush();
convertStream.close();
// On pre-Honeycomb systems, it's important to call recycle on bitmaps
avatar.recycle();
return convertStream.toByteArray();
} finally {
connection.disconnect();
}
} catch (MalformedURLException muex) {
// A bad URL - nothing we can really do about it here...
Log.e(TAG, "Malformed avatar URL: " + avatarUrl);
} catch (IOException ioex) {
// If we're unable to download the avatar, it's a bummer but not the
// end of the world. We'll try to get it next time we sync.
Log.e(TAG, "Failed to download user avatar: " + avatarUrl);
}
return null;
}
}

View File

@@ -0,0 +1,260 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.example.android.samplesync.client;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONObject;
import org.json.JSONException;
import java.lang.StringBuilder;
/**
* Represents a low-level contacts RawContact - or at least
* the fields of the RawContact that we care about.
*/
final public class RawContact {
/** The tag used to log to adb console. **/
private static final String TAG = "RawContact";
private final String mUserName;
private final String mFullName;
private final String mFirstName;
private final String mLastName;
private final String mCellPhone;
private final String mOfficePhone;
private final String mHomePhone;
private final String mEmail;
private final String mStatus;
private final String mAvatarUrl;
private final boolean mDeleted;
private final boolean mDirty;
private final long mServerContactId;
private final long mRawContactId;
private final long mSyncState;
public long getServerContactId() {
return mServerContactId;
}
public long getRawContactId() {
return mRawContactId;
}
public String getUserName() {
return mUserName;
}
public String getFirstName() {
return mFirstName;
}
public String getLastName() {
return mLastName;
}
public String getFullName() {
return mFullName;
}
public String getCellPhone() {
return mCellPhone;
}
public String getOfficePhone() {
return mOfficePhone;
}
public String getHomePhone() {
return mHomePhone;
}
public String getEmail() {
return mEmail;
}
public String getStatus() {
return mStatus;
}
public String getAvatarUrl() {
return mAvatarUrl;
}
public boolean isDeleted() {
return mDeleted;
}
public boolean isDirty() {
return mDirty;
}
public long getSyncState() {
return mSyncState;
}
public String getBestName() {
if (!TextUtils.isEmpty(mFullName)) {
return mFullName;
} else if (TextUtils.isEmpty(mFirstName)) {
return mLastName;
} else {
return mFirstName;
}
}
/**
* Convert the RawContact object into a JSON string. From the
* JSONString interface.
* @return a JSON string representation of the object
*/
public JSONObject toJSONObject() {
JSONObject json = new JSONObject();
try {
if (!TextUtils.isEmpty(mFirstName)) {
json.put("f", mFirstName);
}
if (!TextUtils.isEmpty(mLastName)) {
json.put("l", mLastName);
}
if (!TextUtils.isEmpty(mCellPhone)) {
json.put("m", mCellPhone);
}
if (!TextUtils.isEmpty(mOfficePhone)) {
json.put("o", mOfficePhone);
}
if (!TextUtils.isEmpty(mHomePhone)) {
json.put("h", mHomePhone);
}
if (!TextUtils.isEmpty(mEmail)) {
json.put("e", mEmail);
}
if (mServerContactId > 0) {
json.put("i", mServerContactId);
}
if (mRawContactId > 0) {
json.put("c", mRawContactId);
}
if (mDeleted) {
json.put("d", mDeleted);
}
} catch (final Exception ex) {
Log.i(TAG, "Error converting RawContact to JSONObject" + ex.toString());
}
return json;
}
public RawContact(String name, String fullName, String firstName, String lastName,
String cellPhone, String officePhone, String homePhone, String email,
String status, String avatarUrl, boolean deleted, long serverContactId,
long rawContactId, long syncState, boolean dirty) {
mUserName = name;
mFullName = fullName;
mFirstName = firstName;
mLastName = lastName;
mCellPhone = cellPhone;
mOfficePhone = officePhone;
mHomePhone = homePhone;
mEmail = email;
mStatus = status;
mAvatarUrl = avatarUrl;
mDeleted = deleted;
mServerContactId = serverContactId;
mRawContactId = rawContactId;
mSyncState = syncState;
mDirty = dirty;
}
/**
* Creates and returns an instance of the RawContact from the provided JSON data.
*
* @param user The JSONObject containing user data
* @return user The new instance of Sample RawContact created from the JSON data.
*/
public static RawContact valueOf(JSONObject contact) {
try {
final String userName = !contact.isNull("u") ? contact.getString("u") : null;
final int serverContactId = !contact.isNull("i") ? contact.getInt("i") : -1;
// If we didn't get either a username or serverId for the contact, then
// we can't do anything with it locally...
if ((userName == null) && (serverContactId <= 0)) {
throw new JSONException("JSON contact missing required 'u' or 'i' fields");
}
final int rawContactId = !contact.isNull("c") ? contact.getInt("c") : -1;
final String firstName = !contact.isNull("f") ? contact.getString("f") : null;
final String lastName = !contact.isNull("l") ? contact.getString("l") : null;
final String cellPhone = !contact.isNull("m") ? contact.getString("m") : null;
final String officePhone = !contact.isNull("o") ? contact.getString("o") : null;
final String homePhone = !contact.isNull("h") ? contact.getString("h") : null;
final String email = !contact.isNull("e") ? contact.getString("e") : null;
final String status = !contact.isNull("s") ? contact.getString("s") : null;
final String avatarUrl = !contact.isNull("a") ? contact.getString("a") : null;
final boolean deleted = !contact.isNull("d") ? contact.getBoolean("d") : false;
final long syncState = !contact.isNull("x") ? contact.getLong("x") : 0;
return new RawContact(userName, null, firstName, lastName, cellPhone,
officePhone, homePhone, email, status, avatarUrl, deleted,
serverContactId, rawContactId, syncState, false);
} catch (final Exception ex) {
Log.i(TAG, "Error parsing JSON contact object" + ex.toString());
}
return null;
}
/**
* Creates and returns RawContact instance from all the supplied parameters.
*/
public static RawContact create(String fullName, String firstName, String lastName,
String cellPhone, String officePhone, String homePhone,
String email, String status, boolean deleted, long rawContactId,
long serverContactId) {
return new RawContact(null, fullName, firstName, lastName, cellPhone, officePhone,
homePhone, email, status, null, deleted, serverContactId, rawContactId,
-1, true);
}
/**
* Creates and returns a User instance that represents a deleted user.
* Since the user is deleted, all we need are the client/server IDs.
* @param clientUserId The client-side ID for the contact
* @param serverUserId The server-side ID for the contact
* @return a minimal User object representing the deleted contact.
*/
public static RawContact createDeletedContact(long rawContactId, long serverContactId)
{
return new RawContact(null, null, null, null, null, null, null,
null, null, null, true, serverContactId, rawContactId, -1, true);
}
}

View File

@@ -1,155 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.example.android.samplesync.client;
import android.util.Log;
import org.json.JSONObject;
/**
* Represents a sample SyncAdapter user
*/
final public class User {
private final String mUserName;
private final String mFirstName;
private final String mLastName;
private final String mCellPhone;
private final String mOfficePhone;
private final String mHomePhone;
private final String mEmail;
private final boolean mDeleted;
private final int mUserId;
public int getUserId() {
return mUserId;
}
public String getUserName() {
return mUserName;
}
public String getFirstName() {
return mFirstName;
}
public String getLastName() {
return mLastName;
}
public String getCellPhone() {
return mCellPhone;
}
public String getOfficePhone() {
return mOfficePhone;
}
public String getHomePhone() {
return mHomePhone;
}
public String getEmail() {
return mEmail;
}
public boolean isDeleted() {
return mDeleted;
}
private User(String name, String firstName, String lastName, String cellPhone,
String officePhone, String homePhone, String email, Boolean deleted, Integer userId) {
mUserName = name;
mFirstName = firstName;
mLastName = lastName;
mCellPhone = cellPhone;
mOfficePhone = officePhone;
mHomePhone = homePhone;
mEmail = email;
mDeleted = deleted;
mUserId = userId;
}
/**
* Creates and returns an instance of the user from the provided JSON data.
*
* @param user The JSONObject containing user data
* @return user The new instance of Voiper user created from the JSON data.
*/
public static User valueOf(JSONObject user) {
try {
final String userName = user.getString("u");
final String firstName = user.has("f") ? user.getString("f") : null;
final String lastName = user.has("l") ? user.getString("l") : null;
final String cellPhone = user.has("m") ? user.getString("m") : null;
final String officePhone = user.has("o") ? user.getString("o") : null;
final String homePhone = user.has("h") ? user.getString("h") : null;
final String email = user.has("e") ? user.getString("e") : null;
final boolean deleted = user.has("d") ? user.getBoolean("d") : false;
final int userId = user.getInt("i");
return new User(userName, firstName, lastName, cellPhone, officePhone, homePhone,
email, deleted, userId);
} catch (final Exception ex) {
Log.i("User", "Error parsing JSON user object" + ex.toString());
}
return null;
}
/**
* Represents the User's status messages
*
*/
final public static class Status {
private final Integer mUserId;
private final String mStatus;
public int getUserId() {
return mUserId;
}
public String getStatus() {
return mStatus;
}
public Status(Integer userId, String status) {
mUserId = userId;
mStatus = status;
}
public static User.Status valueOf(JSONObject userStatus) {
try {
final int userId = userStatus.getInt("i");
final String status = userStatus.getString("s");
return new User.Status(userId, status);
} catch (final Exception ex) {
Log.i("User.Status", "Error parsing JSON user object");
}
return null;
}
}
}