blob: 4947aa4c10d22a1d179be2c3e26ea6f781e8a211 [file] [log] [blame]
/*
* Copyright (C) 2013 Samsung System LSI
* 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.android.bluetooth.map;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.util.Log;
import com.android.bluetooth.SignedLongLong;
import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
import com.android.internal.util.XmlUtils;
public class BluetoothMapConvoListingElement
implements Comparable<BluetoothMapConvoListingElement> {
public static final String XML_TAG_CONVERSATION = "conversation";
private static final String XML_ATT_LAST_ACTIVITY = "last_activity";
private static final String XML_ATT_NAME = "name";
private static final String XML_ATT_ID = "id";
private static final String XML_ATT_READ = "readstatus";
private static final String XML_ATT_VERSION_COUNTER = "version_counter";
private static final String XML_ATT_SUMMARY = "summary";
private static final String TAG = "BluetoothMapConvoListingElement";
private static final boolean D = BluetoothMapService.DEBUG;
private static final boolean V = BluetoothMapService.VERBOSE;
private SignedLongLong mId = null;
private String mName = ""; //title of the conversation #REQUIRED, but allowed empty
private long mLastActivity = -1;
private boolean mRead = false;
private boolean mReportRead = false; // TODO: Is this needed? - false means UNKNOWN
private List<BluetoothMapConvoContactElement> mContacts;
private long mVersionCounter = -1;
private int mCursorIndex = 0;
private TYPE mType = null;
private String mSummary = null;
// Used only to keep track of changes to convoListVersionCounter;
private String mSmsMmsContacts = null;
public int getCursorIndex() {
return mCursorIndex;
}
public void setCursorIndex(int cursorIndex) {
this.mCursorIndex = cursorIndex;
if(D) Log.d(TAG, "setCursorIndex: " + cursorIndex);
}
public long getVersionCounter(){
return mVersionCounter;
}
public void setVersionCounter(long vcount){
if(D) Log.d(TAG, "setVersionCounter: " + vcount);
this.mVersionCounter = vcount;
}
public void incrementVersionCounter() {
mVersionCounter++;
}
private void setVersionCounter(String vcount){
if(D) Log.d(TAG, "setVersionCounter: " + vcount);
try {
this.mVersionCounter = Long.parseLong(vcount);
} catch (NumberFormatException e) {
Log.w(TAG, "unable to parse XML versionCounter:" + vcount);
mVersionCounter = -1;
}
}
public String getName() {
return mName;
}
public void setName(String name) {
if(D) Log.d(TAG, "setName: " + name);
this.mName = name;
}
public TYPE getType() {
return mType;
}
public void setType(TYPE type) {
this.mType = type;
}
public List<BluetoothMapConvoContactElement> getContacts() {
return mContacts;
}
public void setContacts(List<BluetoothMapConvoContactElement> contacts) {
this.mContacts = contacts;
}
public void addContact(BluetoothMapConvoContactElement contact){
if(mContacts == null)
mContacts = new ArrayList<BluetoothMapConvoContactElement>();
mContacts.add(contact);
}
public void removeContact(BluetoothMapConvoContactElement contact){
mContacts.remove(contact);
}
public void removeContact(int index){
mContacts.remove(index);
}
public long getLastActivity() {
return mLastActivity;
}
public String getLastActivityString() {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
Date date = new Date(mLastActivity);
return format.format(date); // Format to YYYYMMDDTHHMMSS local time
}
public void setLastActivity(long last) {
if(D) Log.d(TAG, "setLastActivity: " + last);
this.mLastActivity = last;
}
public void setLastActivity(String lastActivity)throws ParseException {
// TODO: Encode with time-zone if MCE requests it
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
Date date = format.parse(lastActivity);
this.mLastActivity = date.getTime();
}
public String getRead() {
if(mReportRead == false) {
return "UNKNOWN";
}
return (mRead?"READ":"UNREAD");
}
public boolean getReadBool() {
return mRead;
}
public void setRead(boolean read, boolean reportRead) {
this.mRead = read;
if(D) Log.d(TAG, "setRead: " + read);
this.mReportRead = reportRead;
}
private void setRead(String value) {
if(value.trim().equalsIgnoreCase("yes")) {
mRead = true;
} else {
mRead = false;
}
mReportRead = true;
}
/**
* Set the conversation ID
* @param type 0 if the thread ID is valid across all message types in the instance - else
* use one of the CONVO_ID_xxx types.
* @param threadId the conversation ID
*/
public void setConvoId(long type, long threadId) {
this.mId = new SignedLongLong(threadId,type);
if(D) Log.d(TAG, "setConvoId: " + threadId + " type:" + type);
}
public String getConvoId(){
return mId.toHexString();
}
public long getCpConvoId() {
return mId.getLeastSignificantBits();
}
public void setSummary(String summary) {
mSummary = summary;
}
public String getFullSummary() {
return mSummary;
}
/* Get a valid UTF-8 string of maximum 256 bytes */
private String getSummary() {
if(mSummary != null) {
try {
return new String(BluetoothMapUtils.truncateUtf8StringToBytearray(mSummary, 256),
"UTF-8");
} catch (UnsupportedEncodingException e) {
// This cannot happen on an Android platform - UTF-8 is mandatory
Log.e(TAG,"Missing UTF-8 support on platform", e);
}
}
return null;
}
public String getSmsMmsContacts() {
return mSmsMmsContacts;
}
public void setSmsMmsContacts(String smsMmsContacts) {
mSmsMmsContacts = smsMmsContacts;
}
public int compareTo(BluetoothMapConvoListingElement e) {
if (this.mLastActivity < e.mLastActivity) {
return 1;
} else if (this.mLastActivity > e.mLastActivity) {
return -1;
} else {
return 0;
}
}
/* Encode the MapMessageListingElement into the StringBuilder reference.
* Here we have taken the choice not to report empty attributes, to reduce the
* amount of data to be transfered over BT. */
public void encode(XmlSerializer xmlConvoElement)
throws IllegalArgumentException, IllegalStateException, IOException
{
// contruct the XML tag for a single conversation in the convolisting
xmlConvoElement.startTag(null, XML_TAG_CONVERSATION);
xmlConvoElement.attribute(null, XML_ATT_ID, mId.toHexString());
if(mName != null) {
xmlConvoElement.attribute(null, XML_ATT_NAME,
BluetoothMapUtils.stripInvalidChars(mName));
}
if(mLastActivity != -1) {
xmlConvoElement.attribute(null, XML_ATT_LAST_ACTIVITY,
getLastActivityString());
}
// Even though this is implied, the value "UNKNOWN" kind of indicated it is required.
if(mReportRead == true) {
xmlConvoElement.attribute(null, XML_ATT_READ, getRead());
}
if(mVersionCounter != -1) {
xmlConvoElement.attribute(null, XML_ATT_VERSION_COUNTER,
Long.toString(getVersionCounter()));
}
if(mSummary != null) {
xmlConvoElement.attribute(null, XML_ATT_SUMMARY, getSummary());
}
if(mContacts != null){
for(BluetoothMapConvoContactElement contact:mContacts){
contact.encode(xmlConvoElement);
}
}
xmlConvoElement.endTag(null, XML_TAG_CONVERSATION);
}
/**
* Consumes a conversation tag. It is expected that the parser is beyond the start-tag event,
* with the name "conversation".
* @param parser
* @return
* @throws XmlPullParserException
* @throws IOException
*/
public static BluetoothMapConvoListingElement createFromXml(XmlPullParser parser)
throws XmlPullParserException, IOException, ParseException {
BluetoothMapConvoListingElement newElement = new BluetoothMapConvoListingElement();
int count = parser.getAttributeCount();
int type;
for (int i = 0; i<count; i++) {
String attributeName = parser.getAttributeName(i).trim();
String attributeValue = parser.getAttributeValue(i);
if(attributeName.equalsIgnoreCase(XML_ATT_ID)) {
newElement.mId = SignedLongLong.fromString(attributeValue);
} else if(attributeName.equalsIgnoreCase(XML_ATT_NAME)) {
newElement.mName = attributeValue;
} else if(attributeName.equalsIgnoreCase(XML_ATT_LAST_ACTIVITY)) {
newElement.setLastActivity(attributeValue);
} else if(attributeName.equalsIgnoreCase(XML_ATT_READ)) {
newElement.setRead(attributeValue);
} else if(attributeName.equalsIgnoreCase(XML_ATT_VERSION_COUNTER)) {
newElement.setVersionCounter(attributeValue);
} else if(attributeName.equalsIgnoreCase(XML_ATT_SUMMARY)) {
newElement.setSummary(attributeValue);
} else {
if(D) Log.i(TAG,"Unknown XML attribute: " + parser.getAttributeName(i));
}
}
// Now determine if we get an end-tag, or a new start tag for contacts
while((type=parser.next()) != XmlPullParser.END_TAG
&& type != XmlPullParser.END_DOCUMENT ) {
// Skip until we get a start tag
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
// Skip until we get a convocontact tag
String name = parser.getName().trim();
if(name.equalsIgnoreCase(BluetoothMapConvoContactElement.XML_TAG_CONVOCONTACT)){
newElement.addContact(BluetoothMapConvoContactElement.createFromXml(parser));
} else {
if(D) Log.i(TAG,"Unknown XML tag: " + name);
XmlUtils.skipCurrentTag(parser);
continue;
}
}
// As we have extracted all attributes, we should expect an end-tag
// parser.nextTag(); // consume the end-tag
// TODO: Is this needed? - we should already be at end-tag, as this is the top condition
return newElement;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
BluetoothMapConvoListingElement other = (BluetoothMapConvoListingElement) obj;
if (mContacts == null) {
if (other.mContacts != null) {
return false;
}
} else if (!mContacts.equals(other.mContacts)) {
return false;
}
/* As we use equals only for test, we don't compare auto assigned values
* if (mId == null) {
if (other.mId != null) {
return false;
}
} else if (!mId.equals(other.mId)) {
return false;
} */
if (mLastActivity != other.mLastActivity) {
return false;
}
if (mName == null) {
if (other.mName != null) {
return false;
}
} else if (!mName.equals(other.mName)) {
return false;
}
if (mRead != other.mRead) {
return false;
}
return true;
}
/* @Override
public boolean equals(Object o) {
return true;
};
*/
}