com.radicaldynamic.groupinform.tasks.InstanceUploaderTask.java Source code

Java tutorial

Introduction

Here is the source code for com.radicaldynamic.groupinform.tasks.InstanceUploaderTask.java

Source

/*
 * Copyright (C) 2009 University of Washington
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.radicaldynamic.groupinform.tasks;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.protocol.HttpContext;
import org.ektorp.Attachment;
import org.ektorp.AttachmentInputStream;
import org.ektorp.DbAccessException;
import org.ektorp.DocumentNotFoundException;

import com.radicaldynamic.groupinform.application.Collect;
import com.radicaldynamic.groupinform.documents.FormInstance;
import com.radicaldynamic.groupinform.documents.Generic;
import com.radicaldynamic.groupinform.logic.ODKInstanceAttributes;
import com.radicaldynamic.groupinform.utilities.FileUtilsExtended;

import com.radicaldynamic.groupinform.R;
import org.odk.collect.android.listeners.InstanceUploaderListener;
import org.odk.collect.android.preferences.PreferencesActivity;
import org.odk.collect.android.utilities.FileUtils;
import org.odk.collect.android.utilities.WebUtils;

import android.content.ContentValues;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.util.Log;
import android.webkit.MimeTypeMap;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.Map.Entry;

/**
 * Background task for uploading completed forms.
 * 
 * @author Carl Hartung (carlhartung@gmail.com)
 */
// BEGIN custom
//public class InstanceUploaderTask extends AsyncTask<Long, Integer, HashMap<String, String>> {
public class InstanceUploaderTask extends AsyncTask<String, Integer, HashMap<String, String>> {
    // END custom

    private static String t = "InstanceUploaderTask";
    private InstanceUploaderListener mStateListener;
    private static final int CONNECTION_TIMEOUT = 30000;
    private static final String fail = "Error: ";
    private String mAuth = "";

    private URI mAuthRequestingServer;
    HashMap<String, String> mResults;

    public void setAuth(String auth) {
        this.mAuth = auth;
    }

    // TODO: This method is like 350 lines long, down from 400.
    // still. ridiculous. make it smaller.
    @Override
    // BEGIN custom
    //    protected HashMap<String, String> doInBackground(Long... values) {
    protected HashMap<String, String> doInBackground(String... values) {
        // END custom    
        mResults = new HashMap<String, String>();

        // BEGIN custom
        //        String selection = InstanceColumns._ID + "=?";
        //        String[] selectionArgs = new String[values.length];
        //        for (int i = 0; i < values.length; i++) {
        //            if (i != values.length - 1) {
        //                selection += " or " + InstanceColumns._ID + "=?";
        //            }
        //            selectionArgs[i] = values[i].toString();
        //        }        
        // END custom

        // get shared HttpContext so that authentication and cookies are retained.
        HttpContext localContext = Collect.getInstance().getHttpContext();
        HttpClient httpclient = WebUtils.createHttpClient(CONNECTION_TIMEOUT);

        Map<URI, URI> uriRemap = new HashMap<URI, URI>();

        // BEGIN custom        
        //      Cursor c =
        //      Collect.getInstance().getContentResolver()
        //              .query(InstanceColumns.CONTENT_URI, null, selection, selectionArgs, null);
        //
        //  if (c.getCount() > 0) {
        //      c.moveToPosition(-1);
        //      next_submission: while (c.moveToNext()) {
        //          if (isCancelled()) {
        //              return mResults;
        //          }        
        //          publishProgress(c.getPosition() + 1, c.getCount());
        //          String instance = c.getString(c.getColumnIndex(InstanceColumns.INSTANCE_FILE_PATH));
        //          String id = c.getString(c.getColumnIndex(InstanceColumns._ID));
        //          Uri toUpdate = Uri.withAppendedPath(InstanceColumns.CONTENT_URI, id);
        //
        //          String urlString = c.getString(c.getColumnIndex(InstanceColumns.SUBMISSION_URI));

        next_submission: for (int i = 0; i < values.length; i++) {
            if (isCancelled()) {
                return mResults;
            }

            publishProgress(i + 1, values.length);

            FormInstance instanceDoc = null;
            String id = values[i];

            try {
                instanceDoc = Collect.getInstance().getDbService().getDb().get(FormInstance.class, id);
            } catch (DocumentNotFoundException e) {
                if (Collect.Log.WARN)
                    Log.w(Collect.LOGTAG, t + "unable to retrieve instance: " + e.toString());
                mResults.put(id, fail + "warning: document not found :: details: " + e.getMessage());
                continue;
            } catch (DbAccessException e) {
                if (Collect.Log.WARN)
                    Log.w(Collect.LOGTAG, t + "unable to access database: " + e.toString());
                mResults.put(id, fail + "error: could not acess database :: details: " + e.getMessage());
                continue;
            } catch (Exception e) {
                if (Collect.Log.ERROR)
                    Log.e(Collect.LOGTAG, t + "unexpected exception: " + e.toString());
                e.printStackTrace();
                mResults.put(id, fail + "unexpected error :: details: " + e.getMessage());
                continue;
            }

            String urlString = instanceDoc.getOdk().getUploadUri();
            // END custom

            if (urlString == null) {
                SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(Collect.getInstance());
                urlString = settings.getString(PreferencesActivity.KEY_SERVER_URL,
                        Collect.getInstance().getString(R.string.default_server_url));
                String submissionUrl = settings.getString(PreferencesActivity.KEY_SUBMISSION_URL, "/submission");
                urlString = urlString + submissionUrl;
            }

            @SuppressWarnings("unused")
            ContentValues cv = new ContentValues();
            URI u = null;
            try {
                URL url = new URL(URLDecoder.decode(urlString, "utf-8"));
                u = url.toURI();
            } catch (MalformedURLException e) {
                e.printStackTrace();
                mResults.put(id, fail + "invalid url: " + urlString + " :: details: " + e.getMessage());
                // BEGIN custom
                //                    cv.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                //                    Collect.getInstance().getContentResolver().update(toUpdate, cv, null, null);

                try {
                    instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                    Collect.getInstance().getDbService().getDb().update(instanceDoc);
                } catch (Exception e1) {
                    if (Collect.Log.ERROR)
                        Log.e(Collect.LOGTAG,
                                t + ": could not record upload failed because of MalformedURLException for " + id
                                        + ": " + e1.toString());
                }
                // END custom
                continue;
            } catch (URISyntaxException e) {
                e.printStackTrace();
                mResults.put(id, fail + "invalid uri: " + urlString + " :: details: " + e.getMessage());
                // BEGIN custom
                //                    cv.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                //                    Collect.getInstance().getContentResolver().update(toUpdate, cv, null, null);

                try {
                    instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                    Collect.getInstance().getDbService().getDb().update(instanceDoc);
                } catch (Exception e1) {
                    if (Collect.Log.ERROR)
                        Log.e(Collect.LOGTAG,
                                t + ": could not record upload failed because of URISyntaxException for " + id
                                        + ": " + e1.toString());
                }
                // END custom
                continue;
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                mResults.put(id, fail + "invalid url: " + urlString + " :: details: " + e.getMessage());
                // BEGIN custom
                //                    cv.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                //                    Collect.getInstance().getContentResolver().update(toUpdate, cv, null, null);

                try {
                    instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                    Collect.getInstance().getDbService().getDb().update(instanceDoc);
                } catch (Exception e1) {
                    if (Collect.Log.ERROR)
                        Log.e(Collect.LOGTAG,
                                t + ": could not record upload failed because of UnsupportedEncodingException for "
                                        + id + ": " + e1.toString());
                }
                // END custom
                continue;
            }

            boolean openRosaServer = false;
            if (uriRemap.containsKey(u)) {
                // we already issued a head request and got a response,
                // so we know the proper URL to send the submission to
                // and the proper scheme. We also know that it was an
                // OpenRosa compliant server.
                openRosaServer = true;
                u = uriRemap.get(u);
            } else {
                // we need to issue a head request
                HttpHead httpHead = WebUtils.createOpenRosaHttpHead(u);

                // prepare response
                HttpResponse response = null;
                try {
                    response = httpclient.execute(httpHead, localContext);
                    int statusCode = response.getStatusLine().getStatusCode();
                    if (statusCode == 401) {
                        // we need authentication, so stop and return what we've
                        // done so far.
                        mAuthRequestingServer = u;
                        return null;
                    } else if (statusCode == 204) {
                        Header[] locations = response.getHeaders("Location");
                        if (locations != null && locations.length == 1) {
                            try {
                                URL url = new URL(URLDecoder.decode(locations[0].getValue(), "utf-8"));
                                URI uNew = url.toURI();
                                if (u.getHost().equalsIgnoreCase(uNew.getHost())) {
                                    openRosaServer = true;
                                    // trust the server to tell us a new location
                                    // ... and possibly to use https instead.
                                    uriRemap.put(u, uNew);
                                    u = uNew;
                                } else {
                                    // Don't follow a redirection attempt to a different host.
                                    // We can't tell if this is a spoof or not.
                                    mResults.put(id, fail + "Unexpected redirection attempt to a different host: "
                                            + uNew.toString());
                                    // BEGIN custom
                                    //                                        cv.put(InstanceColumns.STATUS,
                                    //                                            InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                                    //                                        Collect.getInstance().getContentResolver()
                                    //                                                .update(toUpdate, cv, null, null);

                                    try {
                                        instanceDoc.getOdk()
                                                .setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                                        Collect.getInstance().getDbService().getDb().update(instanceDoc);
                                    } catch (Exception e1) {
                                        if (Collect.Log.ERROR)
                                            Log.e(Collect.LOGTAG, t
                                                    + ": could not record upload failed because of redirection error for "
                                                    + id + ": " + e1.toString());
                                    }
                                    // END custom
                                    continue;
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                                mResults.put(id, fail + urlString + " " + e.getMessage());
                                // BEGIN custom
                                //                                    cv.put(InstanceColumns.STATUS,
                                //                                        InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                                //                                    Collect.getInstance().getContentResolver()
                                //                                            .update(toUpdate, cv, null, null);

                                try {
                                    instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                                    Collect.getInstance().getDbService().getDb().update(instanceDoc);
                                } catch (Exception e1) {
                                    if (Collect.Log.ERROR)
                                        Log.e(Collect.LOGTAG, t
                                                + ": could not record upload failed because of unexpected exception for "
                                                + id + ": " + e1.toString());
                                }
                                // END custom
                                continue;
                            }
                        }
                    } else {
                        // may be a server that does not handle
                        try {
                            // have to read the stream in order to reuse the connection
                            InputStream is = response.getEntity().getContent();
                            // read to end of stream...
                            final long count = 1024L;
                            while (is.skip(count) == count)
                                ;
                            is.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                        Log.w(t, "Status code on Head request: " + statusCode);
                        if (statusCode >= 200 && statusCode <= 299) {
                            mResults.put(id, fail
                                    + "Invalid status code on Head request.  If you have a web proxy, you may need to login to your network. ");
                            // BEGIN custom
                            //                                cv.put(InstanceColumns.STATUS,
                            //                                    InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                            //                                Collect.getInstance().getContentResolver()
                            //                                        .update(toUpdate, cv, null, null);

                            try {
                                instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                                Collect.getInstance().getDbService().getDb().update(instanceDoc);
                            } catch (Exception e1) {
                                if (Collect.Log.ERROR)
                                    Log.e(Collect.LOGTAG, t
                                            + ": could not record upload failed because of network login error for "
                                            + id + ": " + e1.toString());
                            }
                            // END custom
                            continue;
                        }
                    }
                } catch (ClientProtocolException e) {
                    e.printStackTrace();
                    Log.e(t, e.getMessage());
                    mResults.put(id, fail + "Client Protocol Exception");

                    // BEGIN custom
                    //                        cv.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                    //                        Collect.getInstance().getContentResolver().update(toUpdate, cv, null, null);

                    try {
                        instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                        Collect.getInstance().getDbService().getDb().update(instanceDoc);
                    } catch (Exception e1) {
                        if (Collect.Log.ERROR)
                            Log.e(Collect.LOGTAG,
                                    t + ": could not record upload failed because of client protocol exception for "
                                            + id + ": " + e1.toString());
                    }
                    // END custom
                    continue;
                } catch (ConnectTimeoutException e) {
                    e.printStackTrace();
                    Log.e(t, e.getMessage());
                    mResults.put(id, fail + "Connection Timeout");
                    // BEGIN custom
                    //                        cv.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                    //                        Collect.getInstance().getContentResolver().update(toUpdate, cv, null, null);

                    try {
                        instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                        Collect.getInstance().getDbService().getDb().update(instanceDoc);
                    } catch (Exception e1) {
                        if (Collect.Log.ERROR)
                            Log.e(Collect.LOGTAG, t
                                    + ": could not record upload failed because of connection timeout exception for "
                                    + id + ": " + e1.toString());
                    }
                    // END custom
                    continue;
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                    mResults.put(id, fail + e.getMessage() + " :: Network Connection Failed");
                    Log.e(t, e.getMessage());
                    // BEGIN custom
                    //                        cv.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                    //                        Collect.getInstance().getContentResolver().update(toUpdate, cv, null, null);

                    try {
                        instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                        Collect.getInstance().getDbService().getDb().update(instanceDoc);
                    } catch (Exception e1) {
                        if (Collect.Log.ERROR)
                            Log.e(Collect.LOGTAG,
                                    t + ": could not record upload failed because of unknown host exception for "
                                            + id + ": " + e1.toString());
                    }
                    // END custom
                    continue;
                } catch (Exception e) {
                    e.printStackTrace();
                    mResults.put(id, fail + "Generic Exception");
                    Log.e(t, e.getMessage());
                    //                        cv.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                    //                        Collect.getInstance().getContentResolver().update(toUpdate, cv, null, null);

                    try {
                        instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                        Collect.getInstance().getDbService().getDb().update(instanceDoc);
                    } catch (Exception e1) {
                        if (Collect.Log.ERROR)
                            Log.e(Collect.LOGTAG, t
                                    + ": could not record upload failed because of (generic) unexpected exception for "
                                    + id + ": " + e1.toString());
                    }
                    // END custom
                    continue;
                }
            }

            // At this point, we may have updated the uri to use https.
            // This occurs only if the Location header keeps the host name
            // the same. If it specifies a different host name, we error
            // out.
            //
            // And we may have set authentication cookies in our
            // cookiestore (referenced by localContext) that will enable
            // authenticated publication to the server.
            //
            // BEGIN custom                
            String uploadFolder = FileUtilsExtended.ODK_UPLOAD_PATH + File.separator + UUID.randomUUID();
            FileUtils.createFolder(uploadFolder);

            try {
                HashMap<String, Attachment> attachments = (HashMap<String, Attachment>) instanceDoc
                        .getAttachments();

                // Download files from database
                for (Entry<String, Attachment> entry : attachments.entrySet()) {
                    String key = entry.getKey();

                    AttachmentInputStream ais = Collect.getInstance().getDbService().getDb().getAttachment(id, key);

                    // ODK code below expects the XML instance to have a .xml extension
                    if (key.equals("xml"))
                        key = id + ".xml";

                    FileOutputStream file = new FileOutputStream(new File(uploadFolder, key));
                    byte[] buffer = new byte[8192];
                    int bytesRead = 0;

                    while ((bytesRead = ais.read(buffer)) != -1) {
                        file.write(buffer, 0, bytesRead);
                    }

                    ais.close();
                    file.close();
                }
            } catch (DocumentNotFoundException e) {
                if (Collect.Log.WARN)
                    Log.w(Collect.LOGTAG, t + "unable to retrieve attachment: " + e.toString());
                mResults.put(id, fail + "warning: attachment not found :: details: " + e.getMessage());
                continue;
            } catch (DbAccessException e) {
                if (Collect.Log.WARN)
                    Log.w(Collect.LOGTAG, t + "unable to access database: " + e.toString());
                mResults.put(id, fail + "error: could not acess database :: details: " + e.getMessage());
                continue;
            } catch (Exception e) {
                if (Collect.Log.ERROR)
                    Log.e(Collect.LOGTAG, t + "unexpected exception: " + e.toString());
                e.printStackTrace();
                mResults.put(id, fail + "unexpected error :: details: " + e.getMessage());
                continue;
            }
            // END custom

            // get instance file
            // BEGIN custom
            //                File instanceFile = new File(instance);
            File instanceFile = new File(uploadFolder, id + ".xml");
            // END custom

            if (!instanceFile.exists()) {
                mResults.put(id, fail + "instance XML file does not exist!");
                // BEGIN custom
                //                    cv.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                //                    Collect.getInstance().getContentResolver().update(toUpdate, cv, null, null);

                try {
                    instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                    Collect.getInstance().getDbService().getDb().update(instanceDoc);
                } catch (Exception e1) {
                    if (Collect.Log.ERROR)
                        Log.e(Collect.LOGTAG,
                                t + ": could not record upload failed because of missing instance file for " + id
                                        + ": " + e1.toString());
                }
                // END custom
                continue;
            }

            // find all files in parent directory
            File[] allFiles = instanceFile.getParentFile().listFiles();

            // add media files
            List<File> files = new ArrayList<File>();
            for (File f : allFiles) {
                String fileName = f.getName();

                int dotIndex = fileName.lastIndexOf(".");
                String extension = "";
                if (dotIndex != -1) {
                    extension = fileName.substring(dotIndex + 1);
                }

                if (fileName.startsWith(".")) {
                    // ignore invisible files
                    continue;
                }
                if (fileName.equals(instanceFile.getName())) {
                    continue; // the xml file has already been added
                } else if (openRosaServer) {
                    files.add(f);
                } else if (extension.equals("jpg")) { // legacy 0.9x
                    files.add(f);
                } else if (extension.equals("3gpp")) { // legacy 0.9x
                    files.add(f);
                } else if (extension.equals("3gp")) { // legacy 0.9x
                    files.add(f);
                } else if (extension.equals("mp4")) { // legacy 0.9x
                    files.add(f);
                } else {
                    Log.w(t, "unrecognized file type " + f.getName());
                }
            }

            boolean first = true;
            int j = 0;
            while (j < files.size() || first) {
                first = false;

                HttpPost httppost = WebUtils.createOpenRosaHttpPost(u, mAuth);

                MimeTypeMap m = MimeTypeMap.getSingleton();

                long byteCount = 0L;

                // mime post
                MultipartEntity entity = new MultipartEntity();

                // add the submission file first...
                FileBody fb = new FileBody(instanceFile, "text/xml");
                entity.addPart("xml_submission_file", fb);
                Log.i(t, "added xml_submission_file: " + instanceFile.getName());
                byteCount += instanceFile.length();

                for (; j < files.size(); j++) {
                    File f = files.get(j);
                    String fileName = f.getName();
                    int idx = fileName.lastIndexOf(".");
                    String extension = "";
                    if (idx != -1) {
                        extension = fileName.substring(idx + 1);
                    }
                    String contentType = m.getMimeTypeFromExtension(extension);

                    // we will be processing every one of these, so
                    // we only need to deal with the content type determination...
                    if (extension.equals("xml")) {
                        fb = new FileBody(f, "text/xml");
                        entity.addPart(f.getName(), fb);
                        byteCount += f.length();
                        Log.i(t, "added xml file " + f.getName());
                    } else if (extension.equals("jpg")) {
                        fb = new FileBody(f, "image/jpeg");
                        entity.addPart(f.getName(), fb);
                        byteCount += f.length();
                        Log.i(t, "added image file " + f.getName());
                    } else if (extension.equals("3gpp")) {
                        fb = new FileBody(f, "audio/3gpp");
                        entity.addPart(f.getName(), fb);
                        byteCount += f.length();
                        Log.i(t, "added audio file " + f.getName());
                    } else if (extension.equals("3gp")) {
                        fb = new FileBody(f, "video/3gpp");
                        entity.addPart(f.getName(), fb);
                        byteCount += f.length();
                        Log.i(t, "added video file " + f.getName());
                    } else if (extension.equals("mp4")) {
                        fb = new FileBody(f, "video/mp4");
                        entity.addPart(f.getName(), fb);
                        byteCount += f.length();
                        Log.i(t, "added video file " + f.getName());
                    } else if (extension.equals("csv")) {
                        fb = new FileBody(f, "text/csv");
                        entity.addPart(f.getName(), fb);
                        byteCount += f.length();
                        Log.i(t, "added csv file " + f.getName());
                    } else if (f.getName().endsWith(".amr")) {
                        fb = new FileBody(f, "audio/amr");
                        entity.addPart(f.getName(), fb);
                        Log.i(t, "added audio file " + f.getName());
                    } else if (extension.equals("xls")) {
                        fb = new FileBody(f, "application/vnd.ms-excel");
                        entity.addPart(f.getName(), fb);
                        byteCount += f.length();
                        Log.i(t, "added xls file " + f.getName());
                    } else if (contentType != null) {
                        fb = new FileBody(f, contentType);
                        entity.addPart(f.getName(), fb);
                        byteCount += f.length();
                        Log.i(t, "added recognized filetype (" + contentType + ") " + f.getName());
                    } else {
                        contentType = "application/octet-stream";
                        fb = new FileBody(f, contentType);
                        entity.addPart(f.getName(), fb);
                        byteCount += f.length();
                        Log.w(t, "added unrecognized file (" + contentType + ") " + f.getName());
                    }

                    // we've added at least one attachment to the request...
                    if (j + 1 < files.size()) {
                        if (byteCount + files.get(j + 1).length() > 10000000L) {
                            // the next file would exceed the 10MB threshold...
                            Log.i(t, "Extremely long post is being split into multiple posts");
                            try {
                                StringBody sb = new StringBody("yes", Charset.forName("UTF-8"));
                                entity.addPart("*isIncomplete*", sb);
                            } catch (Exception e) {
                                e.printStackTrace(); // never happens...
                            }
                            ++j; // advance over the last attachment added...
                            break;
                        }
                    }
                }

                httppost.setEntity(entity);

                // prepare response and return uploaded
                HttpResponse response = null;
                try {
                    response = httpclient.execute(httppost, localContext);
                    int responseCode = response.getStatusLine().getStatusCode();

                    try {
                        // have to read the stream in order to reuse the connection
                        InputStream is = response.getEntity().getContent();
                        // read to end of stream...
                        final long count = 1024L;
                        while (is.skip(count) == count)
                            ;
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    Log.i(t, "Response code:" + responseCode);
                    // verify that the response was a 201 or 202.
                    // If it wasn't, the submission has failed.
                    if (responseCode != 201 && responseCode != 202) {
                        if (responseCode == 200) {
                            mResults.put(id, fail + "Network login failure? Again?");
                        } else {
                            mResults.put(id, fail + response.getStatusLine().getReasonPhrase() + " (" + responseCode
                                    + ") at " + urlString);
                        }
                        // BEGIN custom
                        //                            cv.put(InstanceColumns.STATUS,
                        //                                InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                        //                            Collect.getInstance().getContentResolver()
                        //                                    .update(toUpdate, cv, null, null);

                        try {
                            instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                            Collect.getInstance().getDbService().getDb().update(instanceDoc);
                        } catch (Exception e1) {
                            if (Collect.Log.ERROR)
                                Log.e(Collect.LOGTAG,
                                        t + ": could not record upload failed because of network login error for "
                                                + id + ": " + e1.toString());
                        }
                        // END custom
                        continue next_submission;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    mResults.put(id, fail + "Generic Exception. " + e.getMessage());
                    // BEGIN custom
                    //                        cv.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_SUBMISSION_FAILED);
                    //                        Collect.getInstance().getContentResolver().update(toUpdate, cv, null, null);

                    try {
                        instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.failed);
                        Collect.getInstance().getDbService().getDb().update(instanceDoc);
                    } catch (Exception e1) {
                        if (Collect.Log.ERROR)
                            Log.e(Collect.LOGTAG,
                                    t + ": could not record upload failed because of generic exception for " + id
                                            + ": " + e1.toString());
                    }
                    // END custom
                    continue next_submission;
                }
            }

            // if it got here, it must have worked
            mResults.put(id, Collect.getInstance().getString(R.string.success));
            // BEGIN custom
            //                cv.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_SUBMITTED);
            //                Collect.getInstance().getContentResolver().update(toUpdate, cv, null, null);
            // END custom

            // BEGIN custom
            instanceDoc.getOdk().setUploadStatus(ODKInstanceAttributes.UploadStatus.complete);
            instanceDoc.getOdk().setUploadDate(Generic.generateTimestamp());

            try {
                Collect.getInstance().getDbService().getDb().update(instanceDoc);
            } catch (Exception e) {
                if (Collect.Log.ERROR)
                    Log.e(Collect.LOGTAG, t + "unable to setUploadDate of successful upload: " + e.toString());
                e.printStackTrace();
            } finally {
                FileUtilsExtended.deleteFolder(uploadFolder);
            }
            // END custom
        }
        // BEGIN custom
        //            if (c != null) {
        //                c.close();
        //            }
        //
        //        } // end while
        // END custom

        return mResults;
    }

    @Override
    protected void onPostExecute(HashMap<String, String> value) {
        synchronized (this) {
            if (mStateListener != null) {
                if (mAuthRequestingServer != null) {
                    mStateListener.authRequest(mAuthRequestingServer, mResults);
                } else {
                    mStateListener.uploadingComplete(value);
                }
            }
        }
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        synchronized (this) {
            if (mStateListener != null) {
                // update progress and total
                mStateListener.progressUpdate(values[0].intValue(), values[1].intValue());
            }
        }
    }

    public void setUploaderListener(InstanceUploaderListener sl) {
        synchronized (this) {
            mStateListener = sl;
        }
    }
}