1 /*
2 * Copyright (C) 2014 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 
18 import android.util.Log;
19 import org.xmlpull.v1.XmlSerializer;
20 
21 import com.android.internal.util.FastXmlSerializer;
22 
23 import java.io.IOException;
24 import java.io.StringWriter;
25 import java.io.UnsupportedEncodingException;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.Locale;
30 import java.util.Map.Entry;
31 
32 
33 /**
34  * Class to contain a single folder element representation.
35  *
36  */
37 public class BluetoothMapFolderElement {
38     private String mName;
39     private BluetoothMapFolderElement mParent = null;
40     private boolean mHasSmsMmsContent = false;
41     private long mEmailFolderId = -1;
42     private HashMap<String, BluetoothMapFolderElement> mSubFolders;
43 
44     private static final boolean D = BluetoothMapService.DEBUG;
45     private static final boolean V = BluetoothMapService.VERBOSE;
46 
47     private final static String TAG = "BluetoothMapFolderElement";
48 
BluetoothMapFolderElement( String name, BluetoothMapFolderElement parrent)49     public BluetoothMapFolderElement( String name, BluetoothMapFolderElement parrent){
50         this.mName = name;
51         this.mParent = parrent;
52         mSubFolders = new HashMap<String, BluetoothMapFolderElement>();
53     }
54 
getName()55     public String getName() {
56         return mName;
57     }
58 
hasSmsMmsContent()59     public boolean hasSmsMmsContent(){
60         return mHasSmsMmsContent;
61     }
62 
getEmailFolderId()63     public long getEmailFolderId(){
64         return mEmailFolderId;
65     }
66 
setEmailFolderId(long emailFolderId)67     public void setEmailFolderId(long emailFolderId) {
68         this.mEmailFolderId = emailFolderId;
69     }
70 
setHasSmsMmsContent(boolean hasSmsMmsContent)71     public void setHasSmsMmsContent(boolean hasSmsMmsContent) {
72         this.mHasSmsMmsContent = hasSmsMmsContent;
73     }
74 
75     /**
76      * Fetch the parent folder.
77      * @return the parent folder or null if we are at the root folder.
78      */
getParent()79     public BluetoothMapFolderElement getParent() {
80         return mParent;
81     }
82 
83     /**
84      * Build the full path to this folder
85      * @return a string representing the full path.
86      */
getFullPath()87     public String getFullPath() {
88         StringBuilder sb = new StringBuilder(mName);
89         BluetoothMapFolderElement current = mParent;
90         while(current != null) {
91             if(current.getParent() != null) {
92                 sb.insert(0, current.mName + "/");
93             }
94             current = current.getParent();
95         }
96         //sb.insert(0, "/"); Should this be included? The MAP spec. do not include it in examples.
97         return sb.toString();
98     }
99 
100 
getEmailFolderByName(String name)101     public BluetoothMapFolderElement getEmailFolderByName(String name) {
102         BluetoothMapFolderElement folderElement = this.getRoot();
103         folderElement = folderElement.getSubFolder("telecom");
104         folderElement = folderElement.getSubFolder("msg");
105         folderElement = folderElement.getSubFolder(name);
106         if (folderElement != null && folderElement.getEmailFolderId() == -1 )
107             folderElement = null;
108         return folderElement;
109     }
110 
getEmailFolderById(long id)111     public BluetoothMapFolderElement getEmailFolderById(long id) {
112         return getEmailFolderById(id, this);
113     }
114 
getEmailFolderById(long id, BluetoothMapFolderElement folderStructure)115     public static BluetoothMapFolderElement getEmailFolderById(long id,
116             BluetoothMapFolderElement folderStructure) {
117         if(folderStructure == null) {
118             return null;
119         }
120         return findEmailFolderById(id, folderStructure.getRoot());
121     }
122 
findEmailFolderById(long id, BluetoothMapFolderElement folder)123     private static BluetoothMapFolderElement findEmailFolderById(long id,
124             BluetoothMapFolderElement folder) {
125         if(folder.getEmailFolderId() == id) {
126             return folder;
127         }
128         /* Else */
129         for(BluetoothMapFolderElement subFolder : folder.mSubFolders.values().toArray(
130                 new BluetoothMapFolderElement[folder.mSubFolders.size()]))
131         {
132             BluetoothMapFolderElement ret = findEmailFolderById(id, subFolder);
133             if(ret != null) {
134                 return ret;
135             }
136         }
137         return null;
138     }
139 
140 
141     /**
142      * Fetch the root folder.
143      * @return the parent folder or null if we are at the root folder.
144      */
getRoot()145     public BluetoothMapFolderElement getRoot() {
146         BluetoothMapFolderElement rootFolder = this;
147         while(rootFolder.getParent() != null)
148             rootFolder = rootFolder.getParent();
149         return rootFolder;
150     }
151 
152     /**
153      * Add a virtual folder.
154      * @param name the name of the folder to add.
155      * @return the added folder element.
156      */
addFolder(String name)157     public BluetoothMapFolderElement addFolder(String name){
158         name = name.toLowerCase(Locale.US);
159         BluetoothMapFolderElement newFolder = mSubFolders.get(name);
160         if(D) Log.i(TAG,"addFolder():" + name);
161         if(newFolder == null) {
162             newFolder = new BluetoothMapFolderElement(name, this);
163             mSubFolders.put(name, newFolder);
164         }
165         return newFolder;
166     }
167 
168     /**
169      * Add a sms/mms folder.
170      * @param name the name of the folder to add.
171      * @return the added folder element.
172      */
addSmsMmsFolder(String name)173     public BluetoothMapFolderElement addSmsMmsFolder(String name){
174         name = name.toLowerCase(Locale.US);
175         BluetoothMapFolderElement newFolder = mSubFolders.get(name);
176         if(D) Log.i(TAG,"addSmsMmsFolder():" + name);
177         if(newFolder == null) {
178             newFolder = new BluetoothMapFolderElement(name, this);
179             mSubFolders.put(name, newFolder);
180         }
181         newFolder.setHasSmsMmsContent(true);
182         return newFolder;
183     }
184 
185     /**
186      * Add an Email folder.
187      * @param name the name of the folder to add.
188      * @return the added folder element.
189      */
addEmailFolder(String name, long emailFolderId)190     public BluetoothMapFolderElement addEmailFolder(String name, long emailFolderId){
191         name = name.toLowerCase();
192         BluetoothMapFolderElement newFolder = mSubFolders.get(name);
193         if(V) Log.v(TAG,"addEmailFolder(): name = " + name
194                  + "id = " + emailFolderId);
195         if(newFolder == null) {
196             newFolder = new BluetoothMapFolderElement(name, this);
197             mSubFolders.put(name, newFolder);
198         }
199         newFolder.setEmailFolderId(emailFolderId);
200         return newFolder;
201     }
202 
203     /**
204      * Fetch the number of sub folders.
205      * @return returns the number of sub folders.
206      */
getSubFolderCount()207     public int getSubFolderCount(){
208         return mSubFolders.size();
209     }
210 
211     /**
212      * Returns the subFolder element matching the supplied folder name.
213      * @param folderName the name of the subFolder to find.
214      * @return the subFolder element if found {@code null} otherwise.
215      */
getSubFolder(String folderName)216     public BluetoothMapFolderElement getSubFolder(String folderName){
217         return mSubFolders.get(folderName.toLowerCase());
218     }
219 
encode(int offset, int count)220     public byte[] encode(int offset, int count) throws UnsupportedEncodingException {
221         StringWriter sw = new StringWriter();
222         XmlSerializer xmlMsgElement = new FastXmlSerializer();
223         int i, stopIndex;
224         // We need index based access to the subFolders
225         BluetoothMapFolderElement[] folders = mSubFolders.values().toArray(new BluetoothMapFolderElement[mSubFolders.size()]);
226 
227         if(offset > mSubFolders.size())
228             throw new IllegalArgumentException("FolderListingEncode: offset > subFolders.size()");
229 
230         stopIndex = offset + count;
231         if(stopIndex > mSubFolders.size())
232             stopIndex = mSubFolders.size();
233 
234         try {
235             xmlMsgElement.setOutput(sw);
236             xmlMsgElement.startDocument("UTF-8", true);
237             xmlMsgElement.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
238             xmlMsgElement.startTag(null, "folder-listing");
239             xmlMsgElement.attribute(null, "version", "1.0");
240             for(i = offset; i<stopIndex; i++)
241             {
242                 xmlMsgElement.startTag(null, "folder");
243                 xmlMsgElement.attribute(null, "name", folders[i].getName());
244                 xmlMsgElement.endTag(null, "folder");
245             }
246             xmlMsgElement.endTag(null, "folder-listing");
247             xmlMsgElement.endDocument();
248         } catch (IllegalArgumentException e) {
249             if(D) Log.w(TAG,e);
250             throw new IllegalArgumentException("error encoding folderElement");
251         } catch (IllegalStateException e) {
252             if(D) Log.w(TAG,e);
253             throw new IllegalArgumentException("error encoding folderElement");
254         } catch (IOException e) {
255             if(D) Log.w(TAG,e);
256             throw new IllegalArgumentException("error encoding folderElement");
257         }
258         return sw.toString().getBytes("UTF-8");
259     }
260 }
261