/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at
* src/com/vodafone360/people/VODAFONE.LICENSE.txt or
* http://github.com/360/360-Engine-for-Android
* See the License for the specific language governing permissions and
* limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each file and
* include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt.
* If applicable, add the following below this CDDL HEADER, with the fields
* enclosed by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2010 Vodafone Sales & Services Ltd. All rights reserved.
* Use is subject to license terms.
*/
package com.vodafone360.people.engine.presence;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import android.os.Parcel;
import android.os.Parcelable;
import com.vodafone360.people.Settings;
import com.vodafone360.people.datatypes.ContactSummary;
import com.vodafone360.people.datatypes.ContactSummary.OnlineStatus;
import com.vodafone360.people.engine.presence.NetworkPresence.SocialNetwork;
import com.vodafone360.people.utils.LogUtils;
/**
* User is a class encapsulating the information about a user's presence state.
*/
public class User implements Parcelable {
/**
* This constant is to identify the key for the {@link SocialNetwork} name
* in the Bundle of UiEvent generated by presence status change.
*/
public static final String NETWORK = "network";
/**
* This constant is to identify the key for the {@link OnlineStatus} name
* in the Bundle of UiEvent generated by presence status change.
*/
public static final String STATUS = "status";
/**
* This constant is to identify the key for the source of status change delivered
* in the Bundle of UiEvent generated by presence status change.
*/
public static final String SOURCE = "source";
/**
* This constant is to identify the source of status change delivered
* in the Bundle of UiEvent generated by presence status change as "requested by client".
*/
public static final int SOURCE_REQUESTED = 0;
/**
* This constant is to identify the source of status change delivered
* in the Bundle of UiEvent generated by presence status change as "received by client".
*/
public static final int SOURCE_RECEIVED = 1;
/**
* This constant is to identify the source of status change delivered
* in the Bundle of UiEvent generated by presence status change as "timed out".
*/
public static final int SOURCE_TIMED_OUT = 2;
private static final String COLUMNS = "::";
private static final String PRESENCE_PC = "pc",
PRESENCE_MOBILE = "mobile";
/** Database ID of the contact (e.g. "userid@gmail.com"). **/
private long mLocalContactId;
/** Overall presence state displayed in the common contact list. **/
private int mOverallOnline;
/**
* Communities presence status:
* {google:online, pc:online, mobile:online}.
*/
private ArrayList<NetworkPresence> mPayload;
/**
* Default Constructor.
*/
public User() {
}
/**
* Constructor.
*
* @param userId - user id in the contact list, e.g.
* "google::userid@gmail.com" or "882339"
* @param payload - communities presence status {google:online, pc:online,
* mobile:online}
*/
public User(String userId, Hashtable<String, String> payload) {
if (payload != null) {
if (Settings.LOG_PRESENCE_PUSH_ON_LOGCAT) {
LogUtils.logWithName(LogUtils.PRESENCE_INFO_TAG, "user id:"+ userId + ", " + payload);
}
if (payload.containsKey(PRESENCE_PC)) {
if (Settings.LOG_PRESENCE_PUSH_ON_LOGCAT) {
LogUtils.logWithName(LogUtils.PRESENCE_INFO_TAG, "removed 360PC presence for " + userId);
}
payload.remove(PRESENCE_PC);
}
if (payload.containsKey(PRESENCE_MOBILE)) {
if (Settings.LOG_PRESENCE_PUSH_ON_LOGCAT) {
LogUtils.logWithName(LogUtils.PRESENCE_INFO_TAG, "removed 360MOBILE presence for " + userId);
}
payload.remove(PRESENCE_MOBILE);
}
mOverallOnline = isOverallOnline(payload);
mPayload = createPayload(userId, payload);
}
}
/**
* This method returns the localContactId for this contact in DB across the
* application .
*
* @return the localContactId for this contact in DB
*/
public long getLocalContactId() {
return mLocalContactId;
}
/**
* This method sets localContactId for this User. The id comes from DB.
* @param mLocalContactId long id.
*/
public void setLocalContactId(long mLocalContactId) {
this.mLocalContactId = mLocalContactId;
}
/**
* Returns communities presence status
*
* @return communities presence status, e.g. {google:online, pc:online,
* mobile:online}
*/
public ArrayList<NetworkPresence> getPayload() {
return mPayload;
}
/**
* The method returns the OnlineStatus of this user on the provided network
* @param network SocialNetwork.
* @return NULL if network is null, OnlineStatus.OFFLINE if the information
* about presence state on this network is not available, and actual status in other cases.
*/
public OnlineStatus getStatusForNetwork(SocialNetwork network) {
if (network == null) {
return null;
}
OnlineStatus os = OnlineStatus.OFFLINE;
if (mPayload != null) {
for (NetworkPresence np : mPayload) {
if (np.getNetworkId() == network.ordinal()) {
os = OnlineStatus.getValue(np.getOnlineStatusId());
break;
}
}
}
return os;
}
/**
* Returns communities presence status
*
* @return communities presence status, e.g. {google:online, pc:online,
* mobile:online}
*/
public void setPayload(ArrayList<NetworkPresence> payload) {
mPayload = payload;
}
/**
* Returns the overall user presence status
*
* @return true if user is online at least at one community, e.g. true if
* {google:offline, pc:offline, mobile:online}
*/
private int isOverallOnline(Hashtable<String, String> payload) {
if (payload != null) {
if (payload.values().contains(ContactSummary.OnlineStatus.ONLINE.toString()))
return ContactSummary.OnlineStatus.ONLINE.ordinal();
if (payload.values().contains(ContactSummary.OnlineStatus.INVISIBLE.toString()))
return ContactSummary.OnlineStatus.INVISIBLE.ordinal();
if (payload.values().contains(ContactSummary.OnlineStatus.IDLE.toString()))
return ContactSummary.OnlineStatus.IDLE.ordinal();
}
return ContactSummary.OnlineStatus.OFFLINE.ordinal();
}
/**
* Returns the overall user presence status: in fact the one from the below
* status states first encountered for all known user accounts next:
* INVISIBLE, ONLINE, IDLE, OFFLINE
*
* @return presence state
*/
public int isOnline() {
return mOverallOnline;
}
/**
* @param payload
* @return
*/
private ArrayList<NetworkPresence> createPayload(String userId,
Hashtable<String, String> payload) {
ArrayList<NetworkPresence> presenceList = new ArrayList<NetworkPresence>(payload.size());
String parsedUserId = parseUserName(userId);
String key = null;
SocialNetwork network = null;
String value = null;
OnlineStatus status = null;
for (Enumeration<String> en = payload.keys(); en.hasMoreElements();) {
key = en.nextElement();
network = SocialNetwork.getValue(key);
if (network != null) {
int keyIdx = network.ordinal();
value = payload.get(key);
if (value != null) {
status = OnlineStatus.getValue(value);
if (status != null) {
int valueIdx = status.ordinal();
presenceList.add(new NetworkPresence(parsedUserId, keyIdx, valueIdx));
}
}
}
}
return presenceList;
}
/**
* @param user
* @return
*/
private static String parseUserName(String userId) {
if (userId != null) {
int columnsIndex = userId.indexOf(COLUMNS);
if (columnsIndex > -1) {
return userId.substring(columnsIndex + COLUMNS.length());
} else {
return userId;
}
}
return null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + mOverallOnline;
result = prime * result + ((mPayload == null) ? 0 : mPayload.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User)obj;
if (mOverallOnline != other.mOverallOnline)
return false;
if (mPayload == null) {
if (other.mPayload != null)
return false;
} else if (!mPayload.equals(other.mPayload))
return false;
return true;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("User [mLocalContactId=");
sb.append(mLocalContactId);
sb.append(", mOverallOnline="); sb.append(mOverallOnline);
sb.append(", mPayload="); sb.append(mPayload); sb.append("]");
return sb.toString();
}
/**
* This method sets the overall user presence status,
* @parameter the online status id - the ordinal, see @OnlineStatus
*/
public void setOverallOnline(int overallOnline) {
this.mOverallOnline = overallOnline;
}
/**
* This method removes the network presence information with the given presence id from the User.
* @param ordinal - the network id, ordinal in @see SocialNetworks
*/
public void removeNetwork(int ordinal) {
Iterator<NetworkPresence> itr = mPayload.iterator();
NetworkPresence presence = null;
while (itr.hasNext()) {
presence = itr.next();
if (presence.getNetworkId() == ordinal) {
itr.remove();
break;
}
}
}
@Override
public final int describeContents() {
return 0;
}
@Override
public final void writeToParcel(final Parcel dest, final int flags) {
dest.writeLong(mLocalContactId);
dest.writeInt(mOverallOnline);
writePayloadToParcel(dest, flags);
}
/***
* Parcelable constructor for User.
*
* @param source User Parcel.
*/
private void readFromParcel(final Parcel source) {
mLocalContactId = source.readLong();
mOverallOnline = source.readInt();
readPayloadFromParcel(source);
}
/**
* Helper function to get the payload into & out of a parcel.
* Keeping this code in a helper function should help maintenance.
* Doing it this way seems to be the only way to avoid constant
* ClassNotFoundExceptions even with proper classloaders in place.
*
* @param dest User Parcel.
* @param flags Flags.
*/
public final void writePayloadToParcel(final Parcel dest, final int flags) {
// Locals more efficient than members on Dalvik VM
ArrayList<NetworkPresence> payload = mPayload;
dest.writeInt(payload.size());
for (NetworkPresence netPres : payload) {
netPres.writeToParcel(dest, flags);
}
}
/**
* Helper function to get the payload into & out of a parcel.
* Keeping this code in a helper function should help maintenance.
* Doing it this way seems to be the only way to avoid constant
* ClassNotFoundExceptions even with proper classloaders in place.
*
* @param source User Parcel.
*/
private void readPayloadFromParcel(final Parcel source) {
// Could do this directly into mPayload but locals are more efficient
ArrayList<NetworkPresence> payload = new ArrayList<NetworkPresence>();
for (int i = 0; i < source.readInt(); i++) {
payload.add(new NetworkPresence(source));
}
mPayload = payload;
}
/***
* Parcelable.Creator for User.
*/
public static final Parcelable.Creator<User> CREATOR
= new Parcelable.Creator<User>() {
@Override
public User createFromParcel(final Parcel source) {
User newUser = new User();
newUser.readFromParcel(source);
return newUser;
}
@Override
public User[] newArray(final int size) {
return new User[size];
}
};
}