dentex.youtube.downloader.ShareActivity.java Source code

Java tutorial

Introduction

Here is the source code for dentex.youtube.downloader.ShareActivity.java

Source

/***
Copyright (c) 2012-2013 Samuele Rini
    
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
       
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   GNU General Public License for more details.
       
   You should have received a copy of the GNU General Public License
   along with this program. If not, see <http>http://www.gnu.org/licenses
       
   ***
       
   https://github.com/dentex/ytdownloader/
https://sourceforge.net/projects/ytdownloader/
       
   ***
       
   Different Licenses and Credits where noted in code comments.
*/

package dentex.youtube.downloader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.bugsense.trace.BugSenseHandler;

import dentex.youtube.downloader.service.DownloadsService;
import dentex.youtube.downloader.utils.Observer;
import dentex.youtube.downloader.utils.PopUps;
import dentex.youtube.downloader.utils.Utils;

public class ShareActivity extends Activity {

    private ProgressBar progressBar1;
    private ProgressBar progressBarD;
    private ProgressBar progressBarL;
    private ProgressBar filesizeProgressBar;
    public static final String USER_AGENT_FIREFOX = "Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0";
    private static final String DEBUG_TAG = "ShareActivity";
    private TextView tv;
    private ListView lv;
    public ArrayAdapter<String> aA;
    List<String> links = new ArrayList<String>();
    List<String> codecs = new ArrayList<String>();
    List<String> qualities = new ArrayList<String>();
    List<String> stereo = new ArrayList<String>();
    List<String> sizes = new ArrayList<String>();
    List<String> listEntries = new ArrayList<String>();
    private String titleRaw;
    private String title;
    public int pos;
    public static File path;
    public String validatedLink;
    public static DownloadManager dm;
    public static long enqueue;
    String videoFilename = "video";
    public static String composedVideoFilename = "";
    public static Uri videoUri;
    public boolean videoOnExt;
    private int icon;
    public ScrollView generalInfoScrollview;
    public CheckBox showAgain1;
    public CheckBox showAgain2;
    public CheckBox showAgain3;
    public TextView userFilename;
    public static SharedPreferences settings = YTD.settings;
    public final String PREFS_NAME = YTD.PREFS_NAME;
    public static final File dir_Downloads = Environment
            .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
    public static final File dir_DCIM = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
    public static final File dir_Movies = Environment
            .getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
    public File sdcard = Environment.getExternalStorageDirectory();
    boolean sshInfoCheckboxEnabled;
    boolean generalInfoCheckboxEnabled;
    boolean fileRenameEnabled;
    public static File chooserFolder;
    private AsyncDownload asyncDownload;
    public boolean isAsyncDownloadRunning = false;
    public String videoFileSize = "empty";
    private AsyncSizeQuery sizeQuery;
    public AlertDialog helpDialog;
    public AlertDialog waitBox;
    private AlertDialog.Builder helpBuilder;
    private AlertDialog.Builder waitBuilder;
    private Bitmap img;
    private ImageView imgView;
    private String videoId;
    public static String pt1;
    public static String pt2;
    public static String noDownloads;
    public static Observer.YtdFileObserver videoFileObserver;
    public static int mId = 0;
    public static NotificationManager mNotificationManager;
    public static NotificationCompat.Builder mBuilder;
    public static String onlineVersion;
    public static List<Long> sequence = new ArrayList<Long>();
    String audioFilename = "audio";
    public static String audioCodec = "";
    private boolean audioExtractionEnabled;
    public static Context mContext;
    boolean showSizesInVideoList;
    boolean showSingleSize;
    ContextThemeWrapper boxThemeContextWrapper = new ContextThemeWrapper(this, R.style.BoxTheme);
    public int count;
    public String acodec = "";
    public String extrType;
    public String aquality;
    public boolean audioExtrEnabled = false;
    public CheckBox audioConfirm;

    @SuppressLint("CutPasteId")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = getBaseContext();
        settings = getSharedPreferences(PREFS_NAME, 0);

        // Theme init
        Utils.themeInit(this);

        setContentView(R.layout.activity_share);

        showSizesInVideoList = settings.getBoolean("show_size_list", false);

        // Language init
        Utils.langInit(this);

        // loading views from the layout xml
        tv = (TextView) findViewById(R.id.textView1);

        progressBarD = (ProgressBar) findViewById(R.id.progressBarD);
        progressBarL = (ProgressBar) findViewById(R.id.progressBarL);

        String theme = settings.getString("choose_theme", "D");
        if (theme.equals("D")) {
            progressBar1 = progressBarD;
            progressBarL.setVisibility(View.GONE);
        } else {
            progressBar1 = progressBarL;
            progressBarD.setVisibility(View.GONE);
        }

        imgView = (ImageView) findViewById(R.id.imgview);

        lv = (ListView) findViewById(R.id.list);

        // YTD update initialization
        updateInit();

        // Get intent, action and MIME type
        Intent intent = getIntent();
        String action = intent.getAction();
        String type = intent.getType();

        if (Intent.ACTION_SEND.equals(action) && type != null) {
            if ("text/plain".equals(type)) {
                try {
                    handleSendText(intent, action);
                    Utils.logger("d", "handling ACTION_SEND", DEBUG_TAG);
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(DEBUG_TAG, "Error: " + e.toString(), e);
                }
            }
        }

        if (Intent.ACTION_VIEW.equals(action)) {
            try {
                handleSendText(intent, action);
                Utils.logger("d", "handling ACTION_VIEW", DEBUG_TAG);
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(DEBUG_TAG, "Error: " + e.toString(), e);
            }
        }
    }

    public static Context getContext() {
        return mContext;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_share, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        switch (item.getItemId()) {
        case R.id.menu_donate:
            startActivity(new Intent(this, DonateActivity.class));
            return true;
        case R.id.menu_settings:
            startActivity(new Intent(this, SettingsActivity.class));
            return true;
        case R.id.menu_about:
            startActivity(new Intent(this, AboutActivity.class));
            return true;
        case R.id.menu_dm:
            Intent viewIntent = new Intent(android.app.DownloadManager.ACTION_VIEW_DOWNLOADS);
            if (viewIntent.resolveActivity(getPackageManager()) != null) {
                startActivity(viewIntent);
            } else {
                Toast.makeText(this, getString(R.string.no_downloads_sys_app), Toast.LENGTH_LONG).show();
            }
            return true;
        case R.id.menu_tutorials:
            startActivity(new Intent(this, TutorialsActivity.class));
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        registerReceiver(inAppCompleteReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
        Utils.logger("v", "_onStart", DEBUG_TAG);
    }

    /*@Override
    protected void onRestart() {
       super.onRestart();
       Utils.logger("v", "_onRestart");
    }
        
    @Override
    public void onPause() {
       super.onPause();
       Utils.logger("v", "_onPause");
    }*/

    @Override
    protected void onStop() {
        super.onStop();
        unregisterReceiver(inAppCompleteReceiver);
        Utils.logger("v", "_onStop", DEBUG_TAG);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        /*
         * The next call is here onBackPressed(), and NOT in onStop() because 
         * I want to cancel the asyncDownload task only on back button pressed,
         * and not when switching to Preferences or D.M. from this activity.
         */
        if (isAsyncDownloadRunning) {
            asyncDownload.cancel(true);
        }
        Utils.logger("v", "_onBackPressed", DEBUG_TAG);
    }

    void handleSendText(Intent intent, String action) throws IOException {

        ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isConnected()) {

            String sharedText = null;
            if (action.equals(Intent.ACTION_SEND)) {
                sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
            } else if (action.equals(Intent.ACTION_VIEW)) {
                sharedText = intent.getDataString();
            }

            if (sharedText != null) {
                if (linkValidator(sharedText) == "not_a_valid_youtube_link") {
                    badOrNullLinkAlert();
                } else if (sharedText != null) {
                    showGeneralInfoTutorial();
                    asyncDownload = new AsyncDownload();
                    asyncDownload.execute(validatedLink);
                }
            } else {
                badOrNullLinkAlert();
            }
        } else {
            progressBar1.setVisibility(View.GONE);
            tv.setText(getString(R.string.no_net));
            PopUps.showPopUp(getString(R.string.no_net), getString(R.string.no_net_dialog_msg), "alert", this);
            Button retry = (Button) findViewById(R.id.share_activity_retry_button);
            retry.setVisibility(View.VISIBLE);
            retry.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Utils.reload(ShareActivity.this);
                }
            });
        }
    }

    public void badOrNullLinkAlert() {
        progressBar1.setVisibility(View.GONE);
        tv.setText(getString(R.string.bad_link));
        PopUps.showPopUp(getString(R.string.error), getString(R.string.bad_link_dialog_msg), "alert", this);
    }

    void showGeneralInfoTutorial() {
        generalInfoCheckboxEnabled = settings.getBoolean("general_info", true);
        if (generalInfoCheckboxEnabled == true) {
            AlertDialog.Builder adb = new AlertDialog.Builder(boxThemeContextWrapper);
            LayoutInflater adbInflater = LayoutInflater.from(ShareActivity.this);
            View generalInfo = adbInflater.inflate(R.layout.dialog_general_info, null);
            generalInfoScrollview = (ScrollView) generalInfo.findViewById(R.id.generalInfoScrollview);

            showAgain1 = (CheckBox) generalInfo.findViewById(R.id.showAgain1);
            showAgain1.setChecked(true);
            adb.setView(generalInfo);
            adb.setTitle(getString(R.string.tutorial_title));
            //adb.setMessage(getString(R.string.tutorial_msg));

            adb.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    if (!showAgain1.isChecked()) {
                        SharedPreferences.Editor editor = settings.edit();
                        editor.putBoolean("general_info", false);
                        editor.commit();
                        sshInfoCheckboxEnabled = settings.getBoolean("general_info", true);
                        Utils.logger("d", "generalInfoCheckboxEnabled: " + generalInfoCheckboxEnabled, DEBUG_TAG);
                    }
                }
            });
            if (!((Activity) this).isFinishing()) {
                adb.show();
            }
        }
    }

    private String linkValidator(String sharedText) {
        Pattern pattern = Pattern.compile("(http://|https://).*(v=.{11}).*");
        Matcher matcher = pattern.matcher(sharedText);
        if (matcher.find()) {
            validatedLink = matcher.group(1) + "www.youtube.com/watch?" + matcher.group(2);
            videoId = matcher.group(2).replace("v=", "");
            return validatedLink;
        }
        return "not_a_valid_youtube_link";
    }

    public static void assignPath() {
        boolean Location = settings.getBoolean("swap_location", false);

        if (Location == false) {
            String location = settings.getString("standard_location", "Downloads");
            Utils.logger("d", "location: " + location, DEBUG_TAG);

            if (location.equals("DCIM") == true) {
                path = dir_DCIM;
            }
            if (location.equals("Movies") == true) {
                path = dir_Movies;
            }
            if (location.equals("Downloads") == true) {
                path = dir_Downloads;
            }

        } else {
            String cs = settings.getString("CHOOSER_FOLDER", "");
            chooserFolder = new File(cs);
            if (chooserFolder.exists()) {
                Utils.logger("d", "chooserFolder: " + chooserFolder, DEBUG_TAG);
                path = chooserFolder;
            } else {
                path = dir_Downloads;
                Utils.logger("w", "chooserFolder not found, falling back to Download path", DEBUG_TAG);
            }
        }

        if (!path.exists()) {
            if (new File(path.getAbsolutePath()).mkdirs()) {
                Utils.logger("w", "destination path not found, creating it now", DEBUG_TAG);
            } else {
                Log.e(DEBUG_TAG, "Something really bad happened with the download destination...");
            }

        }

        Utils.logger("d", "path: " + path, DEBUG_TAG);
    }

    private class AsyncDownload extends AsyncTask<String, Integer, String> {

        protected void onPreExecute() {
            isAsyncDownloadRunning = true;
            tv.setText(R.string.loading);
            progressBar1.setIndeterminate(true);
            progressBar1.setVisibility(View.VISIBLE);
        }

        protected String doInBackground(String... urls) {
            try {
                Utils.logger("d", "doInBackground...", DEBUG_TAG);

                if (settings.getBoolean("show_thumb", false)) {
                    downloadThumbnail(generateThumbUrl());
                }

                return downloadUrl(urls[0]);
            } catch (IOException e) {
                return "e";
            }
        }

        public void doProgress(int value) {
            publishProgress(value);
        }

        protected void onProgressUpdate(Integer... values) {
            progressBar1.setProgress(values[0]);
        }

        @Override
        protected void onPostExecute(String result) {

            progressBar1.setVisibility(View.GONE);

            if (settings.getBoolean("show_thumb", false)) {
                imgView.setImageBitmap(img);
            }
            isAsyncDownloadRunning = false;

            if (result == "e") {
                tv.setText(getString(R.string.invalid_url_short));
                PopUps.showPopUp(getString(R.string.error), getString(R.string.invalid_url), "alert",
                        ShareActivity.this);
                titleRaw = getString(R.string.invalid_response);
            }

            String[] lv_arr = listEntries.toArray(new String[0]);

            if (lv_arr.length == 0) {
                PopUps.showPopUp(getString(R.string.login_required), getString(R.string.login_required_dialog_msg),
                        "info", ShareActivity.this);
                TextView info = (TextView) findViewById(R.id.share_activity_info);
                info.setVisibility(View.VISIBLE);
            }

            aA = new ArrayAdapter<String>(ShareActivity.this, android.R.layout.simple_list_item_1, lv_arr);

            lv.setAdapter(aA);
            lv.setLongClickable(true);
            Utils.logger("d", "LISTview done with " + lv_arr.length + " items.", DEBUG_TAG);

            tv.setText(titleRaw);

            lv.setOnItemClickListener(new OnItemClickListener() {
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    //Utils.logger("i", "Selected link: " + links.get(pos), DEBUG_TAG);

                    assignPath();

                    //Utils.createLogFile(sdcard, "ytd_FINAL_LINK.txt", links.get(position));

                    pos = position;
                    //pos = 45;      // to test IndexOutOfBound Exception...

                    helpBuilder = new AlertDialog.Builder(boxThemeContextWrapper);
                    helpBuilder.setIcon(android.R.drawable.ic_dialog_info);
                    helpBuilder.setTitle(getString(R.string.list_click_dialog_title));

                    insertAudioConfirmation();

                    if (showSizesInVideoList) {
                        showSingleSize = true;
                    } else {
                        showSingleSize = settings.getBoolean("show_size", false);
                    }

                    try {
                        if (!showSingleSize) {
                            helpBuilder.setMessage(titleRaw + getString(R.string.codec) + " " + codecs.get(pos)
                                    + getString(R.string.quality) + " " + qualities.get(pos) + stereo.get(pos));
                        } else {
                            if (!showSizesInVideoList) {
                                sizeQuery = new AsyncSizeQuery();
                                sizeQuery.execute(links.get(position));
                            } else {
                                helpBuilder.setMessage(titleRaw + getString(R.string.codec) + " " + codecs.get(pos)
                                        + getString(R.string.quality) + " " + qualities.get(pos) + stereo.get(pos)
                                        + getString(R.string.size) + " " + sizes.get(pos));
                            }
                        }
                    } catch (IndexOutOfBoundsException e) {
                        Toast.makeText(ShareActivity.this, getString(R.string.video_list_error_toast),
                                Toast.LENGTH_SHORT).show();
                    }

                    helpBuilder.setPositiveButton(getString(R.string.list_click_download_local),
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int which) {
                                    try {
                                        fileRenameEnabled = settings.getBoolean("enable_rename", false);
                                        if (fileRenameEnabled == true) {
                                            AlertDialog.Builder adb = new AlertDialog.Builder(
                                                    boxThemeContextWrapper);
                                            LayoutInflater adbInflater = LayoutInflater.from(ShareActivity.this);
                                            View inputFilename = adbInflater.inflate(R.layout.dialog_input_filename,
                                                    null);
                                            userFilename = (TextView) inputFilename
                                                    .findViewById(R.id.input_filename);
                                            userFilename.setText(title);
                                            adb.setView(inputFilename);
                                            adb.setTitle(getString(R.string.rename_dialog_title));
                                            adb.setMessage(getString(R.string.rename_dialog_msg));
                                            adb.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                                                public void onClick(DialogInterface dialog, int which) {
                                                    title = userFilename.getText().toString();
                                                    composedVideoFilename = composeVideoFilename();
                                                    manageAudioFeature();
                                                    callDownloadManager(links.get(pos));
                                                }
                                            });

                                            adb.setNegativeButton(getString(R.string.dialogs_negative),
                                                    new DialogInterface.OnClickListener() {
                                                        public void onClick(DialogInterface dialog, int which) {
                                                            // cancel
                                                        }
                                                    });

                                            if (!((Activity) ShareActivity.this).isFinishing()) {
                                                adb.show();
                                            }
                                        } else {
                                            composedVideoFilename = composeVideoFilename();
                                            manageAudioFeature();
                                            callDownloadManager(links.get(pos));
                                        }
                                    } catch (IndexOutOfBoundsException e) {
                                        Toast.makeText(ShareActivity.this,
                                                getString(R.string.video_list_error_toast), Toast.LENGTH_SHORT)
                                                .show();
                                    }
                                }

                                public void manageAudioFeature() {
                                    audioExtractionEnabled = settings.getBoolean("enable_audio_extraction", false);
                                    if (audioExtractionEnabled == true) {
                                        audioCodec = findAudioCodec();
                                        settings.edit().putString(composedVideoFilename + "FFext", audioCodec)
                                                .apply();
                                    }
                                    settings.edit().putString(composedVideoFilename + "FFbase", title).apply();
                                }
                            });

                    // show central button for SSH send if enabled in prefs
                    if (!settings.getBoolean("ssh_to_longpress_menu", false)) {
                        helpBuilder.setNeutralButton(getString(R.string.list_click_download_ssh),
                                new DialogInterface.OnClickListener() {

                                    public void onClick(DialogInterface dialog, int which) {
                                        sendViaSsh();
                                    }
                                });
                    }

                    helpBuilder.setNegativeButton(getString(R.string.dialogs_negative),
                            new DialogInterface.OnClickListener() {

                                public void onClick(DialogInterface dialog, int which) {
                                    //Toast.makeText(ShareActivity.this, "Download canceled...", Toast.LENGTH_SHORT).show();
                                }
                            });

                    if ((!showSingleSize) || (showSizesInVideoList && showSingleSize)) {
                        helpDialog = helpBuilder.create();

                        if (!((Activity) ShareActivity.this).isFinishing()) {
                            helpDialog.show();
                        }
                    }
                }
            });

            lv.setOnItemLongClickListener(new OnItemLongClickListener() {

                @Override
                public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
                    pos = position;
                    AlertDialog.Builder builder = new AlertDialog.Builder(boxThemeContextWrapper);
                    if (!settings.getBoolean("ssh_to_longpress_menu", false)) {
                        builder.setTitle(R.string.long_click_title).setItems(R.array.long_click_entries,
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog, int which) {
                                        composedVideoFilename = composeVideoFilename();
                                        switch (which) {
                                        case 0: // copy
                                            copy(position);
                                            break;
                                        case 1: // share
                                            share(position);
                                        }
                                    }
                                });
                    } else {
                        builder.setTitle(R.string.long_click_title).setItems(R.array.long_click_entries2,
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog, int which) {
                                        composedVideoFilename = composeVideoFilename();
                                        switch (which) {
                                        case 0: // copy
                                            copy(position);
                                            break;
                                        case 1: // share
                                            share(position);
                                            break;
                                        case 2: // SSH
                                            sendViaSsh();
                                        }
                                    }
                                });
                    }
                    builder.create();
                    if (!((Activity) ShareActivity.this).isFinishing()) {
                        builder.show();
                    }
                    return true;
                }
            });
        }

        private void share(final int position) {
            Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
            sharingIntent.setType("text/plain");
            sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, composedVideoFilename);
            sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, links.get(position));
            startActivity(Intent.createChooser(sharingIntent, "Share YouTube link:"));
        }

        private void copy(final int position) {
            ClipData cmd = ClipData.newPlainText("simple text", links.get(position));
            ClipboardManager cb = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
            cb.setPrimaryClip(cmd);
        }

        private void insertAudioConfirmation() {
            boolean fromPrefs = settings.getBoolean("enable_audio_extraction", false);
            if (fromPrefs) {
                LayoutInflater adbInflater = LayoutInflater.from(ShareActivity.this);
                View handleAudio = adbInflater.inflate(R.layout.dialog_confirm_download, null);
                audioConfirm = (CheckBox) handleAudio.findViewById(R.id.audioConfirm);
                String text = getString(R.string.confirm_audio_checkbox1)
                        + getString(R.string.confirm_audio_checkbox2);
                audioConfirm.setText(text);
                audioConfirm.setChecked(false);
                helpBuilder.setView(handleAudio);
            }
        }

        private boolean useQualitySuffix() {
            boolean enabled = settings.getBoolean("enable_q_suffix", true);
            return enabled;
        }

        private boolean useAudioQualitySuffix() {
            boolean enabled = settings.getBoolean("enable_audio_q_suffix", true);
            return enabled;
        }

        private String composeVideoFilename() {
            if (useQualitySuffix()) {
                videoFilename = title + "_" + qualities.get(pos) + stereo.get(pos) + "." + codecs.get(pos);
            } else {
                videoFilename = title + stereo.get(pos) + "." + codecs.get(pos);
            }
            Utils.logger("d", "videoFilename: " + videoFilename, DEBUG_TAG);
            return videoFilename;
        }

        private String findAudioCodec() {
            //CODEC [file EXTENSION]
            extrType = settings.getString("audio_extraction_type", "extr");
            if (extrType.equals("conv")) {
                acodec = ".mp3";
            } else {
                if (codecs.get(pos).equals("webm"))
                    acodec = ".ogg";
                if (codecs.get(pos).equals("mp4"))
                    acodec = ".aac";
                if (codecs.get(pos).equals("flv") && qualities.get(pos).equals("small"))
                    acodec = ".mp3";
                if (codecs.get(pos).equals("flv") && qualities.get(pos).equals("medium"))
                    acodec = ".aac";
                if (codecs.get(pos).equals("flv") && qualities.get(pos).equals("large"))
                    acodec = ".aac";
                if (codecs.get(pos).equals("3gpp"))
                    acodec = ".aac";
            }
            //QUALITY
            if (useAudioQualitySuffix() && extrType.equals("conv")) {
                aquality = "_" + settings.getString("mp3_bitrate", "192k");
            } else {
                aquality = "";
            }
            //FINALLY
            return aquality + acodec;
        }

        private void callConnectBot() {
            Context context = getApplicationContext();
            PackageManager pm = context.getPackageManager();

            final String connectBotFlavour = settings.getString("connectbot_flavour", "org.connectbot");

            String connectBotFlavourPlain = "ConnectBot";
            if (connectBotFlavour.equals("sk.vx.connectbot"))
                connectBotFlavourPlain = "VX " + connectBotFlavourPlain;
            if (connectBotFlavour.equals("org.woltage.irssiconnectbot"))
                connectBotFlavourPlain = "Irssi " + connectBotFlavourPlain;

            Intent appStartIntent = pm.getLaunchIntentForPackage(connectBotFlavour);
            if (null != appStartIntent) {
                Utils.logger("d", "appStartIntent: " + appStartIntent, DEBUG_TAG);
                context.startActivity(appStartIntent);
            } else {
                AlertDialog.Builder cb = new AlertDialog.Builder(boxThemeContextWrapper);
                cb.setTitle(getString(R.string.callConnectBot_dialog_title, connectBotFlavourPlain));
                cb.setMessage(getString(R.string.callConnectBot_dialog_msg));
                icon = android.R.drawable.ic_dialog_alert;
                cb.setIcon(icon);
                cb.setPositiveButton(getString(R.string.callConnectBot_dialog_positive),
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                Intent intent = new Intent(Intent.ACTION_VIEW);
                                intent.setData(Uri.parse("market://details?id=" + connectBotFlavour));
                                try {
                                    startActivity(intent);
                                } catch (ActivityNotFoundException exception) {
                                    PopUps.showPopUp(getString(R.string.no_market),
                                            getString(R.string.no_net_dialog_msg), "alert", ShareActivity.this);
                                }
                            }
                        });
                cb.setNegativeButton(getString(R.string.dialogs_negative), new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        // Do nothing but close the dialog
                    }
                });

                AlertDialog helpDialog = cb.create();

                if (!((Activity) ShareActivity.this).isFinishing()) {
                    helpDialog.show();
                }
            }
        }

        private void sendViaSsh() {
            try {
                String wgetCmd;
                composedVideoFilename = composeVideoFilename();

                Boolean shortSshCmdEnabled = settings.getBoolean("enable_connectbot_short_cmd", false);
                if (shortSshCmdEnabled) {
                    wgetCmd = "wget -e \"convert-links=off\" --keep-session-cookies --save-cookies /dev/null --no-check-certificate \'"
                            + links.get(pos) + "\' -O " + composedVideoFilename;
                } else {
                    wgetCmd = "REQ=`wget -q -e \"convert-links=off\" --keep-session-cookies --save-cookies /dev/null --no-check-certificate \'"
                            + validatedLink
                            + "\' -O-` && urlblock=`echo $REQ | grep -oE \'url_encoded_fmt_stream_map\": \".*\' | sed -e \'s/\", \".*//\'"
                            + " -e \'s/url_encoded_fmt_stream_map\": \"//\'` && urlarray=( `echo $urlblock | sed \'s/,/\\n\\n/g\'` ) && N="
                            + pos
                            + " && block=`echo \"${urlarray[$N]}\" | sed -e \'s/%3A/:/g\' -e \'s/%2F/\\//g\' -e \'s/%3F/\\?/g\' -e \'s/%3D/\\=/g\'"
                            + " -e \'s/%252C/%2C/g\' -e \'s/%26/\\&/g\' -e \'s/%253A/\\:/g\' -e \'s/\", \"/\"-\"/\' -e \'s/sig=/signature=/\'"
                            + " -e \'s/x-flv/flv/\' -e \'s/\\\\\\u0026/\\&/g\'` && url=`echo $block | grep -oE \'http://.*\' | sed -e \'s/&type=.*//\'"
                            + " -e \'s/&signature=.*//\' -e \'s/&quality=.*//\' -e \'s/&fallback_host=.*//\'` && sig=`echo $block | "
                            + "grep -oE \'signature=.{81}\'` && downloadurl=`echo $url\\&$sig | sed \'s/&itag=[0-9][0-9]&signature/\\&signature/\'` && "
                            + "wget -e \"convert-links=off\" --keep-session-cookies --save-cookies /dev/null --tries=5 --timeout=45 --no-check-certificate "
                            + "\"$downloadurl\" -O " + composedVideoFilename;
                }

                Utils.logger("d", "wgetCmd: " + wgetCmd, DEBUG_TAG);

                ClipData cmd = ClipData.newPlainText("simple text", wgetCmd);
                ClipboardManager cb = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
                cb.setPrimaryClip(cmd);

                sshInfoCheckboxEnabled = settings.getBoolean("ssh_info", true);
                if (sshInfoCheckboxEnabled == true) {
                    AlertDialog.Builder adb = new AlertDialog.Builder(boxThemeContextWrapper);
                    LayoutInflater adbInflater = LayoutInflater.from(ShareActivity.this);
                    View sshInfo = adbInflater.inflate(R.layout.dialog_ssh_info, null);
                    showAgain2 = (CheckBox) sshInfo.findViewById(R.id.showAgain2);
                    showAgain2.setChecked(true);
                    adb.setView(sshInfo);
                    adb.setTitle(getString(R.string.ssh_info_tutorial_title));
                    adb.setMessage(getString(R.string.ssh_info_tutorial_msg));
                    adb.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            if (!showAgain2.isChecked()) {
                                SharedPreferences.Editor editor = settings.edit();
                                editor.putBoolean("ssh_info", false);
                                editor.apply();
                                sshInfoCheckboxEnabled = settings.getBoolean("ssh_info", true);
                                Utils.logger("d", "sshInfoCheckboxEnabled: " + sshInfoCheckboxEnabled, DEBUG_TAG);
                            }
                            callConnectBot();
                        }
                    });
                    if (!((Activity) ShareActivity.this).isFinishing()) {
                        adb.show();
                    }
                } else {
                    callConnectBot();
                }
            } catch (IndexOutOfBoundsException e) {
                Toast.makeText(ShareActivity.this, getString(R.string.video_list_error_toast), Toast.LENGTH_SHORT)
                        .show();
            }
        }
    }

    private void callDownloadManager(String link) {
        videoUri = Uri.parse(path.toURI() + composedVideoFilename);
        Utils.logger("d", "videoUri: " + videoUri, DEBUG_TAG);

        dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        Request request = null;
        try {
            request = new Request(Uri.parse(link));
            request.setDestinationUri(videoUri);
            request.allowScanningByMediaScanner();
            request.setVisibleInDownloadsUi(false);

            String visValue = settings.getString("download_manager_notification", "VISIBLE");
            int vis;
            if (visValue.equals("VISIBLE_NOTIFY_COMPLETED")) {
                vis = DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
            } else if (visValue.equals("HIDDEN")) {
                vis = DownloadManager.Request.VISIBILITY_HIDDEN;
            } else {
                vis = DownloadManager.Request.VISIBILITY_VISIBLE;
            }
            request.setNotificationVisibility(vis);
            request.setTitle(videoFilename);
            request.setDescription(getString(R.string.ytd_video));
        } catch (IllegalArgumentException e) {
            Log.e(DEBUG_TAG, "callDownloadManager: " + e.getMessage());
            YTD.NoDownProvPopUp(this);
            BugSenseHandler.sendExceptionMessage(DEBUG_TAG + "-> callDownloadManager: ", e.getMessage(), e);
        }

        Intent intent1 = new Intent(ShareActivity.this, DownloadsService.class);
        intent1.putExtra("COPY", false);

        audioExtrEnabled = settings.getBoolean("enable_audio_extraction", false);
        if (audioExtractionEnabled && audioConfirm.isChecked()) {
            intent1.putExtra("AUDIO", extrType);
        } else {
            intent1.putExtra("AUDIO", "none");
        }

        try {
            try {
                enqueue = dm.enqueue(request);
            } catch (IllegalArgumentException e) {
                Log.e(DEBUG_TAG, "callDownloadManager: " + e.getMessage());
                YTD.NoDownProvPopUp(this);
                BugSenseHandler.sendExceptionMessage(DEBUG_TAG + "-> callDownloadManager: ", e.getMessage(), e);
            } catch (NullPointerException ne) {
                Log.e(DEBUG_TAG, "callDownloadApk: " + ne.getMessage());
                BugSenseHandler.sendExceptionMessage(DEBUG_TAG + "-> callDownloadApk: ", ne.getMessage(), ne);
                Toast.makeText(this, getString(R.string.error), Toast.LENGTH_LONG).show();
            }
            Utils.logger("d", "_ID " + enqueue + " enqueued", DEBUG_TAG);
        } catch (SecurityException e) {
            // handle path on etxSdCard:
            Utils.logger("w", e.getMessage(), DEBUG_TAG);
            showExtsdcardInfo();
            intent1.putExtra("COPY", true);
            videoOnExt = true;
            tempDownloadToSdcard(request);
        }

        startService(intent1);

        settings.edit().putString(String.valueOf(enqueue), composedVideoFilename).apply();

        if (settings.getBoolean("enable_own_notification", true) == true) {
            Utils.logger("i", "enable_own_notification: true", DEBUG_TAG);
            sequence.add(enqueue);
            settings.edit().putLong(composedVideoFilename, enqueue).apply();

            if (videoOnExt == true) {
                videoFileObserver = new Observer.YtdFileObserver(dir_Downloads.getAbsolutePath());
            } else {
                videoFileObserver = new Observer.YtdFileObserver(path.getAbsolutePath());
            }
            videoFileObserver.startWatching();

            //NotificationHelper();
        }
    }

    private String generateThumbUrl() {
        // link example "http://i2.ytimg.com/vi/8wr-uQX1Grw/mqdefault.jpg"
        Random random = new Random();
        int num = random.nextInt(4 - 1) + 1;
        String url = "http://i" + num + ".ytimg.com/vi/" + videoId + "/mqdefault.jpg";
        Utils.logger("i", "thumbnail url: " + url, DEBUG_TAG);
        return url;
    }

    private void showExtsdcardInfo() {
        generalInfoCheckboxEnabled = settings.getBoolean("extsdcard_info", true);
        if (generalInfoCheckboxEnabled == true) {
            AlertDialog.Builder adb = new AlertDialog.Builder(boxThemeContextWrapper);
            LayoutInflater adbInflater = LayoutInflater.from(ShareActivity.this);
            View generalInfo = adbInflater.inflate(R.layout.dialog_extsdcard_info, null);
            showAgain3 = (CheckBox) generalInfo.findViewById(R.id.showAgain3);
            showAgain3.setChecked(true);
            adb.setView(generalInfo);
            adb.setTitle(getString(R.string.extsdcard_info_title));
            adb.setMessage(getString(R.string.extsdcard_info_msg));

            adb.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    if (showAgain3.isChecked() == false) {
                        settings.edit().putBoolean("extsdcard_info", false).commit();
                        sshInfoCheckboxEnabled = settings.getBoolean("extsdcard_info", true);
                        Utils.logger("d", "generalInfoCheckboxEnabled: " + generalInfoCheckboxEnabled, DEBUG_TAG);
                    }
                }
            });
            if (!((Activity) ShareActivity.this).isFinishing()) {
                adb.show();
            }
        }
    }

    private void tempDownloadToSdcard(Request request) {
        videoUri = Uri.parse(dir_Downloads.toURI() + composedVideoFilename);
        Utils.logger("d", "** NEW ** videoUri: " + videoUri, DEBUG_TAG);
        request.setDestinationUri(videoUri);
        try {
            enqueue = dm.enqueue(request);
        } catch (IllegalArgumentException e) { // probably useless try/catch
            Log.e(DEBUG_TAG, "tempDownloadToSdcard: " + e.getMessage());
            BugSenseHandler.sendExceptionMessage(DEBUG_TAG + "-> tempDownloadToSdcard", e.getMessage(), e);
        } catch (NullPointerException ne) {
            Log.e(DEBUG_TAG, "callDownloadApk: " + ne.getMessage());
            BugSenseHandler.sendExceptionMessage(DEBUG_TAG + "-> callDownloadApk: ", ne.getMessage(), ne);
            Toast.makeText(this, getString(R.string.error), Toast.LENGTH_LONG).show();
        }
    }

    public static void NotificationHelper() {
        pt1 = mContext.getString(R.string.notification_downloading_pt1);
        pt2 = mContext.getString(R.string.notification_downloading_pt2);
        noDownloads = mContext.getString(R.string.notification_no_downloads);

        mBuilder = new NotificationCompat.Builder(mContext);

        mBuilder.setSmallIcon(R.drawable.ic_stat_ytd)
                .setContentTitle(mContext.getString(R.string.title_activity_share))
                .setContentText(mContext.getString(R.string.notification_downloading_pt1) + " " + sequence.size()
                        + " " + mContext.getString(R.string.notification_downloading_pt2));

        mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);

        Intent notificationIntent = new Intent(android.app.DownloadManager.ACTION_VIEW_DOWNLOADS);
        if (notificationIntent.resolveActivity(mContext.getPackageManager()) != null) {
            notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        } else {
            Log.e(DEBUG_TAG, "notificationIntent not resolved");
        }
        PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0);
        mBuilder.setContentIntent(contentIntent);
        mId = 1;
        mNotificationManager.notify(mId, mBuilder.build());
    }

    // Given a URL, establishes an HttpUrlConnection and retrieves
    // the web page content as a InputStream, which it returns as a string.
    private String downloadUrl(String myurl) throws IOException {
        InputStream is = null;
        // Only display the first "len" characters of the retrieved web page content.
        int len = 2000000;
        Utils.logger("d", "The link is: " + myurl, DEBUG_TAG);
        if (!asyncDownload.isCancelled()) {
            try {
                URL url = new URL(myurl);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestProperty("User-Agent", "<em>" + USER_AGENT_FIREFOX + "</em>");
                conn.setReadTimeout(20000 /* milliseconds */);
                conn.setConnectTimeout(30000 /* milliseconds */);
                conn.setInstanceFollowRedirects(false);
                conn.setRequestMethod("GET");
                conn.setDoInput(true);
                //Starts the query
                conn.connect();
                int response = conn.getResponseCode();
                Utils.logger("d", "The response is: " + response, DEBUG_TAG);
                is = conn.getInputStream();

                //Convert the InputStream into a string
                if (!asyncDownload.isCancelled()) {
                    return readIt(is, len);
                } else {
                    Utils.logger("d", "asyncDownload cancelled @ 'return readIt'", DEBUG_TAG);
                    return null;
                }

                //Makes sure that the InputStream is closed after the app is finished using it.
            } finally {
                if (is != null) {
                    is.close();
                }
            }
        } else {
            Utils.logger("d", "asyncDownload cancelled @ 'downloadUrl' begin", DEBUG_TAG);
            return null;
        }
    }

    // Reads an InputStream and converts it to a String.
    private String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
        Reader reader = null;
        reader = new InputStreamReader(stream, "UTF-8");
        char[] buffer = new char[len];
        reader.read(buffer);
        String content = new String(buffer);
        return urlBlockMatchAndDecode(content);
    }

    private String urlBlockMatchAndDecode(String content) {

        if (asyncDownload.isCancelled()) {
            Utils.logger("d", "asyncDownload cancelled @ urlBlockMatchAndDecode begin", DEBUG_TAG);
            return "Cancelled!";
        }

        findVideoFilenameBase(content);

        Pattern pattern = Pattern.compile("url_encoded_fmt_stream_map\\\": \\\"(.*?)\\\"");
        Matcher matcher = pattern.matcher(content);
        if (matcher.find()) {
            Pattern blockPattern = Pattern.compile(",");
            Matcher blockMatcher = blockPattern.matcher(matcher.group(1));
            if (blockMatcher.find() && !asyncDownload.isCancelled()) {
                String[] CQS = matcher.group(1).split(blockPattern.toString());
                count = (CQS.length - 1);
                Utils.logger("d", "number of entries found: " + count, DEBUG_TAG);
                int index = 0;
                progressBar1.setIndeterminate(false);
                while ((index + 1) < CQS.length) {
                    try {
                        CQS[index] = URLDecoder.decode(CQS[index], "UTF-8");
                    } catch (UnsupportedEncodingException e) {
                        Log.e(DEBUG_TAG, e.getMessage());
                    }

                    asyncDownload.doProgress((int) ((index / (float) count) * 100));

                    codecMatcher(CQS[index], index);
                    qualityMatcher(CQS[index], index);
                    stereoMatcher(CQS[index], index);
                    linkComposer(CQS[index], index);
                    Utils.logger("v", "block " + index + ": " + CQS[index], DEBUG_TAG);
                    index++;
                }
                listEntriesBuilder();
            } else {
                Utils.logger("d", "asyncDownload cancelled @ 'findCodecAndQualityAndLinks' match", DEBUG_TAG);
            }

            /*
            Utils.createLogFile(sdcard, "ytd_links.txt", Arrays.toString(links.toArray()));
            Utils.createLogFile(sdcard, "ytd_codecs.txt", Arrays.toString(codecs.toArray()));
            Utils.createLogFile(sdcard, "ytd_qualities.txt", Arrays.toString(qualities.toArray()));
            */

            return "Match!";
        } else {
            return "No Match";
        }
    }

    private void findVideoFilenameBase(String content) {
        Pattern titlePattern = Pattern.compile("<title>(.*?)</title>");
        Matcher titleMatcher = titlePattern.matcher(content);
        if (titleMatcher.find()) {
            titleRaw = titleMatcher.group().replaceAll("(<| - YouTube</)title>", "").replaceAll("&quot;", "\"")
                    .replaceAll("&amp;", "&").replaceAll("&#39;", "'");
            title = titleRaw.replaceAll("\\W", "_");
        } else {
            title = "Youtube Video";
        }
        Utils.logger("d", "findVideoFilenameBase: " + title, DEBUG_TAG);
    }

    private void listEntriesBuilder() {
        Iterator<String> codecsIter = codecs.iterator();
        Iterator<String> qualitiesIter = qualities.iterator();
        Iterator<String> stereoIter = stereo.iterator();
        Iterator<String> sizesIter = sizes.iterator();

        if (settings.getBoolean("show_size_list", false)) {
            while (codecsIter.hasNext()) {
                try {
                    listEntries.add(codecsIter.next().toUpperCase(Locale.ENGLISH).replace("WEBM", "WebM") + " - "
                            + qualitiesIter.next() + stereoIter.next() + " - " + sizesIter.next());
                } catch (NoSuchElementException e) {
                    listEntries.add("//");
                }
            }
        } else {
            while (codecsIter.hasNext()) {
                try {
                    listEntries.add(codecsIter.next().toUpperCase(Locale.ENGLISH).replace("WEBM", "WebM") + " - "
                            + qualitiesIter.next() + stereoIter.next());
                } catch (NoSuchElementException e) {
                    listEntries.add("//");
                }
            }

        }
    }

    private void linkComposer(String block, int i) {
        Pattern urlPattern = Pattern.compile("url=(.+?)\\\\u0026");
        Matcher urlMatcher = urlPattern.matcher(block);
        String url = null;
        if (urlMatcher.find()) {
            url = urlMatcher.group(1);
        } else {
            Pattern urlPattern2 = Pattern.compile("url=(.+?)$");
            Matcher urlMatcher2 = urlPattern2.matcher(block);
            if (urlMatcher2.find()) {
                url = urlMatcher2.group(1);
            } else {
                Log.e(DEBUG_TAG, "url: " + url);
            }
        }

        Pattern sigPattern = Pattern.compile("sig=(.+?)\\\\u0026");
        Matcher sigMatcher = sigPattern.matcher(block);
        String sig = null;
        if (sigMatcher.find()) {
            sig = "signature=" + sigMatcher.group(1);
            Utils.logger("d", "sig found on step 1 (\u0026)", DEBUG_TAG);
        } else {
            Pattern sigPattern2 = Pattern.compile("sig=(.+?)$");
            Matcher sigMatcher2 = sigPattern2.matcher(block);
            if (sigMatcher2.find()) {
                sig = "signature=" + sigMatcher2.group(1);
                Utils.logger("d", "sig found on step 2 ($)", DEBUG_TAG);
            } else {
                Pattern sigPattern3 = Pattern.compile("sig=([[0-9][A-Z]]{39,40}\\.[[0-9][A-Z]]{39,40})");
                Matcher sigMatcher3 = sigPattern3.matcher(block);
                if (sigMatcher3.find()) {
                    sig = "signature=" + sigMatcher3.group(1);
                    Utils.logger("d", "sig found on step 3 ([[0-9][A-Z]]{39,40})", DEBUG_TAG);
                } else {
                    Pattern sigPattern4 = Pattern.compile("s=([[0-9][A-Z]]{40,44}\\.[[0-9][A-Z]]{40,44})");
                    Matcher sigMatcher4 = sigPattern4.matcher(block);
                    if (sigMatcher4.find()) {
                        Utils.logger("d", "sig found on step 4 (s=)", DEBUG_TAG);
                        Log.i(DEBUG_TAG, "(s=) signature length: " + sigMatcher4.group(1).length());
                        String singleEs = parseSingleEsSig(sigMatcher4.group(1));
                        sig = "signature=" + singleEs;
                    } else {
                        Log.e(DEBUG_TAG, "sig: " + sig);
                    }
                }
            }
        }

        Utils.logger("d", "url " + i + ": " + url, DEBUG_TAG);
        Utils.logger("d", "sig " + i + ": " + sig, DEBUG_TAG);

        String composedLink = url + "&" + sig;

        links.add(composedLink);
        //Utils.logger("i", composedLink);
        if (settings.getBoolean("show_size_list", false) && !asyncDownload.isCancelled()) {
            String size = getVideoFileSize(composedLink);
            sizes.add(size);
            Utils.logger("d", "size " + i + ": " + size, DEBUG_TAG);
        }
    }

    /*
     * parseSingleEsSig(...) and initialSigTransformation(...) methods
     * adapted from the Javascript Greasemonkey script 
     * http://userscripts.org/scripts/show/25105 (released under the MIT License)
     * by Gantt: http://userscripts.org/users/gantt
     */

    private String parseSingleEsSig(String sig) {
        if (sig.length() == 88) {
            String[] sigA = sig.split("");
            sigA = Arrays.copyOfRange(sigA, 2, sigA.length);
            sigA = swap(sigA, 1);
            sigA = swap(sigA, 10);
            sigA = reverseArray(sigA);
            sigA = Arrays.copyOfRange(sigA, 2, sigA.length);
            sigA = swap(sigA, 23);
            sigA = Arrays.copyOfRange(sigA, 3, sigA.length);
            sigA = swap(sigA, 15);
            sigA = swap(sigA, 34);
            sig = TextUtils.join("", sigA);
        }

        if (sig.length() == 87) {
            String[] t = initialSigTransformation(sig, 44, 84, 3, 43);
            sig = t[0].substring(21, 22) + t[0].substring(1, 21) + t[0].substring(0, 1) + t[1].substring(22, 31)
                    + sig.substring(0, 1) + t[0].substring(32, 40) + sig.substring(43, 44) + t[1];
        }

        if (sig.length() == 86) {
            String sigA = sig.substring(2, 42);
            String sigB = sig.substring(43, 83);
            sig = sigA + sig.substring(42, 43) + sigB.substring(0, 20) + sigB.substring(39, 40)
                    + sigB.substring(21, 39) + sigB.substring(20, 21);
        }

        if (sig.length() == 85) {
            String[] t = initialSigTransformation(sig, 44, 84, 3, 43);
            sig = t[0].substring(7, 8) + t[0].substring(1, 7) + t[0].substring(0, 1) + t[0].substring(8, 23)
                    + sig.substring(0, 1) + t[0].substring(24, 33) + sig.substring(1, 2) + t[0].substring(34, 40)
                    + sig.substring(43, 44) + t[1];
        }

        if (sig.length() == 84) {
            String[] t = initialSigTransformation(sig, 44, 84, 3, 43);
            sig = t[0] + sig.substring(43, 44) + t[1].substring(0, 6) + sig.substring(2, 3) + t[1].substring(7, 16)
                    + t[1].substring(39, 40) + t[1].substring(17, 39) + t[1].substring(16, 17);
        }

        if (sig.length() == 83) {
            String[] t = initialSigTransformation(sig, 43, 83, 2, 42);
            sig = t[0].substring(30, 31) + t[0].substring(1, 27) + t[1].substring(39, 40) + t[0].substring(28, 30)
                    + t[0].substring(0, 1) + t[0].substring(31, 40) + sig.substring(42, 43) + t[1].substring(0, 5)
                    + t[0].substring(27, 28) + t[1].substring(6, 39) + t[1].substring(5, 6);
        }

        if (sig.length() == 82) {
            String[] t = initialSigTransformation(sig, 34, 82, 0, 33);
            sig = t[0].substring(45, 46) + t[0].substring(2, 14) + t[0].substring(0, 1) + t[0].substring(15, 41)
                    + sig.substring(33, 34) + t[0].substring(42, 43) + t[0].substring(43, 44)
                    + t[0].substring(44, 45) + t[0].substring(41, 42) + t[0].substring(46, 47)
                    + t[1].substring(32, 33) + t[0].substring(14, 15) + t[1].substring(0, 32)
                    + t[0].substring(47, 48);
        }
        return sig;
    }

    private String[] initialSigTransformation(String sig, int a, int b, int c, int d) {
        String[] sigA = sig.substring(a, b).split("");
        sigA = reverseArray(sigA);
        String sigAs = TextUtils.join("", sigA);
        String[] sigB = sig.substring(c, d).split("");
        sigB = reverseArray(sigB);
        String sigBs = TextUtils.join("", sigB);
        return new String[] { sigAs, sigBs };
    }

    /*
      * method reverseArray(String[] a) adapted from Stack Overflow:
     * http://stackoverflow.com/questions/13674466/reverse-the-contents-of-array
     * 
     * Q: http://stackoverflow.com/users/1871089/user1871089
     * A: http://stackoverflow.com/users/1870638/andreih
     */

    public static String[] reverseArray(String[] a) {
        int i = 0;
        int j = a.length - 1;
        for (i = 0; i < a.length / 2; i++, j--) {
            String temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
        return a;
    }

    private String[] swap(String[] a, int b) {
        String c = a[0];
        a[0] = a[b % a.length];
        a[b] = c;
        return a;
    }

    private class AsyncSizeQuery extends AsyncTask<String, Void, String> {

        protected void onPreExecute() {
            waitBuilder = new AlertDialog.Builder(boxThemeContextWrapper);
            LayoutInflater adbInflater = LayoutInflater.from(ShareActivity.this);
            View barView = adbInflater.inflate(R.layout.wait_for_filesize, null);
            filesizeProgressBar = (ProgressBar) barView.findViewById(R.id.filesizeProgressBar);
            filesizeProgressBar.setVisibility(View.VISIBLE);
            waitBuilder.setView(barView);
            waitBuilder.setIcon(android.R.drawable.ic_dialog_info);
            waitBuilder.setTitle(R.string.wait);
            waitBuilder.setMessage(titleRaw + getString(R.string.codec) + " " + codecs.get(pos)
                    + getString(R.string.quality) + " " + qualities.get(pos) + stereo.get(pos));
            waitBox = waitBuilder.create();
            if (!((Activity) ShareActivity.this).isFinishing()) {
                waitBox.show();
            }
        }

        protected String doInBackground(String... urls) {
            // params comes from the execute() call: params[0] is the url.
            return getVideoFileSize(urls[0]);
        }

        @Override
        protected void onPostExecute(String result) {

            filesizeProgressBar.setVisibility(View.GONE);
            if (!((Activity) ShareActivity.this).isFinishing()) {
                waitBox.dismiss();
            }

            videoFileSize = result;
            Utils.logger("d", "size " + pos + ": " + result, DEBUG_TAG);
            helpBuilder.setMessage(titleRaw + getString(R.string.codec) + " " + codecs.get(pos)
                    + getString(R.string.quality) + " " + qualities.get(pos) + stereo.get(pos)
                    + getString(R.string.size) + " " + videoFileSize);
            helpDialog = helpBuilder.create();
            if (!((Activity) ShareActivity.this).isFinishing()) {
                helpDialog.show();
            }
        }
    }

    private String getVideoFileSize(String link) {
        String size;
        try {
            final URL uri = new URL(link);
            HttpURLConnection ucon = (HttpURLConnection) uri.openConnection();
            ucon.connect();
            int file_size = ucon.getContentLength();
            size = MakeSizeHumanReadable(file_size, true);
        } catch (IOException e) {
            size = "n.a.";
        }
        return size;
    }

    /*
     * method MakeSizeHumanReadable(int bytes, boolean si) from Stack Overflow:
    * http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
    * 
    * Q: http://stackoverflow.com/users/404615/iimuhin
    * A: http://stackoverflow.com/users/276052/aioobe
    */

    @SuppressLint("DefaultLocale")
    private String MakeSizeHumanReadable(int bytes, boolean si) {
        String hr;
        int unit = si ? 1000 : 1024;
        if (bytes < unit) {
            hr = bytes + " B";
        } else {
            int exp = (int) (Math.log(bytes) / Math.log(unit));
            String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");
            hr = String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
        }
        hr = hr.replace("-1 B", "n.a.");
        return hr;
    }

    private void codecMatcher(String currentCQ, int i) {
        Pattern codecPattern = Pattern.compile("(webm|mp4|flv|3gpp)");
        Matcher codecMatcher = codecPattern.matcher(currentCQ);
        if (codecMatcher.find()) {
            codecs.add(codecMatcher.group());
        } else {
            codecs.add("NoMatch");
        }
        //Utils.logger("d", "CQ index: " + i + ", Codec: " + codecs.get(i), DEBUG_TAG);
    }

    private void qualityMatcher(String currentCQ, int i) {
        Pattern qualityPattern = Pattern.compile("(highres|hd1080|hd720|large|medium|small)");
        Matcher qualityMatcher = qualityPattern.matcher(currentCQ);
        if (qualityMatcher.find()) {
            qualities.add(qualityMatcher.group().replace("highres", "4K"));
        } else {
            qualities.add("NoMatch");
        }
        //Utils.logger("d", "CQ index: " + i + ", Quality: " + qualities.get(i), DEBUG_TAG);
    }

    private void stereoMatcher(String currentCQ, int i) {
        Pattern qualityPattern = Pattern.compile("stereo3d=1");
        Matcher qualityMatcher = qualityPattern.matcher(currentCQ);
        if (qualityMatcher.find()) {
            stereo.add(qualityMatcher.group().replace("stereo3d=1", "_3D"));

        } else {
            stereo.add("");
        }
        //Utils.logger("d", "CQ index: " + i + ", Quality: " + qualities.get(i), DEBUG_TAG);
    }

    private void downloadThumbnail(String fileUrl) {
        InputStream is = null;
        URL myFileUrl = null;
        try {
            myFileUrl = new URL(fileUrl);
        } catch (MalformedURLException e) {
            try {
                myFileUrl = new URL(
                        "https://raw.github.com/dentex/ytdownloader/master/dentex.youtube.downloader/assets/placeholder.png");
            } catch (MalformedURLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
        try {
            HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection();
            conn.setDoInput(true);
            conn.connect();
            is = conn.getInputStream();

            img = BitmapFactory.decodeStream(is);
        } catch (IOException e) {
            InputStream assIs = null;
            AssetManager assMan = getAssets();
            try {
                assIs = assMan.open("placeholder.png");
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            img = BitmapFactory.decodeStream(assIs);
        }
    }

    private void updateInit() {
        int prefSig = settings.getInt("APP_SIGNATURE", 0);
        Utils.logger("d", "prefSig: " + prefSig, DEBUG_TAG);

        if (prefSig == SettingsActivity.SettingsFragment.YTD_SIG_HASH) {
            Utils.logger("d", "YTD signature in PREFS: update check possile", DEBUG_TAG);

            if (settings.getBoolean("autoupdate", false)) {
                Utils.logger("i", "autoupdate enabled", DEBUG_TAG);
                SettingsActivity.SettingsFragment.autoUpdate(ShareActivity.this);
            }
        } else {
            Utils.logger("d", "different or null YTD signature. Update check cancelled.", DEBUG_TAG);
        }
    }

    BroadcastReceiver inAppCompleteReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            //Utils.logger("d", "inAppCompleteReceiver: onReceive CALLED", DEBUG_TAG);
            long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -2);

            boolean audioConfirmed = false;
            if (audioConfirm != null && audioConfirm.isChecked()) {
                audioConfirmed = true;
            }

            if (enqueue != -1 && id != -2 && id == enqueue && !videoOnExt && !audioConfirmed) {
                Query query = new Query();
                query.setFilterById(id);
                Cursor c = dm.query(query);
                if (c.moveToFirst()) {
                    int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
                    int status = c.getInt(columnIndex);
                    if (status == DownloadManager.STATUS_SUCCESSFUL) {
                        AlertDialog.Builder helpBuilder = new AlertDialog.Builder(boxThemeContextWrapper);
                        helpBuilder.setIcon(android.R.drawable.ic_dialog_info);
                        helpBuilder.setTitle(getString(R.string.information));
                        helpBuilder.setMessage(getString(R.string.download_complete_dialog_msg1) + titleRaw
                                + getString(R.string.download_complete_dialog_msg2));
                        helpBuilder.setPositiveButton(getString(R.string.download_complete_dialog_positive),
                                new DialogInterface.OnClickListener() {

                                    public void onClick(DialogInterface dialog, int which) {

                                        Intent v_intent = new Intent();
                                        v_intent.setAction(android.content.Intent.ACTION_VIEW);
                                        v_intent.setDataAndType(videoUri, "video/*");
                                        startActivity(v_intent);
                                    }
                                });

                        helpBuilder.setNegativeButton(getString(R.string.dialogs_negative),
                                new DialogInterface.OnClickListener() {

                                    public void onClick(DialogInterface dialog, int which) {
                                        // cancel
                                    }
                                });

                        AlertDialog helpDialog = helpBuilder.create();
                        if (!((Activity) context).isFinishing()) {
                            helpDialog.show();
                        }
                    }
                }
            }
        }
    };
}