• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (C) 2011 The Android Open Source Project.
2  *
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 
16 package com.android.exchange.adapter;
17 
18 import com.android.exchange.eas.EasLoadAttachment.ProgressCallback;
19 
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 
24 /**
25  * Parse the result of an ItemOperations command; we use this to load attachments in EAS 14.0
26  */
27 public class ItemOperationsParser extends Parser {
28     private static final int CHUNK_SIZE = 16*1024;
29 
30     private int mStatusCode = 0;
31     private final OutputStream mAttachmentOutputStream;
32     private final long mAttachmentSize;
33     private final ProgressCallback mCallback;
34 
ItemOperationsParser(final InputStream in, final OutputStream out, final long size, final ProgressCallback callback)35     public ItemOperationsParser(final InputStream in, final OutputStream out, final long size,
36             final ProgressCallback callback) throws IOException {
37         super(in);
38         mAttachmentOutputStream = out;
39         mAttachmentSize = size;
40         mCallback = callback;
41     }
42 
getStatusCode()43     public int getStatusCode() {
44         return mStatusCode;
45     }
46 
parseProperties()47     private void parseProperties() throws IOException {
48         while (nextTag(Tags.ITEMS_PROPERTIES) != END) {
49             if (tag == Tags.ITEMS_DATA) {
50                 // Wrap the input stream in our custom base64 input stream
51                 Base64InputStream bis = new Base64InputStream(getInput());
52                 // Read the attachment
53                 readChunked(bis, mAttachmentOutputStream, mAttachmentSize, mCallback);
54             } else {
55                 skipTag();
56             }
57         }
58     }
59 
parseFetch()60     private void parseFetch() throws IOException {
61         while (nextTag(Tags.ITEMS_FETCH) != END) {
62             if (tag == Tags.ITEMS_PROPERTIES) {
63                 parseProperties();
64             } else {
65                 skipTag();
66             }
67         }
68     }
69 
parseResponse()70     private void parseResponse() throws IOException {
71         while (nextTag(Tags.ITEMS_RESPONSE) != END) {
72             if (tag == Tags.ITEMS_FETCH) {
73                 parseFetch();
74             } else {
75                 skipTag();
76             }
77         }
78     }
79 
80     @Override
parse()81     public boolean parse() throws IOException {
82         boolean res = false;
83         if (nextTag(START_DOCUMENT) != Tags.ITEMS_ITEMS) {
84             throw new IOException();
85         }
86         while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
87             if (tag == Tags.ITEMS_STATUS) {
88                 // Save the status code
89                 mStatusCode = getValueInt();
90             } else if (tag == Tags.ITEMS_RESPONSE) {
91                 parseResponse();
92             } else {
93                 skipTag();
94             }
95         }
96         return res;
97     }
98 
99     /**
100      * Read the attachment data in chunks and write the data back out to our attachment file
101      * @param inputStream the InputStream we're reading the attachment from
102      * @param outputStream the OutputStream the attachment will be written to
103      * @param length the number of expected bytes we're going to read
104      * @param callback A {@link ProgressCallback} to use to send progress updates to the UI.
105      * @throws IOException
106      */
readChunked(final InputStream inputStream, final OutputStream outputStream, final long length, final ProgressCallback callback)107     public static void readChunked(final InputStream inputStream, final OutputStream outputStream,
108             final long length, final ProgressCallback callback) throws IOException {
109         final byte[] bytes = new byte[CHUNK_SIZE];
110         // Loop terminates 1) when EOF is reached or 2) IOException occurs
111         // One of these is guaranteed to occur
112         int totalRead = 0;
113         long lastCallbackPct = -1;
114         int lastCallbackTotalRead = 0;
115         while (true) {
116             final int read = inputStream.read(bytes, 0, CHUNK_SIZE);
117             if (read < 0) {
118                 // -1 means EOF
119                 break;
120             }
121 
122             // Keep track of how much we've read for progress callback
123             totalRead += read;
124             // Write these bytes out
125             outputStream.write(bytes, 0, read);
126 
127             // We can't report percentage if data is chunked; the length of incoming data is unknown
128             if (length > 0) {
129                 final int pct = (int)((totalRead * 100) / length);
130                 // Callback only if we've read at least 1% more and have read more than CHUNK_SIZE
131                 // We don't want to spam the Email app
132                 if ((pct > lastCallbackPct) && (totalRead > (lastCallbackTotalRead + CHUNK_SIZE))) {
133                     // Report progress back to the UI
134                     callback.doCallback(pct);
135 
136                     // TODO: Fix this.
137                     //doProgressCallback(pct);
138                     lastCallbackTotalRead = totalRead;
139                     lastCallbackPct = pct;
140                 }
141             }
142         }
143     }
144 }
145