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 java.io.IOException;
18 import java.io.UnsupportedEncodingException;
19 import java.text.ParseException;
20 import java.text.SimpleDateFormat;
21 import java.util.Date;
22 
23 import org.xmlpull.v1.XmlPullParser;
24 import org.xmlpull.v1.XmlPullParserException;
25 import org.xmlpull.v1.XmlSerializer;
26 
27 
28 import android.util.Log;
29 
30 import com.android.bluetooth.SignedLongLong;
31 
32 public class BluetoothMapConvoContactElement
33     implements Comparable<BluetoothMapConvoContactElement> {
34 
35     public static final long CONTACT_ID_TYPE_SMS_MMS = 1;
36     public static final long CONTACT_ID_TYPE_EMAIL   = 2;
37     public static final long CONTACT_ID_TYPE_IM      = 3;
38 
39     private static final String XML_ATT_PRIORITY = "priority";
40     private static final String XML_ATT_PRESENCE_STATUS = "presence_status";
41     private static final String XML_ATT_PRESENCE_AVAILABILITY = "presence_availability";
42     private static final String XML_ATT_X_BT_UID = "x_bt_uid";
43     private static final String XML_ATT_LAST_ACTIVITY = "last_activity";
44     private static final String XML_ATT_CHAT_STATE = "chat_state";
45     private static final String XML_ATT_NAME = "name";
46     private static final String XML_ATT_DISPLAY_NAME = "display_name";
47     private static final String XML_ATT_UCI = "x_bt_uci";
48     protected static final String XML_TAG_CONVOCONTACT = "convocontact";
49     private static final String TAG = "BluetoothMapConvoContactElement";
50     private static final boolean D = false;
51     private static final boolean V = false;
52 
53     private String mUci = null;
54     private String mName = null;
55     private String mDisplayName = null;
56     private String mPresenceStatus = null;
57     private int mPresenceAvailability = -1;
58     private int mPriority = -1;
59     private long mLastActivity = -1;
60     private SignedLongLong mBtUid = null;
61     private int mChatState = -1;
62 
createFromMapContact(MapContact contact, String address)63     public static BluetoothMapConvoContactElement createFromMapContact(MapContact contact,
64             String address) {
65         BluetoothMapConvoContactElement newElement = new BluetoothMapConvoContactElement();
66         newElement.mUci = address;
67         // TODO: For now we use the ID as BT-UID
68         newElement.mBtUid = new SignedLongLong(contact.getId(),0);
69         newElement.mDisplayName = contact.getName();
70         return newElement;
71     }
72 
BluetoothMapConvoContactElement(String uci, String name, String displayName, String presenceStatus, int presenceAvailability, long lastActivity, int chatState, int priority, String btUid)73     public BluetoothMapConvoContactElement(String uci, String name, String displayName,
74             String presenceStatus, int presenceAvailability, long lastActivity, int chatState,
75             int priority, String btUid) {
76         this.mUci = uci;
77         this.mName = name;
78         this.mDisplayName = displayName;
79         this.mPresenceStatus = presenceStatus;
80         this.mPresenceAvailability = presenceAvailability;
81         this.mLastActivity = lastActivity;
82         this.mChatState = chatState;
83         this.mPresenceStatus = presenceStatus;
84         this.mPriority = priority;
85         if(btUid != null) {
86             try {
87                 this.mBtUid = SignedLongLong.fromString(btUid);
88             } catch (UnsupportedEncodingException e) {
89                 Log.w(TAG,e);
90             }
91         }
92     }
93 
BluetoothMapConvoContactElement()94     public BluetoothMapConvoContactElement() {
95         // TODO Auto-generated constructor stub
96     }
97 
getPresenceStatus()98     public String getPresenceStatus() {
99         return mPresenceStatus;
100     }
101 
getDisplayName()102     public String getDisplayName() {
103         return mDisplayName;
104     }
105 
setDisplayName(String displayName)106     public void setDisplayName(String displayName) {
107         this.mDisplayName = displayName;
108     }
109 
setPresenceStatus(String presenceStatus)110     public void setPresenceStatus(String presenceStatus) {
111         this.mPresenceStatus = presenceStatus;
112     }
113 
getPresenceAvailability()114     public int getPresenceAvailability() {
115         return mPresenceAvailability;
116     }
117 
setPresenceAvailability(int presenceAvailability)118     public void setPresenceAvailability(int presenceAvailability) {
119         this.mPresenceAvailability = presenceAvailability;
120     }
121 
getPriority()122     public int getPriority() {
123         return mPriority;
124     }
125 
setPriority(int priority)126     public void setPriority(int priority) {
127         this.mPriority = priority;
128     }
129 
getName()130     public String getName() {
131         return mName;
132     }
133 
setName(String name)134     public void setName(String name) {
135         this.mName = name;
136     }
137 
getBtUid()138     public String getBtUid() {
139         return mBtUid.toHexString();
140     }
141 
setBtUid(SignedLongLong btUid)142     public void setBtUid(SignedLongLong btUid) {
143         this.mBtUid = btUid;
144     }
145 
getChatState()146     public int getChatState() {
147         return mChatState;
148     }
149 
setChatState(int chatState)150     public void setChatState(int chatState) {
151         this.mChatState = chatState;
152     }
153 
setChatState(String chatState)154     public void setChatState(String chatState) {
155         this.mChatState = Integer.valueOf(chatState);
156     }
157 
158 
getLastActivityString()159     public String getLastActivityString() {
160         SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
161         Date date = new Date(mLastActivity);
162         return format.format(date); // Format to YYYYMMDDTHHMMSS local time
163     }
164 
setLastActivity(long dateTime)165     public void setLastActivity(long dateTime) {
166         this.mLastActivity = dateTime;
167     }
168 
setLastActivity(String lastActivity)169     public void setLastActivity(String lastActivity) throws ParseException {
170         SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
171         Date date = format.parse(lastActivity);
172         this.mLastActivity = date.getTime();
173     }
174 
setContactId(String uci)175     public void setContactId(String uci) {
176         this.mUci = uci;
177     }
178 
getContactId()179     public String getContactId(){
180         return mUci;
181     }
182 
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     {
199             // construct the XML tag for a single contact in the convolisting element.
200             xmlConvoElement.startTag(null, XML_TAG_CONVOCONTACT);
201             if(mUci != null) {
202                 xmlConvoElement.attribute(null, XML_ATT_UCI, mUci);
203             }
204             if(mDisplayName != null) {
205                 xmlConvoElement.attribute(null, XML_ATT_DISPLAY_NAME,
206                             BluetoothMapUtils.stripInvalidChars(mDisplayName));
207             }
208             if(mName != null) {
209                 xmlConvoElement.attribute(null, XML_ATT_NAME,
210                         BluetoothMapUtils.stripInvalidChars(mName));
211             }
212             if(mChatState != -1) {
213                 xmlConvoElement.attribute(null, XML_ATT_CHAT_STATE, String.valueOf(mChatState));
214             }
215             if(mLastActivity != -1) {
216                 xmlConvoElement.attribute(null, XML_ATT_LAST_ACTIVITY,
217                         this.getLastActivityString());
218             }
219             if(mBtUid != null) {
220                 xmlConvoElement.attribute(null, XML_ATT_X_BT_UID, mBtUid.toHexString());
221             }
222             if(mPresenceAvailability != -1) {
223                 xmlConvoElement.attribute(null,  XML_ATT_PRESENCE_AVAILABILITY,
224                         String.valueOf(mPresenceAvailability));
225             }
226             if(mPresenceStatus != null) {
227                 xmlConvoElement.attribute(null,  XML_ATT_PRESENCE_STATUS, mPresenceStatus);
228             }
229             if(mPriority != -1) {
230                 xmlConvoElement.attribute(null, XML_ATT_PRIORITY, String.valueOf(mPriority));
231             }
232 
233             xmlConvoElement.endTag(null, XML_TAG_CONVOCONTACT);
234     }
235 
236 
237     /**
238      * Call this function to create a BluetoothMapConvoContactElement. Will consume the end-tag.
239      * @param parser must point into XML_TAG_CONVERSATION tag, hence attributes can be read.
240      * @return
241      * @throws IOException
242      * @throws XmlPullParserException
243      */
createFromXml(XmlPullParser parser)244     public static BluetoothMapConvoContactElement createFromXml(XmlPullParser parser)
245             throws ParseException, XmlPullParserException, IOException {
246         int count = parser.getAttributeCount();
247         BluetoothMapConvoContactElement newElement;
248         if(count<1) {
249             throw new IllegalArgumentException(XML_TAG_CONVOCONTACT +
250                     " is not decorated with attributes");
251         }
252         newElement = new BluetoothMapConvoContactElement();
253         for (int i = 0; i<count; i++) {
254             String attributeName = parser.getAttributeName(i).trim();
255             String attributeValue = parser.getAttributeValue(i);
256             if(attributeName.equalsIgnoreCase(XML_ATT_UCI)) {
257                 newElement.mUci = attributeValue;
258             } else if(attributeName.equalsIgnoreCase(XML_ATT_NAME)) {
259                 newElement.mName = attributeValue;
260             } else if(attributeName.equalsIgnoreCase(XML_ATT_DISPLAY_NAME)) {
261                 newElement.mDisplayName = attributeValue;
262             } else if(attributeName.equalsIgnoreCase(XML_ATT_CHAT_STATE)) {
263                 newElement.setChatState(attributeValue);
264             } else if(attributeName.equalsIgnoreCase(XML_ATT_LAST_ACTIVITY)) {
265                 newElement.setLastActivity(attributeValue);
266             } else if(attributeName.equalsIgnoreCase(XML_ATT_X_BT_UID)) {
267                 newElement.setBtUid(SignedLongLong.fromString(attributeValue));
268             } else if(attributeName.equalsIgnoreCase(XML_ATT_PRESENCE_AVAILABILITY)) {
269                 newElement.mPresenceAvailability = Integer.parseInt(attributeValue);
270             } else if(attributeName.equalsIgnoreCase(XML_ATT_PRESENCE_STATUS)) {
271                 newElement.setPresenceStatus(attributeValue);
272             } else if(attributeName.equalsIgnoreCase(XML_ATT_PRIORITY)) {
273                 newElement.setPriority(Integer.parseInt(attributeValue));
274             } else {
275                 if(D) Log.i(TAG,"Unknown XML attribute: " + parser.getAttributeName(i));
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