1bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank/* Copyright (C) 2011 The Android Open Source Project. 2bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * 3bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 4bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * you may not use this file except in compliance with the License. 5bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * You may obtain a copy of the License at 6bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * 7bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * http://www.apache.org/licenses/LICENSE-2.0 8bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * 9bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * Unless required by applicable law or agreed to in writing, software 10bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * distributed under the License is distributed on an "AS IS" BASIS, 11bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * See the License for the specific language governing permissions and 13bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * limitations under the License. 14bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank */ 15bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank 16bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blankpackage com.android.exchange.adapter; 17bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank 1802d8984e7452e6c3a3aaec189aca885c3f206a32Anthony Leeimport com.android.exchange.eas.EasLoadAttachment.ProgressCallback; 19da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu 20bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blankimport java.io.IOException; 21bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blankimport java.io.InputStream; 22bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blankimport java.io.OutputStream; 23bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank 24bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank/** 25bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank * Parse the result of an ItemOperations command; we use this to load attachments in EAS 14.0 26bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank */ 27bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blankpublic class ItemOperationsParser extends Parser { 28c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu private static final int CHUNK_SIZE = 16*1024; 29c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu 30bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank private int mStatusCode = 0; 31bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank private final OutputStream mAttachmentOutputStream; 32c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu private final long mAttachmentSize; 33da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu private final ProgressCallback mCallback; 34bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank 35da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu public ItemOperationsParser(final InputStream in, final OutputStream out, final long size, 36da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu final ProgressCallback callback) throws IOException { 37bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank super(in); 38bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank mAttachmentOutputStream = out; 39bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank mAttachmentSize = size; 40da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu mCallback = callback; 41bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 42bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank 43bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank public int getStatusCode() { 44bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank return mStatusCode; 45bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 46bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank 47bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank private void parseProperties() throws IOException { 48bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank while (nextTag(Tags.ITEMS_PROPERTIES) != END) { 49bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank if (tag == Tags.ITEMS_DATA) { 50bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank // Wrap the input stream in our custom base64 input stream 51bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank Base64InputStream bis = new Base64InputStream(getInput()); 52bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank // Read the attachment 53da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu readChunked(bis, mAttachmentOutputStream, mAttachmentSize, mCallback); 54bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } else { 55bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank skipTag(); 56bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 57bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 58bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 59bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank 60bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank private void parseFetch() throws IOException { 61bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank while (nextTag(Tags.ITEMS_FETCH) != END) { 62bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank if (tag == Tags.ITEMS_PROPERTIES) { 63bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank parseProperties(); 64bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } else { 65bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank skipTag(); 66bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 67bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 68bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 69bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank 70bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank private void parseResponse() throws IOException { 71bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank while (nextTag(Tags.ITEMS_RESPONSE) != END) { 72bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank if (tag == Tags.ITEMS_FETCH) { 73bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank parseFetch(); 74bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } else { 75bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank skipTag(); 76bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 77bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 78bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 79bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank 80bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank @Override 81bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank public boolean parse() throws IOException { 82bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank boolean res = false; 83bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank if (nextTag(START_DOCUMENT) != Tags.ITEMS_ITEMS) { 84bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank throw new IOException(); 85bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 86bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank while (nextTag(START_DOCUMENT) != END_DOCUMENT) { 87bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank if (tag == Tags.ITEMS_STATUS) { 88bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank // Save the status code 89bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank mStatusCode = getValueInt(); 90bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } else if (tag == Tags.ITEMS_RESPONSE) { 91bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank parseResponse(); 92bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } else { 93bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank skipTag(); 94bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 95bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 96bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank return res; 97bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 98c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu 99c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu /** 100c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu * Read the attachment data in chunks and write the data back out to our attachment file 101c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu * @param inputStream the InputStream we're reading the attachment from 102c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu * @param outputStream the OutputStream the attachment will be written to 103c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu * @param length the number of expected bytes we're going to read 104da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu * @param callback A {@link ProgressCallback} to use to send progress updates to the UI. 105c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu * @throws IOException 106c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu */ 107c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu public static void readChunked(final InputStream inputStream, final OutputStream outputStream, 108da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu final long length, final ProgressCallback callback) throws IOException { 109c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu final byte[] bytes = new byte[CHUNK_SIZE]; 110c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu // Loop terminates 1) when EOF is reached or 2) IOException occurs 111c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu // One of these is guaranteed to occur 112c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu int totalRead = 0; 113c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu long lastCallbackPct = -1; 114c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu int lastCallbackTotalRead = 0; 115c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu while (true) { 116c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu final int read = inputStream.read(bytes, 0, CHUNK_SIZE); 117c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu if (read < 0) { 118c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu // -1 means EOF 119c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu break; 120c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu } 121c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu 122c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu // Keep track of how much we've read for progress callback 123c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu totalRead += read; 124c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu // Write these bytes out 125c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu outputStream.write(bytes, 0, read); 126c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu 127c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu // We can't report percentage if data is chunked; the length of incoming data is unknown 128c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu if (length > 0) { 129da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu final int pct = (int)((totalRead * 100) / length); 130c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu // Callback only if we've read at least 1% more and have read more than CHUNK_SIZE 131c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu // We don't want to spam the Email app 132c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu if ((pct > lastCallbackPct) && (totalRead > (lastCallbackTotalRead + CHUNK_SIZE))) { 133c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu // Report progress back to the UI 134da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu callback.doCallback(pct); 135da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu 136c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu // TODO: Fix this. 137c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu //doProgressCallback(pct); 138c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu lastCallbackTotalRead = totalRead; 139c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu lastCallbackPct = pct; 140c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu } 141c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu } 142c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu } 143c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu } 144bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank} 145