Home | History | Annotate | Download | only in adapter
      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 
     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 
     43     public int getStatusCode() {
     44         return mStatusCode;
     45     }
     46 
     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 
     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 
     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
     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      */
    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