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