15a60e47497f21f64e6d79420dc4c56c1907df22akschulz/* 25a60e47497f21f64e6d79420dc4c56c1907df22akschulz* Copyright (C) 2015 Samsung System LSI 35a60e47497f21f64e6d79420dc4c56c1907df22akschulz* Licensed under the Apache License, Version 2.0 (the "License"); 45a60e47497f21f64e6d79420dc4c56c1907df22akschulz* you may not use this file except in compliance with the License. 55a60e47497f21f64e6d79420dc4c56c1907df22akschulz* You may obtain a copy of the License at 65a60e47497f21f64e6d79420dc4c56c1907df22akschulz* 75a60e47497f21f64e6d79420dc4c56c1907df22akschulz* http://www.apache.org/licenses/LICENSE-2.0 85a60e47497f21f64e6d79420dc4c56c1907df22akschulz* 95a60e47497f21f64e6d79420dc4c56c1907df22akschulz* Unless required by applicable law or agreed to in writing, software 105a60e47497f21f64e6d79420dc4c56c1907df22akschulz* distributed under the License is distributed on an "AS IS" BASIS, 115a60e47497f21f64e6d79420dc4c56c1907df22akschulz* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 125a60e47497f21f64e6d79420dc4c56c1907df22akschulz* See the License for the specific language governing permissions and 135a60e47497f21f64e6d79420dc4c56c1907df22akschulz* limitations under the License. 145a60e47497f21f64e6d79420dc4c56c1907df22akschulz*/ 155a60e47497f21f64e6d79420dc4c56c1907df22akschulzpackage com.android.bluetooth.map; 165a60e47497f21f64e6d79420dc4c56c1907df22akschulz 175a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.io.IOException; 185a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.io.InputStream; 195a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.io.StringWriter; 205a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.io.UnsupportedEncodingException; 215a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.text.ParseException; 225a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.ArrayList; 235a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.Collections; 245a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.List; 255a60e47497f21f64e6d79420dc4c56c1907df22akschulz 265a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport org.xmlpull.v1.XmlPullParser; 275a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport org.xmlpull.v1.XmlPullParserException; 285a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport org.xmlpull.v1.XmlSerializer; 295a60e47497f21f64e6d79420dc4c56c1907df22akschulz 305a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.util.Log; 315a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.util.Xml; 325a60e47497f21f64e6d79420dc4c56c1907df22akschulz 335a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport com.android.internal.util.FastXmlSerializer; 345a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport com.android.internal.util.XmlUtils; 355a60e47497f21f64e6d79420dc4c56c1907df22akschulz 365a60e47497f21f64e6d79420dc4c56c1907df22akschulzpublic class BluetoothMapConvoListing { 375a60e47497f21f64e6d79420dc4c56c1907df22akschulz private boolean hasUnread = false; 385a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final String TAG = "BluetoothMapConvoListing"; 395a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final boolean D = BluetoothMapService.DEBUG; 405a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final String XML_TAG = "MAP-convo-listing"; 415a60e47497f21f64e6d79420dc4c56c1907df22akschulz 425a60e47497f21f64e6d79420dc4c56c1907df22akschulz private List<BluetoothMapConvoListingElement> mList; 435a60e47497f21f64e6d79420dc4c56c1907df22akschulz 445a60e47497f21f64e6d79420dc4c56c1907df22akschulz public BluetoothMapConvoListing(){ 455a60e47497f21f64e6d79420dc4c56c1907df22akschulz mList = new ArrayList<BluetoothMapConvoListingElement>(); 465a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 475a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void add(BluetoothMapConvoListingElement element) { 485a60e47497f21f64e6d79420dc4c56c1907df22akschulz mList.add(element); 495a60e47497f21f64e6d79420dc4c56c1907df22akschulz /* update info regarding whether the list contains unread conversations */ 505a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (element.getReadBool()) 515a60e47497f21f64e6d79420dc4c56c1907df22akschulz { 525a60e47497f21f64e6d79420dc4c56c1907df22akschulz hasUnread = true; 535a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 545a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 555a60e47497f21f64e6d79420dc4c56c1907df22akschulz 565a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 575a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Used to fetch the number of BluetoothMapConvoListingElement elements in the list. 585a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return the number of elements in the list. 595a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 605a60e47497f21f64e6d79420dc4c56c1907df22akschulz public int getCount() { 615a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mList != null) 625a60e47497f21f64e6d79420dc4c56c1907df22akschulz { 635a60e47497f21f64e6d79420dc4c56c1907df22akschulz return mList.size(); 645a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 655a60e47497f21f64e6d79420dc4c56c1907df22akschulz return 0; 665a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 675a60e47497f21f64e6d79420dc4c56c1907df22akschulz 685a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 695a60e47497f21f64e6d79420dc4c56c1907df22akschulz * does the list contain any unread messages 705a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return true if unread messages have been added to the list, else false 715a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 725a60e47497f21f64e6d79420dc4c56c1907df22akschulz public boolean hasUnread() 735a60e47497f21f64e6d79420dc4c56c1907df22akschulz { 745a60e47497f21f64e6d79420dc4c56c1907df22akschulz return hasUnread; 755a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 765a60e47497f21f64e6d79420dc4c56c1907df22akschulz 775a60e47497f21f64e6d79420dc4c56c1907df22akschulz 785a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 795a60e47497f21f64e6d79420dc4c56c1907df22akschulz * returns the entire list as a list 805a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return list 815a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 825a60e47497f21f64e6d79420dc4c56c1907df22akschulz public List<BluetoothMapConvoListingElement> getList(){ 835a60e47497f21f64e6d79420dc4c56c1907df22akschulz return mList; 845a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 855a60e47497f21f64e6d79420dc4c56c1907df22akschulz 865a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 875a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Encode the list of BluetoothMapMessageListingElement(s) into a UTF-8 885a60e47497f21f64e6d79420dc4c56c1907df22akschulz * formatted XML-string in a trimmed byte array 895a60e47497f21f64e6d79420dc4c56c1907df22akschulz * 905a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return a reference to the encoded byte array. 915a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @throws UnsupportedEncodingException 925a60e47497f21f64e6d79420dc4c56c1907df22akschulz * if UTF-8 encoding is unsupported on the platform. 935a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 945a60e47497f21f64e6d79420dc4c56c1907df22akschulz public byte[] encode() throws UnsupportedEncodingException { 955a60e47497f21f64e6d79420dc4c56c1907df22akschulz StringWriter sw = new StringWriter(); 965a60e47497f21f64e6d79420dc4c56c1907df22akschulz XmlSerializer xmlConvoElement = new FastXmlSerializer(); 975a60e47497f21f64e6d79420dc4c56c1907df22akschulz try { 985a60e47497f21f64e6d79420dc4c56c1907df22akschulz xmlConvoElement.setOutput(sw); 995a60e47497f21f64e6d79420dc4c56c1907df22akschulz xmlConvoElement.startDocument("UTF-8", true); 1005a60e47497f21f64e6d79420dc4c56c1907df22akschulz xmlConvoElement.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", 1015a60e47497f21f64e6d79420dc4c56c1907df22akschulz true); 1025a60e47497f21f64e6d79420dc4c56c1907df22akschulz xmlConvoElement.startTag(null, XML_TAG); 1035a60e47497f21f64e6d79420dc4c56c1907df22akschulz xmlConvoElement.attribute(null, "version", "1.0"); 1045a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Do the XML encoding of list 1055a60e47497f21f64e6d79420dc4c56c1907df22akschulz for (BluetoothMapConvoListingElement element : mList) { 1065a60e47497f21f64e6d79420dc4c56c1907df22akschulz element.encode(xmlConvoElement); // Append the list element 1075a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1085a60e47497f21f64e6d79420dc4c56c1907df22akschulz xmlConvoElement.endTag(null, XML_TAG); 1095a60e47497f21f64e6d79420dc4c56c1907df22akschulz xmlConvoElement.endDocument(); 1105a60e47497f21f64e6d79420dc4c56c1907df22akschulz } catch (IllegalArgumentException e) { 1115a60e47497f21f64e6d79420dc4c56c1907df22akschulz Log.w(TAG, e); 1125a60e47497f21f64e6d79420dc4c56c1907df22akschulz } catch (IllegalStateException e) { 1135a60e47497f21f64e6d79420dc4c56c1907df22akschulz Log.w(TAG, e); 1145a60e47497f21f64e6d79420dc4c56c1907df22akschulz } catch (IOException e) { 1155a60e47497f21f64e6d79420dc4c56c1907df22akschulz Log.w(TAG, e); 1165a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1175a60e47497f21f64e6d79420dc4c56c1907df22akschulz return sw.toString().getBytes("UTF-8"); 1185a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1195a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1205a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void sort() { 1215a60e47497f21f64e6d79420dc4c56c1907df22akschulz Collections.sort(mList); 1225a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1235a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1245a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void segment(int count, int offset) { 1255a60e47497f21f64e6d79420dc4c56c1907df22akschulz count = Math.min(count, mList.size() - offset); 1265a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (count > 0) { 1275a60e47497f21f64e6d79420dc4c56c1907df22akschulz mList = mList.subList(offset, offset + count); 1285a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mList == null) { 1295a60e47497f21f64e6d79420dc4c56c1907df22akschulz mList = new ArrayList<BluetoothMapConvoListingElement>(); // Return an empty list 1305a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1315a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 1325a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(offset > mList.size()) { 1335a60e47497f21f64e6d79420dc4c56c1907df22akschulz mList = new ArrayList<BluetoothMapConvoListingElement>(); 1345a60e47497f21f64e6d79420dc4c56c1907df22akschulz Log.d(TAG, "offset greater than list size. Returning empty list"); 1355a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 1365a60e47497f21f64e6d79420dc4c56c1907df22akschulz mList = mList.subList(offset, mList.size()); 1375a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1385a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1395a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1405a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1415a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void appendFromXml(InputStream xmlDocument) 1425a60e47497f21f64e6d79420dc4c56c1907df22akschulz throws XmlPullParserException, IOException, ParseException { 1435a60e47497f21f64e6d79420dc4c56c1907df22akschulz try { 1445a60e47497f21f64e6d79420dc4c56c1907df22akschulz XmlPullParser parser = Xml.newPullParser(); 1455a60e47497f21f64e6d79420dc4c56c1907df22akschulz int type; 1465a60e47497f21f64e6d79420dc4c56c1907df22akschulz parser.setInput(xmlDocument, "UTF-8"); 1475a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1485a60e47497f21f64e6d79420dc4c56c1907df22akschulz // First find the folder-listing 1495a60e47497f21f64e6d79420dc4c56c1907df22akschulz while((type=parser.next()) != XmlPullParser.END_TAG 1505a60e47497f21f64e6d79420dc4c56c1907df22akschulz && type != XmlPullParser.END_DOCUMENT ) { 1515a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Skip until we get a start tag 1525a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (parser.getEventType() != XmlPullParser.START_TAG) { 1535a60e47497f21f64e6d79420dc4c56c1907df22akschulz continue; 1545a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1555a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Skip until we get a folder-listing tag 1565a60e47497f21f64e6d79420dc4c56c1907df22akschulz String name = parser.getName(); 1575a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(!name.equalsIgnoreCase(XML_TAG)) { 1585a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D) Log.i(TAG,"Unknown XML tag: " + name); 1595a60e47497f21f64e6d79420dc4c56c1907df22akschulz XmlUtils.skipCurrentTag(parser); 1605a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1615a60e47497f21f64e6d79420dc4c56c1907df22akschulz readConversations(parser); 1625a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1635a60e47497f21f64e6d79420dc4c56c1907df22akschulz } finally { 1645a60e47497f21f64e6d79420dc4c56c1907df22akschulz xmlDocument.close(); 1655a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1665a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1675a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1685a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 1695a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Parses folder elements, and add to mSubFolders. 1705a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param parser the Xml Parser currently pointing to an folder-listing tag. 1715a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @throws XmlPullParserException 1725a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @throws IOException 1735a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @throws 1745a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 1755a60e47497f21f64e6d79420dc4c56c1907df22akschulz private void readConversations(XmlPullParser parser) 1765a60e47497f21f64e6d79420dc4c56c1907df22akschulz throws XmlPullParserException, IOException, ParseException { 1775a60e47497f21f64e6d79420dc4c56c1907df22akschulz int type; 1785a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D) Log.i(TAG,"readConversations(): "); 1795a60e47497f21f64e6d79420dc4c56c1907df22akschulz while((type=parser.next()) != XmlPullParser.END_TAG 1805a60e47497f21f64e6d79420dc4c56c1907df22akschulz && type != XmlPullParser.END_DOCUMENT ) { 1815a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Skip until we get a start tag 1825a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (parser.getEventType() != XmlPullParser.START_TAG) { 1835a60e47497f21f64e6d79420dc4c56c1907df22akschulz continue; 1845a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1855a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Skip until we get a folder-listing tag 1865a60e47497f21f64e6d79420dc4c56c1907df22akschulz String name = parser.getName(); 1875a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(name.trim().equalsIgnoreCase(BluetoothMapConvoListingElement.XML_TAG_CONVERSATION) 1885a60e47497f21f64e6d79420dc4c56c1907df22akschulz == false) { 1895a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D) Log.i(TAG,"Unknown XML tag: " + name); 1905a60e47497f21f64e6d79420dc4c56c1907df22akschulz XmlUtils.skipCurrentTag(parser); 1915a60e47497f21f64e6d79420dc4c56c1907df22akschulz continue; 1925a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1935a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Add a single conversation 1945a60e47497f21f64e6d79420dc4c56c1907df22akschulz add(BluetoothMapConvoListingElement.createFromXml(parser)); 1955a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1965a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1975a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1985a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1995a60e47497f21f64e6d79420dc4c56c1907df22akschulz @Override 2005a60e47497f21f64e6d79420dc4c56c1907df22akschulz public boolean equals(Object obj) { 2015a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (this == obj) { 2025a60e47497f21f64e6d79420dc4c56c1907df22akschulz return true; 2035a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2045a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (obj == null) { 2055a60e47497f21f64e6d79420dc4c56c1907df22akschulz return false; 2065a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2075a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (getClass() != obj.getClass()) { 2085a60e47497f21f64e6d79420dc4c56c1907df22akschulz return false; 2095a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2105a60e47497f21f64e6d79420dc4c56c1907df22akschulz BluetoothMapConvoListing other = (BluetoothMapConvoListing) obj; 2115a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (hasUnread != other.hasUnread) { 2125a60e47497f21f64e6d79420dc4c56c1907df22akschulz return false; 2135a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2145a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (mList == null) { 2155a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (other.mList != null) { 2165a60e47497f21f64e6d79420dc4c56c1907df22akschulz return false; 2175a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2185a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if (!mList.equals(other.mList)) { 2195a60e47497f21f64e6d79420dc4c56c1907df22akschulz return false; 2205a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2215a60e47497f21f64e6d79420dc4c56c1907df22akschulz return true; 2225a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2235a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2245a60e47497f21f64e6d79420dc4c56c1907df22akschulz} 225