/* * 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 { 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 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 getContacts() { return mContacts; } public void setContacts(List contacts) { this.mContacts = contacts; } public void addContact(BluetoothMapConvoContactElement contact){ if(mContacts == null) mContacts = new ArrayList(); 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