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.InputStream;
19 import java.io.StringWriter;
20 import java.io.UnsupportedEncodingException;
21 import java.text.ParseException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 
26 import org.xmlpull.v1.XmlPullParser;
27 import org.xmlpull.v1.XmlPullParserException;
28 import org.xmlpull.v1.XmlSerializer;
29 
30 import android.util.Log;
31 import android.util.Xml;
32 
33 import com.android.internal.util.FastXmlSerializer;
34 import com.android.internal.util.XmlUtils;
35 
36 public class BluetoothMapConvoListing {
37     private boolean hasUnread = false;
38     private static final String TAG = "BluetoothMapConvoListing";
39     private static final boolean D = BluetoothMapService.DEBUG;
40     private static final String XML_TAG = "MAP-convo-listing";
41 
42     private List<BluetoothMapConvoListingElement> mList;
43 
BluetoothMapConvoListing()44     public BluetoothMapConvoListing(){
45      mList = new ArrayList<BluetoothMapConvoListingElement>();
46     }
add(BluetoothMapConvoListingElement element)47     public void add(BluetoothMapConvoListingElement element) {
48         mList.add(element);
49         /* update info regarding whether the list contains unread conversations */
50         if (element.getReadBool())
51         {
52             hasUnread = true;
53         }
54     }
55 
56     /**
57      * Used to fetch the number of BluetoothMapConvoListingElement elements in the list.
58      * @return the number of elements in the list.
59      */
getCount()60     public int getCount() {
61         if(mList != null)
62         {
63             return mList.size();
64         }
65         return 0;
66     }
67 
68     /**
69      * does the list contain any unread messages
70      * @return true if unread messages have been added to the list, else false
71      */
hasUnread()72     public boolean hasUnread()
73     {
74         return hasUnread;
75     }
76 
77 
78     /**
79      *  returns the entire list as a list
80      * @return list
81      */
getList()82     public List<BluetoothMapConvoListingElement> getList(){
83         return mList;
84     }
85 
86     /**
87      * Encode the list of BluetoothMapMessageListingElement(s) into a UTF-8
88      * formatted XML-string in a trimmed byte array
89      *
90      * @return a reference to the encoded byte array.
91      * @throws UnsupportedEncodingException
92      *             if UTF-8 encoding is unsupported on the platform.
93      */
encode()94     public byte[] encode() throws UnsupportedEncodingException {
95         StringWriter sw = new StringWriter();
96         XmlSerializer xmlConvoElement = new FastXmlSerializer();
97         try {
98             xmlConvoElement.setOutput(sw);
99             xmlConvoElement.startDocument("UTF-8", true);
100             xmlConvoElement.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
101                     true);
102             xmlConvoElement.startTag(null, XML_TAG);
103             xmlConvoElement.attribute(null, "version", "1.0");
104             // Do the XML encoding of list
105             for (BluetoothMapConvoListingElement element : mList) {
106                 element.encode(xmlConvoElement); // Append the list element
107             }
108             xmlConvoElement.endTag(null, XML_TAG);
109             xmlConvoElement.endDocument();
110         } catch (IllegalArgumentException e) {
111             Log.w(TAG, e);
112         } catch (IllegalStateException e) {
113             Log.w(TAG, e);
114         } catch (IOException e) {
115             Log.w(TAG, e);
116         }
117         return sw.toString().getBytes("UTF-8");
118     }
119 
sort()120     public void sort() {
121         Collections.sort(mList);
122     }
123 
segment(int count, int offset)124     public void segment(int count, int offset) {
125         count = Math.min(count, mList.size() - offset);
126         if (count > 0) {
127             mList = mList.subList(offset, offset + count);
128             if(mList == null) {
129                 mList = new ArrayList<BluetoothMapConvoListingElement>(); // Return an empty list
130             }
131         } else {
132             if(offset > mList.size()) {
133                mList = new ArrayList<BluetoothMapConvoListingElement>();
134                Log.d(TAG, "offset greater than list size. Returning empty list");
135             } else {
136                mList = mList.subList(offset, mList.size());
137             }
138         }
139     }
140 
appendFromXml(InputStream xmlDocument)141     public void appendFromXml(InputStream xmlDocument)
142             throws XmlPullParserException, IOException, ParseException {
143         try {
144             XmlPullParser parser = Xml.newPullParser();
145             int type;
146             parser.setInput(xmlDocument, "UTF-8");
147 
148             // First find the folder-listing
149             while((type=parser.next()) != XmlPullParser.END_TAG
150                     && type != XmlPullParser.END_DOCUMENT ) {
151                 // Skip until we get a start tag
152                 if (parser.getEventType() != XmlPullParser.START_TAG) {
153                     continue;
154                 }
155                 // Skip until we get a folder-listing tag
156                 String name = parser.getName();
157                 if(!name.equalsIgnoreCase(XML_TAG)) {
158                     if(D) Log.i(TAG,"Unknown XML tag: " + name);
159                     XmlUtils.skipCurrentTag(parser);
160                 }
161                 readConversations(parser);
162             }
163         } finally {
164             xmlDocument.close();
165         }
166     }
167 
168     /**
169      * Parses folder elements, and add to mSubFolders.
170      * @param parser the Xml Parser currently pointing to an folder-listing tag.
171      * @throws XmlPullParserException
172      * @throws IOException
173      * @throws
174      */
readConversations(XmlPullParser parser)175     private void readConversations(XmlPullParser parser)
176             throws XmlPullParserException, IOException, ParseException {
177         int type;
178         if(D) Log.i(TAG,"readConversations(): ");
179         while((type=parser.next()) != XmlPullParser.END_TAG
180                 && type != XmlPullParser.END_DOCUMENT ) {
181             // Skip until we get a start tag
182             if (parser.getEventType() != XmlPullParser.START_TAG) {
183                 continue;
184             }
185             // Skip until we get a folder-listing tag
186             String name = parser.getName();
187             if(name.trim().equalsIgnoreCase(BluetoothMapConvoListingElement.XML_TAG_CONVERSATION)
188                     == false) {
189                 if(D) Log.i(TAG,"Unknown XML tag: " + name);
190                 XmlUtils.skipCurrentTag(parser);
191                 continue;
192             }
193             // Add a single conversation
194             add(BluetoothMapConvoListingElement.createFromXml(parser));
195         }
196     }
197 
198 
199     @Override
equals(Object obj)200     public boolean equals(Object obj) {
201         if (this == obj) {
202             return true;
203         }
204         if (obj == null) {
205             return false;
206         }
207         if (getClass() != obj.getClass()) {
208             return false;
209         }
210         BluetoothMapConvoListing other = (BluetoothMapConvoListing) obj;
211         if (hasUnread != other.hasUnread) {
212             return false;
213         }
214         if (mList == null) {
215             if (other.mList != null) {
216                 return false;
217             }
218         } else if (!mList.equals(other.mList)) {
219             return false;
220         }
221         return true;
222     }
223 
224 }
225