1 /* 2 * Copyright (C) 2015 Samsung System LSI 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 package com.android.bluetooth.map; 16 17 import android.bluetooth.BluetoothProfile; 18 import android.bluetooth.BluetoothProtoEnums; 19 import android.util.Log; 20 21 import com.android.bluetooth.BluetoothStatsLog; 22 import com.android.bluetooth.SignedLongLong; 23 import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils; 24 25 import org.xmlpull.v1.XmlPullParser; 26 import org.xmlpull.v1.XmlPullParserException; 27 import org.xmlpull.v1.XmlSerializer; 28 29 import java.io.IOException; 30 import java.io.UnsupportedEncodingException; 31 import java.text.ParseException; 32 import java.text.SimpleDateFormat; 33 import java.util.Date; 34 35 // Next tag value for ContentProfileErrorReportUtils.report(): 1 36 public class BluetoothMapConvoContactElement 37 implements Comparable<BluetoothMapConvoContactElement> { 38 39 public static final long CONTACT_ID_TYPE_SMS_MMS = 1; 40 public static final long CONTACT_ID_TYPE_EMAIL = 2; 41 public static final long CONTACT_ID_TYPE_IM = 3; 42 43 private static final String XML_ATT_PRIORITY = "priority"; 44 private static final String XML_ATT_PRESENCE_STATUS = "presence_status"; 45 private static final String XML_ATT_PRESENCE_AVAILABILITY = "presence_availability"; 46 private static final String XML_ATT_X_BT_UID = "x_bt_uid"; 47 private static final String XML_ATT_LAST_ACTIVITY = "last_activity"; 48 private static final String XML_ATT_CHAT_STATE = "chat_state"; 49 private static final String XML_ATT_NAME = "name"; 50 private static final String XML_ATT_DISPLAY_NAME = "display_name"; 51 private static final String XML_ATT_UCI = "x_bt_uci"; 52 protected static final String XML_TAG_CONVOCONTACT = "convocontact"; 53 private static final String TAG = "BluetoothMapConvoContactElement"; 54 55 private String mUci = null; 56 private String mName = null; 57 private String mDisplayName = null; 58 private String mPresenceStatus = null; 59 private int mPresenceAvailability = -1; 60 private int mPriority = -1; 61 private long mLastActivity = -1; 62 private SignedLongLong mBtUid = null; 63 private int mChatState = -1; 64 createFromMapContact( MapContact contact, String address)65 public static BluetoothMapConvoContactElement createFromMapContact( 66 MapContact contact, String address) { 67 BluetoothMapConvoContactElement newElement = new BluetoothMapConvoContactElement(); 68 newElement.mUci = address; 69 // TODO: For now we use the ID as BT-UID 70 newElement.mBtUid = new SignedLongLong(contact.getId(), 0); 71 newElement.mDisplayName = contact.getName(); 72 return newElement; 73 } 74 BluetoothMapConvoContactElement( String uci, String name, String displayName, String presenceStatus, int presenceAvailability, long lastActivity, int chatState, int priority, String btUid)75 public BluetoothMapConvoContactElement( 76 String uci, 77 String name, 78 String displayName, 79 String presenceStatus, 80 int presenceAvailability, 81 long lastActivity, 82 int chatState, 83 int priority, 84 String btUid) { 85 this.mUci = uci; 86 this.mName = name; 87 this.mDisplayName = displayName; 88 this.mPresenceStatus = presenceStatus; 89 this.mPresenceAvailability = presenceAvailability; 90 this.mLastActivity = lastActivity; 91 this.mChatState = chatState; 92 this.mPresenceStatus = presenceStatus; 93 this.mPriority = priority; 94 if (btUid != null) { 95 try { 96 this.mBtUid = SignedLongLong.fromString(btUid); 97 } catch (UnsupportedEncodingException e) { 98 ContentProfileErrorReportUtils.report( 99 BluetoothProfile.MAP, 100 BluetoothProtoEnums.BLUETOOTH_MAP_CONVO_CONTACT_ELEMENT, 101 BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 102 0); 103 Log.w(TAG, e); 104 } 105 } 106 } 107 BluetoothMapConvoContactElement()108 public BluetoothMapConvoContactElement() { 109 // TODO Auto-generated constructor stub 110 } 111 getPresenceStatus()112 public String getPresenceStatus() { 113 return mPresenceStatus; 114 } 115 getDisplayName()116 public String getDisplayName() { 117 return mDisplayName; 118 } 119 setDisplayName(String displayName)120 public void setDisplayName(String displayName) { 121 this.mDisplayName = displayName; 122 } 123 setPresenceStatus(String presenceStatus)124 public void setPresenceStatus(String presenceStatus) { 125 this.mPresenceStatus = presenceStatus; 126 } 127 getPresenceAvailability()128 public int getPresenceAvailability() { 129 return mPresenceAvailability; 130 } 131 setPresenceAvailability(int presenceAvailability)132 public void setPresenceAvailability(int presenceAvailability) { 133 this.mPresenceAvailability = presenceAvailability; 134 } 135 getPriority()136 public int getPriority() { 137 return mPriority; 138 } 139 setPriority(int priority)140 public void setPriority(int priority) { 141 this.mPriority = priority; 142 } 143 getName()144 public String getName() { 145 return mName; 146 } 147 setName(String name)148 public void setName(String name) { 149 this.mName = name; 150 } 151 getBtUid()152 public String getBtUid() { 153 return mBtUid.toHexString(); 154 } 155 setBtUid(SignedLongLong btUid)156 public void setBtUid(SignedLongLong btUid) { 157 this.mBtUid = btUid; 158 } 159 getChatState()160 public int getChatState() { 161 return mChatState; 162 } 163 setChatState(int chatState)164 public void setChatState(int chatState) { 165 this.mChatState = chatState; 166 } 167 setChatState(String chatState)168 public void setChatState(String chatState) { 169 this.mChatState = Integer.valueOf(chatState); 170 } 171 getLastActivityString()172 public String getLastActivityString() { 173 SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); 174 Date date = new Date(mLastActivity); 175 return format.format(date); // Format to YYYYMMDDTHHMMSS local time 176 } 177 setLastActivity(long dateTime)178 public void setLastActivity(long dateTime) { 179 this.mLastActivity = dateTime; 180 } 181 setLastActivity(String lastActivity)182 public void setLastActivity(String lastActivity) throws ParseException { 183 SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); 184 Date date = format.parse(lastActivity); 185 this.mLastActivity = date.getTime(); 186 } 187 setContactId(String uci)188 public void setContactId(String uci) { 189 this.mUci = uci; 190 } 191 getContactId()192 public String getContactId() { 193 return mUci; 194 } 195 196 @Override compareTo(BluetoothMapConvoContactElement e)197 public int compareTo(BluetoothMapConvoContactElement e) { 198 if (this.mLastActivity < e.mLastActivity) { 199 return 1; 200 } else if (this.mLastActivity > e.mLastActivity) { 201 return -1; 202 } else { 203 return 0; 204 } 205 } 206 207 /* Encode the MapConvoContactElement into the StringBuilder reference. 208 * Here we have taken the choice not to report empty attributes, to reduce the 209 * amount of data to be transfered over BT. */ encode(XmlSerializer xmlConvoElement)210 public void encode(XmlSerializer xmlConvoElement) 211 throws IllegalArgumentException, IllegalStateException, IOException { 212 // construct the XML tag for a single contact in the convolisting element. 213 xmlConvoElement.startTag(null, XML_TAG_CONVOCONTACT); 214 if (mUci != null) { 215 xmlConvoElement.attribute(null, XML_ATT_UCI, mUci); 216 } 217 if (mDisplayName != null) { 218 xmlConvoElement.attribute( 219 null, XML_ATT_DISPLAY_NAME, BluetoothMapUtils.stripInvalidChars(mDisplayName)); 220 } 221 if (mName != null) { 222 xmlConvoElement.attribute( 223 null, XML_ATT_NAME, BluetoothMapUtils.stripInvalidChars(mName)); 224 } 225 if (mChatState != -1) { 226 xmlConvoElement.attribute(null, XML_ATT_CHAT_STATE, String.valueOf(mChatState)); 227 } 228 if (mLastActivity != -1) { 229 xmlConvoElement.attribute(null, XML_ATT_LAST_ACTIVITY, this.getLastActivityString()); 230 } 231 if (mBtUid != null) { 232 xmlConvoElement.attribute(null, XML_ATT_X_BT_UID, mBtUid.toHexString()); 233 } 234 if (mPresenceAvailability != -1) { 235 xmlConvoElement.attribute( 236 null, XML_ATT_PRESENCE_AVAILABILITY, String.valueOf(mPresenceAvailability)); 237 } 238 if (mPresenceStatus != null) { 239 xmlConvoElement.attribute(null, XML_ATT_PRESENCE_STATUS, mPresenceStatus); 240 } 241 if (mPriority != -1) { 242 xmlConvoElement.attribute(null, XML_ATT_PRIORITY, String.valueOf(mPriority)); 243 } 244 245 xmlConvoElement.endTag(null, XML_TAG_CONVOCONTACT); 246 } 247 248 /** 249 * Call this function to create a BluetoothMapConvoContactElement. Will consume the end-tag. 250 * 251 * @param parser must point into XML_TAG_CONVERSATION tag, hence attributes can be read. 252 */ createFromXml(XmlPullParser parser)253 public static BluetoothMapConvoContactElement createFromXml(XmlPullParser parser) 254 throws ParseException, XmlPullParserException, IOException { 255 int count = parser.getAttributeCount(); 256 BluetoothMapConvoContactElement newElement; 257 if (count < 1) { 258 throw new IllegalArgumentException( 259 XML_TAG_CONVOCONTACT + " is not decorated with attributes"); 260 } 261 newElement = new BluetoothMapConvoContactElement(); 262 for (int i = 0; i < count; i++) { 263 String attributeName = parser.getAttributeName(i).trim(); 264 String attributeValue = parser.getAttributeValue(i); 265 if (attributeName.equalsIgnoreCase(XML_ATT_UCI)) { 266 newElement.mUci = attributeValue; 267 } else if (attributeName.equalsIgnoreCase(XML_ATT_NAME)) { 268 newElement.mName = attributeValue; 269 } else if (attributeName.equalsIgnoreCase(XML_ATT_DISPLAY_NAME)) { 270 newElement.mDisplayName = attributeValue; 271 } else if (attributeName.equalsIgnoreCase(XML_ATT_CHAT_STATE)) { 272 newElement.setChatState(attributeValue); 273 } else if (attributeName.equalsIgnoreCase(XML_ATT_LAST_ACTIVITY)) { 274 newElement.setLastActivity(attributeValue); 275 } else if (attributeName.equalsIgnoreCase(XML_ATT_X_BT_UID)) { 276 newElement.setBtUid(SignedLongLong.fromString(attributeValue)); 277 } else if (attributeName.equalsIgnoreCase(XML_ATT_PRESENCE_AVAILABILITY)) { 278 newElement.mPresenceAvailability = Integer.parseInt(attributeValue); 279 } else if (attributeName.equalsIgnoreCase(XML_ATT_PRESENCE_STATUS)) { 280 newElement.setPresenceStatus(attributeValue); 281 } else if (attributeName.equalsIgnoreCase(XML_ATT_PRIORITY)) { 282 newElement.setPriority(Integer.parseInt(attributeValue)); 283 } else { 284 Log.w(TAG, "Unknown XML attribute: " + parser.getAttributeName(i)); 285 } 286 } 287 parser.nextTag(); // Consume the end-tag 288 return newElement; 289 } 290 291 @Override equals(Object obj)292 public boolean equals(Object obj) { 293 if (this == obj) { 294 return true; 295 } 296 if (obj == null) { 297 return false; 298 } 299 if (getClass() != obj.getClass()) { 300 return false; 301 } 302 BluetoothMapConvoContactElement other = (BluetoothMapConvoContactElement) obj; 303 /* As we use equals only for test, we don't compare auto assigned values 304 * if (mBtUid == null) { 305 if (other.mBtUid != null) { 306 return false; 307 } 308 } else if (!mBtUid.equals(other.mBtUid)) { 309 return false; 310 }*/ 311 if (mChatState != other.mChatState) { 312 return false; 313 } 314 if (mDisplayName == null) { 315 if (other.mDisplayName != null) { 316 return false; 317 } 318 } else if (!mDisplayName.equals(other.mDisplayName)) { 319 return false; 320 } 321 /* As we use equals only for test, we don't compare auto assigned values 322 * if (mId == null) { 323 if (other.mId != null) { 324 return false; 325 } 326 } else if (!mId.equals(other.mId)) { 327 return false; 328 }*/ 329 if (mLastActivity != other.mLastActivity) { 330 return false; 331 } 332 if (mName == null) { 333 if (other.mName != null) { 334 return false; 335 } 336 } else if (!mName.equals(other.mName)) { 337 return false; 338 } 339 if (mPresenceAvailability != other.mPresenceAvailability) { 340 return false; 341 } 342 if (mPresenceStatus == null) { 343 if (other.mPresenceStatus != null) { 344 return false; 345 } 346 } else if (!mPresenceStatus.equals(other.mPresenceStatus)) { 347 return false; 348 } 349 if (mPriority != other.mPriority) { 350 return false; 351 } 352 return true; 353 } 354 } 355