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*/
15package com.android.bluetooth.map;
16
17import java.io.IOException;
18import java.io.InputStream;
19import java.io.StringWriter;
20import java.io.UnsupportedEncodingException;
21import java.text.ParseException;
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.List;
25
26import org.xmlpull.v1.XmlPullParser;
27import org.xmlpull.v1.XmlPullParserException;
28import org.xmlpull.v1.XmlSerializer;
29
30import android.util.Log;
31import android.util.Xml;
32
33import com.android.internal.util.FastXmlSerializer;
34import com.android.internal.util.XmlUtils;
35
36public 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
44    public BluetoothMapConvoListing(){
45     mList = new ArrayList<BluetoothMapConvoListingElement>();
46    }
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     */
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     */
72    public boolean hasUnread()
73    {
74        return hasUnread;
75    }
76
77
78    /**
79     *  returns the entire list as a list
80     * @return list
81     */
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     */
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
120    public void sort() {
121        Collections.sort(mList);
122    }
123
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
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     */
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
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