org.witness.ssc.xfer.utils.PublishingUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.witness.ssc.xfer.utils.PublishingUtils.java

Source

package org.witness.ssc.xfer.utils;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.http.HttpHost;
import org.apache.http.HttpVersion;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.witness.ssc.xfer.R;
import org.witness.ssc.xfer.activity.SSCXferActivity;
import org.witness.ssc.xfer.sslemail.SSLEmailSender;
import org.witness.ssc.xfer.utils.CustomMultiPartEntity.ProgressListener;
import org.witness.ssc.xfer.utils.GoogleAuthoriser.AuthorizationListener;
import org.xml.sax.SAXException;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;

/*
 * Vidiom Library Activity 
 * 
 * AUTHORS:
 * 
 * Andy Nicholson
 * 
 * 2010
 * Copyright Infinite Recursion Pty Ltd.
 * http://www.infiniterecursion.com.au
 */

public class PublishingUtils {

    private static final String TAG = "sscxfer";

    private File folder;
    private Resources res;
    private DBUtils dbutils;
    private Activity mActivity;

    private static final String INITIAL_UPLOAD_URL = "https://uploads.gdata.youtube.com/resumable/feeds/api/users/default/uploads";
    private static final String DEFAULT_VIDEO_CATEGORY = "News";
    private static final String DEFAULT_VIDEO_TAGS = "mobile";
    private static final String YOUTUBE_PLAYER_URL = "https://www.youtube.com/watch?feature=player_profilepage&v=";

    private static final int MAX_RETRIES = 5;
    private static final int BACKOFF = 4; // base of exponential backoff

    private String clientLoginToken = null;
    private String youTubeName = null;

    private double currentFileSize = 0;
    private double totalBytesUploaded = 0;
    private int numberOfRetries = 0;
    private String tags = null;
    private GoogleAuthoriser authorizer;

    //for Tor
    private final static String PROXY_HOST = "127.0.0.1";
    private final static int PROXY_PORT_HTTP = 8118;
    private final static int PROXY_PORT_SOCKS = 9050;

    private boolean useProxy = true;

    private int percentUploaded = 0;
    private long totalLength = 0;

    public PublishingUtils(Resources res, DBUtils dbutils) {

        this.res = res;
        this.dbutils = dbutils;
        folder = new File(Environment.getExternalStorageDirectory() + res.getString(R.string.rootSDcardFolder));

    }

    public static String showDate(long timemillis) {

        if (timemillis <= 0)
            return "N/A";

        Calendar cal;
        SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss");
        cal = Calendar.getInstance();
        cal.setTimeInMillis(timemillis);
        return sdf.format(cal.getTime());

    }

    /*
     * 
     * Methods for publishing the video
     */

    public Thread videoUploadToVideoBin(final Activity activity, final Handler handler,
            final String video_absolutepath, final String title, final String description,
            final String emailAddress, final long sdrecord_id) {

        Log.d(TAG, "doPOSTtoVideoBin starting");

        // Make the progress bar view visible.
        ((SSCXferActivity) activity).startedUploading();
        final Resources res = activity.getResources();

        Thread t = new Thread(new Runnable() {
            public void run() {
                // Do background task.

                boolean failed = false;

                HttpClient client = new DefaultHttpClient();

                if (useProxy) {
                    HttpHost proxy = new HttpHost(PROXY_HOST, PROXY_PORT_HTTP);
                    client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
                }

                client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);

                URI url = null;
                try {
                    url = new URI(res.getString(R.string.http_videobin_org_add));
                } catch (URISyntaxException e) {
                    // Ours is a fixed URL, so not likely to get here.
                    e.printStackTrace();
                    return;

                }
                HttpPost post = new HttpPost(url);
                CustomMultiPartEntity entity = new CustomMultiPartEntity(HttpMultipartMode.BROWSER_COMPATIBLE,
                        new ProgressListener() {
                            int lastPercent = 0;

                            @Override
                            public void transferred(long num) {

                                percentUploaded = (int) (((float) num) / ((float) totalLength) * 99f);
                                //Log.d(TAG, "percent uploaded: " + percentUploaded + " - " + num + " / " + totalLength);
                                if (lastPercent != percentUploaded) {
                                    ((SSCXferActivity) activity).showProgress("uploading...", percentUploaded);
                                    lastPercent = percentUploaded;
                                }
                            }

                        });

                File file = new File(video_absolutepath);
                entity.addPart(res.getString(R.string.video_bin_API_videofile), new FileBody(file));

                try {
                    entity.addPart(res.getString(R.string.video_bin_API_api),
                            new StringBody("1", "text/plain", Charset.forName("UTF-8")));

                    // title
                    entity.addPart(res.getString(R.string.video_bin_API_title),
                            new StringBody(title, "text/plain", Charset.forName("UTF-8")));

                    // description
                    entity.addPart(res.getString(R.string.video_bin_API_description),
                            new StringBody(description, "text/plain", Charset.forName("UTF-8")));

                } catch (IllegalCharsetNameException e) {
                    // error
                    e.printStackTrace();
                    failed = true;

                } catch (UnsupportedCharsetException e) {
                    // error
                    e.printStackTrace();
                    return;
                } catch (UnsupportedEncodingException e) {
                    // error
                    e.printStackTrace();
                    failed = true;
                }

                post.setEntity(entity);

                totalLength = entity.getContentLength();

                // Here we go!
                String response = null;
                try {
                    response = EntityUtils.toString(client.execute(post).getEntity(), "UTF-8");
                } catch (ParseException e) {
                    // error
                    e.printStackTrace();
                    failed = true;
                } catch (ClientProtocolException e) {
                    // error
                    e.printStackTrace();
                    failed = true;
                } catch (IOException e) {
                    // error
                    e.printStackTrace();
                    failed = true;
                }

                client.getConnectionManager().shutdown();

                if (failed) {
                    // Use the handler to execute a Runnable on the
                    // main thread in order to have access to the
                    // UI elements.
                    handler.postDelayed(new Runnable() {
                        public void run() {
                            // Update UI

                            // Indicate back to calling activity the result!
                            // update uploadInProgress state also.

                            ((SSCXferActivity) activity).finishedUploading(false);

                            ((SSCXferActivity) activity)
                                    .createNotification(res.getString(R.string.upload_to_videobin_org_failed_));
                        }
                    }, 0);

                    return;
                }

                Log.d(TAG, " video bin got back " + response);

                // XXX Convert to preference for auto-email on videobin post
                // XXX ADD EMAIL NOTIF to all other upload methods
                // stuck on YES here, if email is defined.

                if (emailAddress != null && response != null) {

                    // EmailSender through IR controlled gmail system.
                    SSLEmailSender sender = new SSLEmailSender(
                            activity.getString(R.string.automatic_email_username),
                            activity.getString(R.string.automatic_email_password)); // consider
                    // this
                    // public
                    // knowledge.
                    try {
                        sender.sendMail(activity.getString(R.string.vidiom_automatic_email), // subject.getText().toString(),
                                activity.getString(R.string.url_of_hosted_video_is_) + " " + response, // body.getText().toString(),
                                activity.getString(R.string.automatic_email_from), // from.getText().toString(),
                                emailAddress // to.getText().toString()
                        );
                    } catch (Exception e) {
                        Log.e(TAG, e.getMessage(), e);
                    }
                }

                // Log record of this URL in POSTs table
                dbutils.creatHostDetailRecordwithNewVideoUploaded(sdrecord_id,
                        res.getString(R.string.http_videobin_org_add), response, "");

                // Use the handler to execute a Runnable on the
                // main thread in order to have access to the
                // UI elements.
                handler.postDelayed(new Runnable() {
                    public void run() {
                        // Update UI

                        // Indicate back to calling activity the result!
                        // update uploadInProgress state also.

                        ((SSCXferActivity) activity).finishedUploading(true);
                        ((SSCXferActivity) activity)
                                .createNotification(res.getString(R.string.upload_to_videobin_org_succeeded_));

                    }
                }, 0);
            }
        });

        t.start();

        return t;

    }

    public Thread videoUploadToFTPserver(final Activity activity, final Handler handler,
            final String latestVideoFile_filename, final String latestVideoFile_absolutepath,
            final String emailAddress, final long sdrecord_id) {

        Log.d(TAG, "doVideoFTP starting");

        // Make the progress bar view visible.
        ((SSCXferActivity) activity).startedUploading();

        final Resources res = activity.getResources();

        Thread t = new Thread(new Runnable() {
            public void run() {
                // Do background task.
                // FTP; connect preferences here!
                //
                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity.getBaseContext());
                String ftpHostName = prefs.getString("defaultFTPhostPreference", null);
                String ftpUsername = prefs.getString("defaultFTPusernamePreference", null);
                String ftpPassword = prefs.getString("defaultFTPpasswordPreference", null);

                // use name of local file.
                String ftpRemoteFtpFilename = latestVideoFile_filename;

                // FTP
                FTPClient ftpClient = new FTPClient();
                InetAddress uploadhost = null;
                try {

                    uploadhost = InetAddress.getByName(ftpHostName);
                } catch (UnknownHostException e1) {
                    // If DNS resolution fails then abort immediately - show
                    // dialog to
                    // inform user first.
                    e1.printStackTrace();
                    Log.e(TAG, " got exception resolving " + ftpHostName + " - video uploading failed.");
                    uploadhost = null;
                }

                if (uploadhost == null) {

                    // Use the handler to execute a Runnable on the
                    // main thread in order to have access to the
                    // UI elements.
                    handler.postDelayed(new Runnable() {
                        public void run() {
                            // Update UI

                            // Hide the progress bar
                            ((SSCXferActivity) activity).finishedUploading(false);
                            ((SSCXferActivity) activity)
                                    .createNotification(res.getString(R.string.upload_to_ftp_host_failed_));

                            new AlertDialog.Builder(activity).setMessage(R.string.cant_find_upload_host)
                                    .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                                        public void onClick(DialogInterface dialog, int whichButton) {

                                        }
                                    })

                                    .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                                        public void onClick(DialogInterface dialog, int whichButton) {

                                        }
                                    }).show();

                        }
                    }, 0);

                    return;
                }

                boolean connected = false;

                try {
                    ftpClient.connect(uploadhost);
                    connected = true;

                } catch (SocketException e) {
                    e.printStackTrace();
                    connected = false;

                } catch (UnknownHostException e) {
                    //
                    e.printStackTrace();
                    connected = false;
                } catch (IOException e) {
                    //
                    e.printStackTrace();
                    connected = false;
                }

                if (!connected) {

                    // Use the handler to execute a Runnable on the
                    // main thread in order to have access to the
                    // UI elements.
                    handler.postDelayed(new Runnable() {
                        public void run() {
                            // Update UI

                            // Hide the progress bar
                            ((SSCXferActivity) activity).finishedUploading(false);
                            ((SSCXferActivity) activity)
                                    .createNotification(res.getString(R.string.upload_to_ftp_host_failed_));

                            new AlertDialog.Builder(activity).setMessage(R.string.cant_login_upload_host)
                                    .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                                        public void onClick(DialogInterface dialog, int whichButton) {

                                        }
                                    })

                                    .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                                        public void onClick(DialogInterface dialog, int whichButton) {

                                        }
                                    }).show();

                        }
                    }, 0);

                    return;
                }

                boolean reply = false;
                try {

                    reply = ftpClient.login(ftpUsername, ftpPassword);
                } catch (IOException e) {
                    //
                    e.printStackTrace();
                    Log.e(TAG, " got exception on ftp.login - video uploading failed.");
                }

                // check the reply code here
                // If we cant login, abort after showing user a dialog.
                if (!reply) {
                    try {
                        ftpClient.disconnect();
                    } catch (IOException e) {
                        //
                        e.printStackTrace();
                    }

                    // Use the handler to execute a Runnable on the
                    // main thread in order to have access to the
                    // UI elements.
                    handler.postDelayed(new Runnable() {
                        public void run() {
                            // Update UI

                            // Hide the progress bar
                            ((SSCXferActivity) activity).finishedUploading(false);
                            ((SSCXferActivity) activity)
                                    .createNotification(res.getString(R.string.upload_to_ftp_host_failed_));

                            new AlertDialog.Builder(activity).setMessage(R.string.cant_login_upload_host)
                                    .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                                        public void onClick(DialogInterface dialog, int whichButton) {

                                        }
                                    }).show();
                        }
                    }, 0);

                    return;
                }

                // Set File type to binary
                try {
                    ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
                } catch (IOException e) {
                    //
                    e.printStackTrace();
                    // keep going?!
                }

                // BEYOND HERE DONT USE DIALOGS!

                // Construct the input stream to send to Ftp server, from the
                // local
                // video file on the sd card
                BufferedInputStream buffIn = null;
                File file = new File(latestVideoFile_absolutepath);

                try {
                    buffIn = new BufferedInputStream(new FileInputStream(file));
                } catch (FileNotFoundException e) {
                    //
                    e.printStackTrace();
                    Log.e(TAG, " got exception on local video file - video uploading failed.");

                    // Use the handler to execute a Runnable on the
                    // main thread in order to have access to the
                    // UI elements.
                    handler.postDelayed(new Runnable() {
                        public void run() {
                            // Update UI

                            // Hide the progress bar
                            ((SSCXferActivity) activity).finishedUploading(false);
                            ((SSCXferActivity) activity)
                                    .createNotification(res.getString(R.string.upload_to_ftp_host_failed_));

                        }
                    }, 0);

                    // This is a bad error, lets abort.
                    // user dialog ?! shouldnt happen, but still...
                    return;
                }

                ftpClient.enterLocalPassiveMode();

                try {
                    // UPLOAD THE LOCAL VIDEO FILE.
                    ftpClient.storeFile(ftpRemoteFtpFilename, buffIn);
                } catch (IOException e) {
                    //
                    e.printStackTrace();
                    Log.e(TAG, " got exception on storeFile - video uploading failed.");

                    // This is a bad error, lets abort.
                    // user dialog ?! shouldnt happen, but still...
                    // Use the handler to execute a Runnable on the
                    // main thread in order to have access to the
                    // UI elements.
                    handler.postDelayed(new Runnable() {
                        public void run() {
                            // Update UI

                            // Hide the progress bar
                            ((SSCXferActivity) activity).finishedUploading(false);
                            ((SSCXferActivity) activity)
                                    .createNotification(res.getString(R.string.upload_to_ftp_host_failed_));

                        }
                    }, 0);
                    return;
                }
                try {
                    buffIn.close();
                } catch (IOException e) {
                    //
                    e.printStackTrace();
                    Log.e(TAG, " got exception on buff.close - video uploading failed.");

                    // Use the handler to execute a Runnable on the
                    // main thread in order to have access to the
                    // UI elements.
                    handler.postDelayed(new Runnable() {
                        public void run() {
                            // Update UI

                            // Hide the progress bar
                            ((SSCXferActivity) activity).finishedUploading(false);
                            ((SSCXferActivity) activity)
                                    .createNotification(res.getString(R.string.upload_to_ftp_host_failed_));

                        }
                    }, 0);
                    return;
                }
                try {
                    ftpClient.logout();
                } catch (IOException e) {
                    //
                    e.printStackTrace();
                    Log.e(TAG, " got exception on ftp logout - video uploading failed.");

                    // Use the handler to execute a Runnable on the
                    // main thread in order to have access to the
                    // UI elements.
                    handler.postDelayed(new Runnable() {
                        public void run() {
                            // Update UI

                            // Hide the progress bar
                            ((SSCXferActivity) activity).finishedUploading(false);
                            ((SSCXferActivity) activity)
                                    .createNotification(res.getString(R.string.upload_to_ftp_host_failed_));

                        }
                    }, 0);
                    return;
                }
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    //
                    e.printStackTrace();
                    Log.e(TAG, " got exception on ftp disconnect - video uploading failed.");

                    // Use the handler to execute a Runnable on the
                    // main thread in order to have access to the
                    // UI elements.
                    handler.postDelayed(new Runnable() {
                        public void run() {
                            // Update UI

                            // Hide the progress bar
                            ((SSCXferActivity) activity).finishedUploading(false);
                            ((SSCXferActivity) activity)
                                    .createNotification(res.getString(R.string.upload_to_ftp_host_failed_));

                        }
                    }, 0);
                    return;
                }

                if (emailAddress != null && ftpHostName != null) {

                    // EmailSender through IR controlled gmail system.
                    SSLEmailSender sender = new SSLEmailSender(
                            activity.getString(R.string.automatic_email_username),
                            activity.getString(R.string.automatic_email_password)); // consider
                    // this
                    // public
                    // knowledge.
                    try {
                        sender.sendMail(activity.getString(R.string.vidiom_automatic_email), // subject.getText().toString(),
                                activity.getString(R.string.url_of_hosted_video_is_) + " " + ftpHostName, // body.getText().toString(),
                                activity.getString(R.string.automatic_email_from), // from.getText().toString(),
                                emailAddress // to.getText().toString()
                        );
                    } catch (Exception e) {
                        Log.e(TAG, e.getMessage(), e);
                    }
                }

                // Log record of this URL in POSTs table
                dbutils.creatHostDetailRecordwithNewVideoUploaded(sdrecord_id, ftpHostName, ftpHostName, "");

                // Use the handler to execute a Runnable on the
                // main thread in order to have access to the
                // UI elements.
                handler.postDelayed(new Runnable() {
                    public void run() {
                        // Update UI

                        // Indicate back to calling activity the result!
                        // update uploadInProgress state also.

                        ((SSCXferActivity) activity).finishedUploading(true);
                        ((SSCXferActivity) activity)
                                .createNotification(res.getString(R.string.upload_to_ftp_host_succeeded_));

                    }
                }, 0);

            }
        });

        t.start();

        return t;
    }

    public void launchEmailIntentWithCurrentVideo(final Activity activity,
            final String latestVideoFile_absolutepath) {
        Log.d(TAG, "launchEmailIntentWithCurrentVideo starting");

        Intent i = new Intent(Intent.ACTION_SEND);
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        // XXX hardcoded video mimetype
        i.setType("video/mp4");
        i.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + latestVideoFile_absolutepath));
        activity.startActivity(i);
    }

    public void launchVideoPlayer(final Activity activity, final String movieurl) {

        try {
            Intent tostart = new Intent(Intent.ACTION_VIEW);
            tostart.setDataAndType(Uri.parse(movieurl), "video/*");
            activity.startActivity(tostart);
        } catch (android.content.ActivityNotFoundException e) {
            Log.e(TAG, " Cant start activity to show video!");

            new AlertDialog.Builder(activity).setMessage(R.string.cant_show_video)
                    .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {

                        }
                    })

                    .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {

                        }
                    }).show();

            return;
        }

    }

    public File selectFilenameAndCreateFile(String filenameConventionPrefence) {
        // Video file name selection process
        String new_videofile_name = res.getString(R.string.defaultVideoFilenamePrefix);
        String file_ext_name = ".mp4";

        if (filenameConventionPrefence
                .compareTo(res.getString(R.string.filenameConventionDefaultPreference)) == 0) {
            // The default is by date
            SimpleDateFormat postFormater = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
            Calendar cal = Calendar.getInstance();
            Date now = cal.getTime();
            String newDateStr = postFormater.format(now);

            new_videofile_name += newDateStr + file_ext_name;

        } else {
            // Sequentially

            // look into database for this number
            int next_number = dbutils.getNextFilenameNumberAndIncrement();

            // XXX deal with -1 error condition

            new_videofile_name += next_number + file_ext_name;

        }

        File tempFile = new File(folder.getAbsolutePath(), new_videofile_name);
        return tempFile;
    }

    public boolean deleteVideo(String movieuri) {
        Log.d(TAG, "deleteVideo with " + movieuri);

        File tempFile = new File(movieuri);

        return tempFile.delete();

    }

    // youtube upload code from
    // from
    // http://code.google.com/p/ytd-android/source/browse/trunk/src/com/google/ytd/SubmitActivity.java
    // http://www.apache.org/licenses/LICENSE-2.0
    // Copyright 2010 Google License Apache
    // 
    // Changed by Andy Nicholson

    public void asyncYouTubeUpload(final Activity activity, final File file, final Handler handler,
            final String emailAddress, final long sdrecord_id) {

        new Thread(new Runnable() {
            public void run() {
                Message msg = new Message();
                Bundle bundle = new Bundle();
                msg.setData(bundle);

                String videoId = null;
                int submitCount = 0;
                try {
                    while (submitCount <= MAX_RETRIES && videoId == null) {
                        try {
                            submitCount++;
                            videoId = startYouTubeUpload(activity, file, handler, emailAddress, sdrecord_id);
                            assert videoId != null;
                        } catch (Internal500ResumeException e500) { // TODO -
                            // this
                            // should
                            // not
                            // really
                            // happen
                            if (submitCount < MAX_RETRIES) {
                                Log.w(TAG, e500.getMessage());
                                Log.d(TAG, String.format("Upload retry :%d.", submitCount));
                            } else {
                                Log.d(TAG, "Giving up");
                                Log.e(TAG, e500.getMessage());
                                throw new IOException(e500.getMessage());
                            }
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(TAG, "AsyncYouTubeUpload ERROR - finishing");
                    return;
                } catch (YouTubeAccountException e) {
                    e.printStackTrace();
                    Log.e(TAG, "AsyncYouTubeUpload ERROR - finishing");
                    return;
                } catch (SAXException e) {
                    e.printStackTrace();
                    Log.e(TAG, "AsyncYouTubeUpload ERROR - finishing");
                } catch (ParserConfigurationException e) {
                    e.printStackTrace();
                    Log.e(TAG, "AsyncYouTubeUpload ERROR - finishing");
                }

            }
        }).start();
    }

    static class YouTubeAccountException extends Exception {
        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        public YouTubeAccountException(String msg) {
            super(msg);
        }
    }

    private String startYouTubeUpload(final Activity activity, File file, final Handler handler,
            final String emailAddress, final long sdrecord_id) throws IOException, YouTubeAccountException,
            SAXException, ParserConfigurationException, Internal500ResumeException {

        if (this.clientLoginToken == null) {
            // The stored gmail account is not linked to YouTube
            throw new YouTubeAccountException(this.youTubeName + " is not linked to a YouTube account.");
        }

        String[] strs = dbutils.getTitleAndDescriptionFromID(new String[] { Long.toString(sdrecord_id) });

        String uploadUrl = uploadMetaData(activity, handler, file.getAbsolutePath(), strs[0], strs[1], true);

        //Log.d(TAG, "uploadUrl=" + uploadUrl + " youtube account name is "
        //   + this.youTubeName);
        //Log.d(TAG, String.format("Client token : %s ", this.clientLoginToken));

        this.currentFileSize = file.length();
        this.totalBytesUploaded = 0;
        this.numberOfRetries = 0;

        int uploadChunk = 1024 * 1024 * 3; // 3MB

        int start = 0;
        int end = -1;

        String videoId = null;
        double fileSize = this.currentFileSize;
        while (fileSize > 0) {
            if (fileSize - uploadChunk > 0) {
                end = start + uploadChunk - 1;
            } else {
                end = start + (int) fileSize - 1;
            }
            Log.d(TAG, String.format("start=%s end=%s total=%s", start, end, file.length()));
            try {
                videoId = gdataUpload(file, uploadUrl, start, end, activity);
                fileSize -= uploadChunk;
                start = end + 1;
                this.numberOfRetries = 0; // clear this counter as we had a
                // successful upload
            } catch (IOException e) {
                Log.d(TAG, "Error during upload : " + e.getMessage());
                ResumeInfo resumeInfo = null;
                do {
                    if (!shouldResume()) {
                        Log.d(TAG, String.format("Giving up uploading '%s'.", uploadUrl));
                        throw e;
                    }
                    try {
                        resumeInfo = resumeFileUpload(uploadUrl);
                    } catch (IOException re) {
                        // ignore
                        Log.d(TAG, String.format("Failed retry attempt of : %s due to: '%s'.", uploadUrl,
                                re.getMessage()));
                    }
                } while (resumeInfo == null);
                Log.d(TAG, String.format("Resuming stalled upload to: %s.", uploadUrl));
                if (resumeInfo.videoId != null) { // upload actually complted
                    // despite the exception
                    videoId = resumeInfo.videoId;
                    Log.d(TAG, String.format("No need to resume video ID '%s'.", videoId));
                    break;
                } else {
                    int nextByteToUpload = resumeInfo.nextByteToUpload;
                    Log.d(TAG, String.format("Next byte to upload is '%d'.", nextByteToUpload));
                    this.totalBytesUploaded = nextByteToUpload; // possibly
                    // rolling back
                    // the
                    // previously
                    // saved value
                    fileSize = this.currentFileSize - nextByteToUpload;
                    start = nextByteToUpload;
                }
            }
        }

        if (videoId != null) {

            if (emailAddress != null) {

                // EmailSender through IR controlled mail system.
                SSLEmailSender sender = new SSLEmailSender(activity.getString(R.string.automatic_email_username),
                        activity.getString(R.string.automatic_email_password)); // consider
                // this
                // public
                // knowledge.
                try {
                    sender.sendMail(activity.getString(R.string.vidiom_automatic_email), // subject.getText().toString(),
                            activity.getString(R.string.url_of_hosted_video_is_) + " " + YOUTUBE_PLAYER_URL
                                    + videoId, // body.getText().toString(),
                            activity.getString(R.string.automatic_email_from), // from.getText().toString(),
                            emailAddress // to.getText().toString()
                    );
                } catch (Exception e) {
                    Log.e(TAG, e.getMessage(), e);
                }
            }

            // Log record of this URL in POSTs table
            dbutils.creatHostDetailRecordwithNewVideoUploaded(sdrecord_id, uploadUrl, YOUTUBE_PLAYER_URL + videoId,
                    "");

            // Use the handler to execute a Runnable on the
            // main thread in order to have access to the
            // UI elements.
            handler.postDelayed(new Runnable() {
                public void run() {
                    // Update UI

                    // Indicate back to calling activity the result!
                    // update uploadInProgress state also.

                    ((SSCXferActivity) activity).finishedUploading(true);
                    ((SSCXferActivity) activity)
                            .createNotification(res.getString(R.string.upload_to_youtube_host_succeeded_));

                }
            }, 0);

            return videoId;
        }

        return null;
    }

    private String uploadMetaData(final Activity activity, final Handler handler, String filePath, String title,
            String description, boolean retry) throws IOException {
        String uploadUrl = INITIAL_UPLOAD_URL;

        HttpURLConnection urlConnection = getGDataUrlConnection(uploadUrl);
        urlConnection.setRequestMethod("POST");
        urlConnection.setDoOutput(true);
        urlConnection.setRequestProperty("Content-Type", "application/atom+xml");
        // urlConnection.setRequestProperty("Content-Length", newValue);
        urlConnection.setRequestProperty("Slug", filePath);
        String atomData = null;

        String category = DEFAULT_VIDEO_CATEGORY;
        this.tags = DEFAULT_VIDEO_TAGS;

        String template = readFile(activity, R.raw.gdata).toString();

        // Workarounds for corner cases. Youtube doesnt like empty titles
        if (title == null || title.length() == 0) {
            title = "Untitled";
        }
        if (description == null || description.length() == 0) {
            description = "No description";
        }

        atomData = String.format(template, title, description, category, this.tags);

        OutputStreamWriter outStreamWriter = null;
        int responseCode = -1;

        try {
            outStreamWriter = new OutputStreamWriter(urlConnection.getOutputStream());
            outStreamWriter.write(atomData);
            outStreamWriter.close();

            /*
             * urlConnection.connect(); InputStream is =
             * urlConnection.getInputStream(); BufferedReader in = new
             * BufferedReader(new InputStreamReader(is)); String inputLine;
             * 
             * while ((inputLine = in.readLine()) != null) {
             * Log.d(TAG,inputLine); } in.close();
             */

            responseCode = urlConnection.getResponseCode();

            // ERROR LOGGING
            InputStream is = urlConnection.getErrorStream();
            if (is != null) {
                Log.e(TAG, " Error stream from Youtube available!");
                BufferedReader in = new BufferedReader(new InputStreamReader(is));
                String inputLine;

                while ((inputLine = in.readLine()) != null) {
                    Log.d(TAG, inputLine);
                }
                in.close();

                Map<String, List<String>> hfs = urlConnection.getHeaderFields();
                for (Entry<String, List<String>> hf : hfs.entrySet()) {
                    Log.d(TAG, " entry : " + hf.getKey());
                    List<String> vals = hf.getValue();
                    for (String s : vals) {
                        Log.d(TAG, "vals:" + s);
                    }
                }
            }

        } catch (IOException e) {
            //
            // Catch IO Exceptions here, like UnknownHostException, so we can
            // detect network failures, and send a notification
            //
            Log.d(TAG, " Error occured in uploadMetaData! ");
            e.printStackTrace();
            responseCode = -1;
            outStreamWriter = null;

            // Use the handler to execute a Runnable on the
            // main thread in order to have access to the
            // UI elements.
            handler.postDelayed(new Runnable() {
                public void run() {
                    // Update UI

                    // Indicate back to calling activity the result!
                    // update uploadInProgress state also.

                    ((SSCXferActivity) activity).finishedUploading(false);
                    ((SSCXferActivity) activity)
                            .createNotification(res.getString(R.string.upload_to_youtube_host_failed_));

                }
            }, 0);

            // forward it on!
            throw e;
        }

        if (responseCode < 200 || responseCode >= 300) {
            // The response code is 40X
            if ((responseCode + "").startsWith("4") && retry) {
                Log.d(TAG, "retrying to fetch auth token for " + youTubeName);
                this.clientLoginToken = authorizer.getFreshAuthToken(youTubeName, clientLoginToken);
                // Try again with fresh token
                return uploadMetaData(activity, handler, filePath, title, description, false);
            } else {

                // Probably not authorised!

                // Need to setup a Youtube account.

                // Use the handler to execute a Runnable on the
                // main thread in order to have access to the
                // UI elements.
                handler.postDelayed(new Runnable() {
                    public void run() {
                        // Update UI

                        // Indicate back to calling activity the result!
                        // update uploadInProgress state also.

                        ((SSCXferActivity) activity).finishedUploading(false);
                        ((SSCXferActivity) activity)
                                .createNotification(res.getString(R.string.upload_to_youtube_host_failed_));

                    }
                }, 0);

                throw new IOException(String.format("response code='%s' (code %d)" + " for %s",
                        urlConnection.getResponseMessage(), responseCode, urlConnection.getURL()));

            }
        }

        return urlConnection.getHeaderField("Location");
    }

    private String gdataUpload(File file, String uploadUrl, int start, int end, Activity activity)
            throws IOException {

        mActivity = activity;

        int chunk = end - start + 1;
        int bufferSize = 1024;
        byte[] buffer = new byte[bufferSize];
        FileInputStream fileStream = new FileInputStream(file);

        HttpURLConnection urlConnection = getGDataUrlConnection(uploadUrl);

        // some mobile proxies do not support PUT, using X-HTTP-Method-Override
        // to get around this problem
        if (isFirstRequest()) {
            Log.d(TAG, String.format("Uploaded %d bytes so far, using POST method.", (int) totalBytesUploaded));
            urlConnection.setRequestMethod("POST");
        } else {
            urlConnection.setRequestMethod("POST");
            urlConnection.setRequestProperty("X-HTTP-Method-Override", "PUT");
            Log.d(TAG, String.format("Uploaded %d bytes so far, using POST with X-HTTP-Method-Override PUT method.",
                    (int) totalBytesUploaded));
        }
        urlConnection.setDoOutput(true);
        urlConnection.setFixedLengthStreamingMode(chunk);
        // /XXX hardcoded video mimetype
        urlConnection.setRequestProperty("Content-Type", "video/mp4");
        urlConnection.setRequestProperty("Content-Range",
                String.format("bytes %d-%d/%d", start, end, file.length()));
        Log.d(TAG, urlConnection.getRequestProperty("Content-Range"));

        OutputStream outStreamWriter = urlConnection.getOutputStream();

        fileStream.skip(start);

        int bytesRead;
        int totalRead = 0;

        Thread thread = new Thread() {

            public void run() {
                int lastPercent = 0;

                while (lastPercent < 100) {
                    if (lastPercent != percentUploaded) {
                        ((SSCXferActivity) mActivity).showProgress("uploading...", percentUploaded);
                        lastPercent = percentUploaded;
                    }

                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                    }

                }
            }
        };
        thread.start();

        while ((bytesRead = fileStream.read(buffer, 0, bufferSize)) != -1) {
            outStreamWriter.write(buffer, 0, bytesRead);
            totalRead += bytesRead;
            this.totalBytesUploaded += bytesRead;

            percentUploaded = (int) (((float) totalBytesUploaded) / ((float) currentFileSize) * 99f);

            if (totalRead == (end - start + 1)) {
                break;
            }
        }

        outStreamWriter.close();

        int responseCode = urlConnection.getResponseCode();

        Log.d(TAG, "responseCode=" + responseCode);
        Log.d(TAG, "responseMessage=" + urlConnection.getResponseMessage());

        try {
            if (responseCode == 201) {
                String videoId = parseVideoId(urlConnection.getInputStream());

                Log.i(TAG, "Youtube video submitted - new video id is " + videoId);

                // 100% finished here.

                // dialog.setProgress(100);

                return videoId;
            } else if (responseCode == 200) {
                Set<String> keySet = urlConnection.getHeaderFields().keySet();
                String keys = urlConnection.getHeaderFields().keySet().toString();
                Log.d(TAG, String.format("Headers keys %s.", keys));
                for (String key : keySet) {
                    Log.d(TAG, String.format("Header key %s value %s.", key, urlConnection.getHeaderField(key)));
                }
                Log.w(TAG, "Received 200 response during resumable uploading");
                throw new IOException(String.format("Unexpected response code : responseCode=%d responseMessage=%s",
                        responseCode, urlConnection.getResponseMessage()));
            } else {
                if ((responseCode + "").startsWith("5")) {
                    String error = String.format("responseCode=%d responseMessage=%s", responseCode,
                            urlConnection.getResponseMessage());
                    Log.w(TAG, error);
                    // TODO - this exception will trigger retry mechanism to
                    // kick in
                    // TODO - even though it should not, consider introducing a
                    // new type so
                    // TODO - resume does not kick in upon 5xx
                    throw new IOException(error);
                } else if (responseCode == 308) {
                    // OK, the chunk completed successfully
                    Log.d(TAG, String.format("responseCode=%d responseMessage=%s", responseCode,
                            urlConnection.getResponseMessage()));
                } else {
                    // TODO - this case is not handled properly yet
                    Log.w(TAG, String.format("Unexpected return code : %d %s while uploading :%s", responseCode,
                            urlConnection.getResponseMessage(), uploadUrl));
                }
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        }

        return null;
    }

    private HttpURLConnection getGDataUrlConnection(String urlString) throws IOException {
        URL url = new URL(urlString);

        if (useProxy) {
            //url = new URL("http",,,urlString);
            System.setProperty("http.proxySet", "true");
            System.setProperty("http.proxyHost", PROXY_HOST);
            System.setProperty("http.proxyPort", PROXY_PORT_HTTP + "");

        } else {
            System.clearProperty("http.proxySet");
            System.clearProperty("http.proxyHost");
            System.clearProperty("http.proxyPort");
        }

        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        connection.setRequestProperty("Authorization", String.format("GoogleLogin auth=\"%s\"", clientLoginToken));
        connection.setRequestProperty("GData-Version", "2");
        connection.setRequestProperty("X-GData-Key",
                String.format("key=%s", res.getString(R.string.youtube_dev_key)));
        return connection;
    }

    public boolean isFirstRequest() {
        return totalBytesUploaded == 0;
    }

    private ResumeInfo resumeFileUpload(String uploadUrl)
            throws IOException, ParserConfigurationException, SAXException, Internal500ResumeException {
        HttpURLConnection urlConnection = getGDataUrlConnection(uploadUrl);
        urlConnection.setRequestProperty("Content-Range", "bytes */*");
        urlConnection.setRequestMethod("POST");
        urlConnection.setRequestProperty("X-HTTP-Method-Override", "PUT");
        urlConnection.setFixedLengthStreamingMode(0);

        HttpURLConnection.setFollowRedirects(false);

        urlConnection.connect();
        int responseCode = urlConnection.getResponseCode();

        if (responseCode >= 300 && responseCode < 400) {
            int nextByteToUpload;
            String range = urlConnection.getHeaderField("Range");
            if (range == null) {
                Log.d(TAG, String.format("PUT to %s did not return 'Range' header.", uploadUrl));
                nextByteToUpload = 0;
            } else {
                Log.d(TAG, String.format("Range header is '%s'.", range));
                String[] parts = range.split("-");
                if (parts.length > 1) {
                    nextByteToUpload = Integer.parseInt(parts[1]) + 1;
                } else {
                    nextByteToUpload = 0;
                }
            }
            return new ResumeInfo(nextByteToUpload);
        } else if (responseCode >= 200 && responseCode < 300) {
            return new ResumeInfo(parseVideoId(urlConnection.getInputStream()));
        } else if (responseCode == 500) {
            // TODO this is a workaround for current problems with resuming
            // uploads while switching transport (Wifi->EDGE)
            throw new Internal500ResumeException(
                    String.format("Unexpected response for PUT to %s: %s " + "(code %d)", uploadUrl,
                            urlConnection.getResponseMessage(), responseCode));
        } else {
            throw new IOException(String.format("Unexpected response for PUT to %s: %s " + "(code %d)", uploadUrl,
                    urlConnection.getResponseMessage(), responseCode));
        }
    }

    //
    // This process fetches a google-youtube auth token, linked from a google
    // email account
    // if successfully, it launches the async upload.
    // 

    private boolean shouldResume() {
        this.numberOfRetries++;
        if (this.numberOfRetries > MAX_RETRIES) {
            return false;
        }
        try {
            int sleepSeconds = (int) Math.pow(BACKOFF, this.numberOfRetries);
            Log.d(TAG, String.format("Zzzzz for : %d sec.", sleepSeconds));
            Thread.currentThread().sleep(sleepSeconds * 1000);
            Log.d(TAG, String.format("Zzzzz for : %d sec done.", sleepSeconds));
        } catch (InterruptedException se) {
            se.printStackTrace();
            return false;
        }
        return true;
    }

    private String parseVideoId(InputStream atomDataStream)
            throws ParserConfigurationException, SAXException, IOException {
        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
        Document doc = docBuilder.parse(atomDataStream);

        NodeList nodes = doc.getElementsByTagNameNS("*", "*");
        for (int i = 0; i < nodes.getLength(); i++) {
            Node node = nodes.item(i);
            String nodeName = node.getNodeName();
            String val_print;
            if (node.getFirstChild() != null) {
                val_print = node.getFirstChild().getNodeValue();
            } else {
                val_print = "null";
            }

            Log.d(TAG, " node name " + nodeName + " val : " + val_print);
            if (nodeName != null && nodeName.equals("yt:videoid")) {
                return node.getFirstChild().getNodeValue();
            }
        }
        return null;
    }

    /*
    private HttpURLConnection getGDataUrlConnection(String urlString)
     throws IOException {
       URL url = new URL(urlString);
       HttpURLConnection connection = (HttpURLConnection) url.openConnection();
       connection.setRequestProperty("Authorization", String.format(
        "GoogleLogin auth=\"%s\"", clientLoginToken));
       connection.setRequestProperty("GData-Version", "2");
       connection.setRequestProperty("X-GData-Key", String.format("key=%s",
        res.getString(R.string.youtube_dev_key)));
       return connection;
    }*/

    public void getYouTubeAuthTokenWithPermissionAndUpload(final Activity activity, String accountName,
            final String path, final Handler handler, final String emailAddress, final long sdrecord_id) {

        // Make the progress bar view visible.
        ((SSCXferActivity) activity).startedUploading();

        this.youTubeName = accountName;

        this.authorizer = new GlsAuthorizer.GlsAuthorizerFactory().getAuthorizer(activity,
                GlsAuthorizer.YOUTUBE_AUTH_TOKEN_TYPE);

        this.authorizer.fetchAuthToken(accountName, activity, new AuthorizationListener<String>() {
            public void onCanceled() {

                Log.d(TAG, " Cancelled in fetchAuthToken! ");

            }

            public void onError(Exception e) {

                Log.d(TAG, " Error in fetchAuthToken! ");

                // Use the handler to execute a Runnable on the
                // main thread in order to have access to the
                // UI elements.
                handler.postDelayed(new Runnable() {
                    public void run() {
                        // Update UI

                        // Indicate back to calling activity the result!
                        // update uploadInProgress state also.

                        ((SSCXferActivity) activity).finishedUploading(false);
                        ((SSCXferActivity) activity)
                                .createNotification(res.getString(R.string.upload_to_youtube_host_failed_));

                    }
                }, 0);

            }

            public void onSuccess(String result) {
                PublishingUtils.this.clientLoginToken = result;
                File file = new File(path);
                // Launch Async YouTube video upload.
                asyncYouTubeUpload(activity, file, handler, emailAddress, sdrecord_id);
            }
        });
    }

    class ResumeInfo {
        int nextByteToUpload;
        String videoId;

        ResumeInfo(int nextByteToUpload) {
            this.nextByteToUpload = nextByteToUpload;
        }

        ResumeInfo(String videoId) {
            this.videoId = videoId;
        }
    }

    /**
     * Need this for now to trigger entire upload transaction retry
     */
    class Internal500ResumeException extends Exception {
        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        Internal500ResumeException(String message) {
            super(message);
        }
    }

    public static CharSequence readFile(Activity activity, int id) {
        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(activity.getResources().openRawResource(id)));
            String line;
            StringBuilder buffer = new StringBuilder();
            while ((line = in.readLine()) != null) {
                buffer.append(line).append('\n');
            }
            // Chomp the last newline
            buffer.deleteCharAt(buffer.length() - 1);
            return buffer;
        } catch (IOException e) {
            return "";
        } finally {
            closeStream(in);
        }
    }

    /**
     * Closes the specified stream.
     * 
     * @param stream
     *            The stream to close.
     */
    private static void closeStream(Closeable stream) {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException e) {
                // Ignore
            }
        }
    }

}