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