com.miz.functions.MizLib.java Source code

Java tutorial

Introduction

Here is the source code for com.miz.functions.MizLib.java

Source

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

package com.miz.functions;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;

import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.json.JSONArray;
import org.json.JSONObject;

import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.StatFs;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.widget.ExpandableListView;
import android.widget.FrameLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.miz.db.DbAdapterSources;
import com.miz.db.DbAdapterTvShow;
import com.miz.db.DbAdapterTvShowEpisode;
import com.miz.mizuu.MizuuApplication;
import com.miz.mizuu.R;
import com.miz.mizuu.TvShow;
import com.miz.mizuu.TvShowEpisode;
import com.miz.mizuu.fragments.ScheduledUpdatesFragment;
import com.miz.service.MakeAvailableOffline;
import com.miz.service.MovieLibraryUpdate;
import com.miz.service.TvShowsLibraryUpdate;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.apache.OkApacheClient;

@SuppressLint("NewApi")
public class MizLib {

    public static final String TYPE = "type";
    public static final String MOVIE = "movie";
    public static final String TV_SHOW = "tvshow";
    public static final String FILESOURCE = "filesource";
    public static final String USER = "user";
    public static final String PASSWORD = "password";
    public static final String DOMAIN = "domain";
    public static final String SERVER = "server";
    public static final String SERIAL_NUMBER = "serial_number";

    public static final String tvdbLanguages = "en,sv,no,da,fi,nl,de,it,es,fr,pl,hu,el,tr,ru,he,ja,pt,zh,cs,sl,hr,ko";
    public static final String allFileTypes = ".3gp.aaf.mp4.ts.webm.m4v.mkv.divx.xvid.rec.avi.flv.f4v.moi.mpeg.mpg.mts.m2ts.ogv.rm.rmvb.mov.wmv.iso.vob.ifo.wtv.pyv.ogm.img";
    public static final String IMAGE_CACHE_DIR = "thumbs";
    public static final String altOfflinePath = "/Removable/MicroSD/mizuu";
    public static final String TMDB_BASE_URL = "http://image.tmdb.org/t/p/";
    public static final String TMDB_API = "8f5f9f44983b8af692aae5f9974500f8";
    public static final String TVDBAPI = "1CB9725D261FAF38";
    public static final String YOUTUBE_API = "AIzaSyACKcfmngguy_PhREycetiispyMZ4fLPDY";
    public static final String TRAKT_API = "b85f6110fd2522022bc53614965415bf";
    public static final String CHARACTER_REGEX = "[^\\w\\s]";
    public static final String[] prefixes = new String[] { "the ", "a ", "an " };

    public static final int SECOND = 1000;
    public static final int MINUTE = 60 * SECOND;
    public static final int HOUR = 60 * MINUTE;
    public static final int DAY = 24 * HOUR;
    public static final int WEEK = 7 * DAY;

    public static String[] getPrefixes(Context c) {
        ArrayList<String> prefixesArray = new ArrayList<String>();
        String prefix = c.getString(R.string.prefixes);

        String[] split = prefix.split(",");
        int count = split.length;
        for (int i = 0; i < count; i++)
            prefixesArray.add(split[i]);

        count = prefixes.length;
        for (int i = 0; i < count; i++)
            prefixesArray.add(prefixes[i]);

        return prefixesArray.toArray(new String[] {});
    }

    public static boolean isVideoFile(String s) {
        String[] fileTypes = new String[] { ".3gp", ".aaf.", "mp4", ".ts", ".webm", ".m4v", ".mkv", ".divx",
                ".xvid", ".rec", ".avi", ".flv", ".f4v", ".moi", ".mpeg", ".mpg", ".mts", ".m2ts", ".ogv", ".rm",
                ".rmvb", ".mov", ".wmv", ".iso", ".vob", ".ifo", ".wtv", ".pyv", ".ogm", ".img" };
        int count = fileTypes.length;
        for (int i = 0; i < count; i++)
            if (s.endsWith(fileTypes[i]))
                return true;
        return false;
    }

    /**
     * Converts the first character of a String to upper case.
     * @param s (input String)
     * @return Input String with first character in upper case.
     */
    public static String toCapitalFirstChar(String s) {
        if (s == null || s.isEmpty())
            return "";
        return s.substring(0, 1).toUpperCase(Locale.ENGLISH) + s.substring(1, s.length());
    }

    /**
     * Converts the first character of all words (separated by space)
     * in the String to upper case.
     * @param s (input String)
     * @return Input string with first character of all words in upper case.
     */
    public static String toCapitalWords(String s) {
        if (s == null || s.isEmpty())
            return "";

        StringBuilder result = new StringBuilder();
        String[] split = s.split("\\s");
        int count = split.length;
        for (int i = 0; i < count; i++)
            result.append(toCapitalFirstChar(split[i]) + " ");
        return result.toString().trim();
    }

    /**
     * Adds spaces between capital characters.
     * @param s (input String)
     * @return Input string with spaces between capital characters.
     */
    public static String addSpaceByCapital(String s) {
        if (s == null || s.isEmpty())
            return "";

        StringBuilder result = new StringBuilder();
        char[] chars = s.toCharArray();
        for (int i = 0; i < chars.length; i++)
            if (chars.length > (i + 1))
                if (Character.isUpperCase(chars[i])
                        && (Character.isLowerCase(chars[i + 1]) && !Character.isSpaceChar(chars[i + 1])))
                    result.append(" " + chars[i]);
                else
                    result.append(chars[i]);
            else
                result.append(chars[i]);
        return result.toString().trim();
    }

    /**
     * Returns any digits (numbers) in a String
     * @param s (Input string)
     * @return A string with any digits from the input string
     */
    public static String getNumbersInString(String s) {
        if (s == null || s.isEmpty())
            return "";
        StringBuilder result = new StringBuilder();
        char[] charArray = s.toCharArray();
        int count = charArray.length;
        for (int i = 0; i < count; i++)
            if (Character.isDigit(charArray[i]))
                result.append(charArray[i]);
        return result.toString();
    }

    public static int getCharacterCountInString(String source, char c) {
        int result = 0;
        if (!isEmpty(source))
            for (int i = 0; i < source.length(); i++)
                if (source.charAt(i) == c)
                    result++;
        return result;
    }

    /**
     * Determines if the application is running on a tablet
     * @param c - Context of the application
     * @return True if running on a tablet, false if on a smartphone
     */
    public static boolean isTablet(Context c) {
        return (c.getResources().getConfiguration().screenLayout
                & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
    }

    /**
     * Determines if the application is running on a xlarge tablet
     * @param c - Context of the application
     * @return True if running on a xlarge tablet, false if on a smaller device
     */
    public static boolean isXlargeTablet(Context c) {
        return (c.getResources().getConfiguration().screenLayout
                & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
    }

    /**
     * Determines if the device is in portrait mode
     * @param c - Context of the application
     * @return True if portrait mode, false if landscape mode
     */
    public static boolean isPortrait(Context c) {
        if (c == null)
            return false;
        return (c.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) ? true
                : false;
    }

    /**
     * Determines if the device is currently connected to a network
     * @param c - Context of the application
     * @return True if connected to a network, else false
     */
    public static boolean isOnline(Context c) {
        ConnectivityManager cm = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo[] netInfo = cm.getAllNetworkInfo();
        int count = netInfo.length;
        for (int i = 0; i < count; i++)
            if (netInfo[i] != null && netInfo[i].isConnected())
                return true;
        return false;
    }

    /**
     * Determines if the device is currently connected to a WiFi or Ethernet network
     * @param c - Context of the application
     * @return True if connected to a network, else false
     */
    public static boolean isWifiConnected(Context c, boolean disableCheck) {
        if (c != null) {
            if (disableCheck)
                return isOnline(c);

            ConnectivityManager connManager = (ConnectivityManager) c
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo[] connections = connManager.getAllNetworkInfo();
            int count = connections.length;
            for (int i = 0; i < count; i++)
                if (connections[i] != null && connections[i].getType() == ConnectivityManager.TYPE_WIFI
                        && connections[i].isConnectedOrConnecting()
                        || connections[i] != null && connections[i].getType() == ConnectivityManager.TYPE_ETHERNET
                                && connections[i].isConnectedOrConnecting())
                    return true;
        }
        return false;
    }

    /**
     * Returns a custom theme background image as Bitmap.
     * @param height
     * @param width
     * @return Bitmap with the background image
     */
    public static Bitmap getCustomThemeBackground(int height, int width, String url) {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Config.RGB_565;
            options.inDither = true;

            Bitmap bm = BitmapFactory.decodeFile(url, options);

            float scaleWidth = bm.getWidth() / ((float) width);
            float scaleHeight = bm.getHeight() / ((float) height);

            if (scaleWidth > scaleHeight)
                bm = Bitmap.createScaledBitmap(bm, (int) (bm.getWidth() / scaleHeight),
                        (int) (bm.getHeight() / scaleHeight), true);
            else
                bm = Bitmap.createScaledBitmap(bm, (int) (bm.getWidth() / scaleWidth),
                        (int) (bm.getHeight() / scaleWidth), true);

            bm = Bitmap.createBitmap(bm, (bm.getWidth() - width) / 2, (bm.getHeight() - height) / 2, width, height);
            return bm;
        } catch (Exception e) {
            return null;
        }
    }

    public static final long GB = 1000 * 1000 * 1000;
    public static final long MB = 1000 * 1000;
    public static final long KB = 1000;

    /**
     * Returns the input file size as a string in either KB, MB or GB
     * @param size (as bytes)
     * @return Size as readable string
     */
    public static String filesizeToString(long size) {
        if ((size / GB) >= 1)
            return substring(String.valueOf(((double) size / (double) GB)), 4) + " GB"; // GB
        else if ((size / MB) >= 1)
            return (size / MB) + " MB"; // MB
        else
            return (size / KB) + " KB"; // KB
    }

    /**
     * Returns a string with a length trimmed to the specified max length
     * @param s
     * @param maxLength
     * @return String with a length of the specified max length
     */
    public static String substring(String s, int maxLength) {
        if (s.length() >= maxLength)
            return s.substring(0, maxLength);
        else
            return s;
    }

    /**
     * Add a padding with a height of the ActionBar to the top of a given View
     * @param c
     * @param v
     */
    public static void addActionBarPadding(Context c, View v) {
        int mActionBarHeight = 0;
        TypedValue tv = new TypedValue();
        if (c.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
            mActionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,
                    c.getResources().getDisplayMetrics());
        else
            mActionBarHeight = 0; // No ActionBar style (pre-Honeycomb or ActionBar not in theme)

        v.setPadding(0, mActionBarHeight, 0, 0);
    }

    /**
     * Add a padding with a height of the ActionBar to the bottom of a given View
     * @param c
     * @param v
     */
    public static void addActionBarPaddingBottom(Context c, View v) {
        int mActionBarHeight = 0;
        TypedValue tv = new TypedValue();
        if (c.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
            mActionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,
                    c.getResources().getDisplayMetrics());
        else
            mActionBarHeight = 0; // No ActionBar style (pre-Honeycomb or ActionBar not in theme)

        v.setPadding(0, 0, 0, mActionBarHeight);
    }

    /**
     * Add a margin with a height of the ActionBar to the top of a given View contained in a FrameLayout
     * @param c
     * @param v
     */
    public static void addActionBarMargin(Context c, View v) {
        int mActionBarHeight = 0;
        TypedValue tv = new TypedValue();
        if (c.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
            mActionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,
                    c.getResources().getDisplayMetrics());
        else
            mActionBarHeight = 0; // No ActionBar style (pre-Honeycomb or ActionBar not in theme)

        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT);
        params.setMargins(0, mActionBarHeight, 0, 0);
        v.setLayoutParams(params);
    }

    public static void addNavigationBarPadding(Context c, View v) {
        v.setPadding(0, 0, 0, getNavigationBarHeight(c));
    }

    public static void addNavigationBarMargin(Context c, View v) {
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT);
        params.setMargins(0, 0, 0, getNavigationBarHeight(c));
        v.setLayoutParams(params);
    }

    public static boolean isGoogleTV(Context context) {
        return context.getPackageManager().hasSystemFeature("com.google.android.tv");
    }

    public static boolean isGoogleTV_720p(Context c) {
        return (isGoogleTV(c) && c.getResources().getDisplayMetrics().densityDpi == DisplayMetrics.DENSITY_TV);
    }

    public static boolean isGoogleTV_1080p(Context c) {
        return (isGoogleTV(c) && c.getResources().getDisplayMetrics().densityDpi == DisplayMetrics.DENSITY_XHIGH);
    }

    public static boolean hasFroyo() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
    }

    public static boolean hasGingerbread() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
    }

    public static boolean hasHoneycomb() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
    }

    public static boolean hasHoneycombMR1() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1;
    }

    public static boolean hasHoneycombMR2() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2;
    }

    public static boolean hasICS() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
    }

    public static boolean hasJellyBean() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
    }

    public static boolean hasJellyBeanMR1() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
    }

    public static boolean hasJellyBeanMR2() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
    }

    public static boolean hasKitKat() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    }

    public static int getThumbnailNotificationSize(Context c) {
        Resources r = c.getResources();
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 64, r.getDisplayMetrics());
    }

    public static Bitmap getNotificationImageThumbnail(Context c, String filepath) {
        int size = getThumbnailNotificationSize(c);

        try {
            Bitmap bm = decodeSampledBitmapFromFile(filepath, size, size);
            bm = Bitmap.createScaledBitmap(bm, size, (int) (size * 1.5), true);
            bm = Bitmap.createBitmap(bm, 0, 0, size, size);

            return bm;
        } catch (Exception e) {
            return decodeSampledBitmapFromResource(c.getResources(), R.drawable.refresh, size, size);
        }
    }

    public static int getLargeNotificationWidth(Context c) {
        Resources r = c.getResources();
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 360, r.getDisplayMetrics());
    }

    public static int getMenuWidth(Context c) {
        Resources r = c.getResources();
        if (MizLib.isTablet(c)) {
            return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 320, r.getDisplayMetrics());
        } else
            return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 300, r.getDisplayMetrics());
    }

    public static int convertDpToPixels(Context c, int dp) {
        Resources r = c.getResources();
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
    }

    public static int getActionBarHeight(Context c) {
        int mActionBarHeight = 0;
        TypedValue tv = new TypedValue();
        if (c.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
            mActionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,
                    c.getResources().getDisplayMetrics());
        else
            mActionBarHeight = 0; // No ActionBar style (pre-Honeycomb or ActionBar not in theme)

        return mActionBarHeight;
    }

    public static final String md5(final String s) {
        try {
            // Create MD5 Hash
            MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
            digest.update(s.getBytes());
            byte messageDigest[] = digest.digest();

            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            for (int i = 0; i < messageDigest.length; i++) {
                String h = Integer.toHexString(0xFF & messageDigest[i]);
                while (h.length() < 2)
                    h = "0" + h;
                hexString.append(h);
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static int getGreatestNumber(int a, int b) {
        return (a > b) ? a : b;
    }

    public static int getLowestNumber(int a, int b) {
        return (a > b) ? b : a;
    }

    public static int getThumbnailSize(Context c) {
        final int mImageThumbSize = c.getResources().getDimensionPixelSize(R.dimen.image_thumbnail_size);
        final int mImageThumbSpacing = c.getResources().getDimensionPixelSize(R.dimen.image_thumbnail_spacing);

        WindowManager window = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
        Display d = window.getDefaultDisplay();

        Point size = new Point();
        d.getSize(size);

        final int numColumns = (int) Math
                .floor(getGreatestNumber(size.x, size.y) / (mImageThumbSize + mImageThumbSpacing));

        if (numColumns > 0) {
            final int columnWidth = (getGreatestNumber(size.x, size.y) / numColumns) - mImageThumbSpacing;

            if (columnWidth > 320)
                return 440;
            else if (columnWidth > 240)
                return 320;
            else if (columnWidth > 180)
                return 240;
        }

        return 180;
    }

    public static void resizeBitmapFileToCoverSize(Context c, String filepath) {

        final int mImageThumbSize = c.getResources().getDimensionPixelSize(R.dimen.image_thumbnail_size);
        final int mImageThumbSpacing = c.getResources().getDimensionPixelSize(R.dimen.image_thumbnail_spacing);

        WindowManager window = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
        Display d = window.getDefaultDisplay();

        Point size = new Point();
        d.getSize(size);

        final int numColumns = (int) Math
                .floor(getGreatestNumber(size.x, size.y) / (mImageThumbSize + mImageThumbSpacing));

        if (numColumns > 0) {
            final int columnWidth = (getGreatestNumber(size.x, size.y) / numColumns) - mImageThumbSpacing;

            int imageWidth = 0;

            if (columnWidth > 300)
                imageWidth = 500;
            else if (columnWidth > 240)
                imageWidth = 320;
            else if (columnWidth > 180)
                imageWidth = 240;
            else
                imageWidth = 180;

            if (new File(filepath).exists())
                try {
                    Bitmap bm = decodeSampledBitmapFromFile(filepath, imageWidth, (int) (imageWidth * 1.5));
                    bm = Bitmap.createScaledBitmap(bm, imageWidth, (int) (imageWidth * 1.5), true);
                    FileOutputStream out = new FileOutputStream(filepath);
                    bm.compress(Bitmap.CompressFormat.JPEG, 90, out);
                    out.close();
                    bm.recycle();
                } catch (Exception e) {
                }
        }
    }

    public static String getImageUrlSize(Context c) {
        final int mImageThumbSize = c.getResources().getDimensionPixelSize(R.dimen.image_thumbnail_size);
        final int mImageThumbSpacing = c.getResources().getDimensionPixelSize(R.dimen.image_thumbnail_spacing);

        WindowManager window = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
        Display d = window.getDefaultDisplay();

        Point size = new Point();
        d.getSize(size);

        final int numColumns = (int) Math
                .floor(getGreatestNumber(size.x, size.y) / (mImageThumbSize + mImageThumbSpacing));

        if (numColumns > 0) {
            final int columnWidth = (getGreatestNumber(size.x, size.y) / numColumns) - mImageThumbSpacing;

            if (columnWidth > 300)
                return "w500";
            else if (columnWidth > 185)
                return "w300";
        }

        return "w185";
    }

    public static String getBackdropUrlSize(Context c) {
        WindowManager window = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
        Display d = window.getDefaultDisplay();

        Point size = new Point();
        d.getSize(size);

        final int width = getGreatestNumber(size.x, size.y);

        if (width > 1280 && isTablet(c)) // We only want to download full size images on tablets, as these are the only devices where you can see the difference
            return "original";
        else if (width > 780)
            return "w1280";
        else
            return "w780";
    }

    public static String getBackdropThumbUrlSize(Context c) {
        WindowManager window = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
        Display d = window.getDefaultDisplay();

        Point size = new Point();
        d.getSize(size);

        final int width = getLowestNumber(size.x, size.y);

        if (width >= 1200)
            return "w780";
        else
            return "w300";
    }

    public static String getActorUrlSize(Context c) {
        final int mImageThumbSize = c.getResources().getDimensionPixelSize(R.dimen.image_thumbnail_size);
        final int mImageThumbSpacing = c.getResources().getDimensionPixelSize(R.dimen.image_thumbnail_spacing);

        WindowManager window = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
        Display d = window.getDefaultDisplay();

        Point size = new Point();
        d.getSize(size);

        final int numColumns = (int) Math
                .floor(getGreatestNumber(size.x, size.y) / (mImageThumbSize + mImageThumbSpacing));

        if (numColumns > 0) {
            final int columnWidth = (getGreatestNumber(size.x, size.y) / numColumns) - mImageThumbSpacing;

            if (columnWidth > 400)
                return "h632";
        }

        return "w185";
    }

    public static Intent getVideoIntent(String fileUrl, boolean useWildcard, Object videoObject) {
        if (fileUrl.startsWith("http"))
            return getVideoIntent(Uri.parse(fileUrl), useWildcard, videoObject);

        Intent videoIntent = new Intent(Intent.ACTION_VIEW);
        videoIntent.setDataAndType(Uri.fromFile(new File(fileUrl)), getMimeType(fileUrl, useWildcard));
        videoIntent.putExtras(getVideoIntentBundle(videoObject));

        return videoIntent;
    }

    public static Intent getVideoIntent(Uri file, boolean useWildcard, Object videoObject) {
        Intent videoIntent = new Intent(Intent.ACTION_VIEW);
        videoIntent.setDataAndType(file, getMimeType(file.getPath(), useWildcard));
        videoIntent.putExtras(getVideoIntentBundle(videoObject));

        return videoIntent;
    }

    public static Intent getVideoIntent(String fileUrl, String mimeType, Object videoObject) {
        if (fileUrl.startsWith("http"))
            return getVideoIntent(Uri.parse(fileUrl), mimeType, videoObject);

        Intent videoIntent = new Intent(Intent.ACTION_VIEW);
        videoIntent.setDataAndType(Uri.fromFile(new File(fileUrl)), mimeType);
        videoIntent.putExtras(getVideoIntentBundle(videoObject));

        return videoIntent;
    }

    public static Intent getVideoIntent(Uri file, String mimeType, Object videoObject) {
        Intent videoIntent = new Intent(Intent.ACTION_VIEW);
        videoIntent.setDataAndType(file, mimeType);
        videoIntent.putExtras(getVideoIntentBundle(videoObject));

        return videoIntent;
    }

    private static Bundle getVideoIntentBundle(Object videoObject) {
        Bundle b = new Bundle();
        String title = "";
        if (videoObject instanceof Movie) {
            title = ((Movie) videoObject).getTitle();
            b.putString("plot", ((Movie) videoObject).getPlot());
            b.putString("date", ((Movie) videoObject).getReleasedate());
            b.putDouble("rating", ((Movie) videoObject).getRawRating());
            b.putString("cover", ((Movie) videoObject).getThumbnail());
            b.putString("genres", ((Movie) videoObject).getGenres());
        } else if (videoObject instanceof TvShowEpisode) {
            title = ((TvShowEpisode) videoObject).getTitle();
            b.putString("plot", ((TvShowEpisode) videoObject).getDescription());
            b.putString("date", ((TvShowEpisode) videoObject).getReleasedate());
            b.putDouble("rating", ((TvShowEpisode) videoObject).getRawRating());
            b.putString("cover", ((TvShowEpisode) videoObject).getEpisodePhoto());
            b.putString("episode", ((TvShowEpisode) videoObject).getEpisode());
            b.putString("season", ((TvShowEpisode) videoObject).getSeason());
        } else {
            title = (String) videoObject;
        }
        b.putString("title", title);
        b.putString("forcename", title);
        b.putBoolean("forcedirect", true);
        return b;
    }

    public static boolean isEmpty(String string) {
        if (string == null)
            return true;
        return string.length() == 0;
    }

    public static boolean checkFileTypes(String file) {
        if (file.contains(".")) { // Must have a file type
            String type = file.substring(file.lastIndexOf("."));
            String[] filetypes = allFileTypes.split("\\.");
            int count = filetypes.length;
            for (int i = 0; i < count; i++)
                if (type.toLowerCase(Locale.ENGLISH).equals("." + filetypes[i]))
                    return true;
        }
        return false;
    }

    public static boolean isNfoFile(String file) {
        if (file.contains(".")) { // Must have a file type
            String type = file.substring(file.lastIndexOf("."));
            return type.equalsIgnoreCase(".nfo");
        }
        return false;
    }

    public static String removeExtension(String filepath) {
        final int lastPeriodPos = filepath.lastIndexOf('.');
        if (lastPeriodPos <= 0) {
            return filepath;
        } else {
            // Remove the last period and everything after it
            return filepath.substring(0, lastPeriodPos);
        }
    }

    public static String convertToGenericNfo(String filepath) {
        final int lastPeriodPos = filepath.lastIndexOf('/');
        if (lastPeriodPos <= 0) {
            return filepath;
        } else {
            // Remove the last period and everything after it
            return filepath.substring(0, lastPeriodPos) + "/movie.nfo";
        }
    }

    /**
     * Returns a blurred bitmap. It uses a fast blur algorithm.
     * @param context
     * @param sentBitmap
     * @param radius
     * @return
     */
    public static Bitmap fastblur(Context context, Bitmap sentBitmap, int radius) {
        // Stack Blur v1.0 from
        // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
        //
        // Java Author: Mario Klingemann <mario at quasimondo.com>
        // http://incubator.quasimondo.com
        // created Feburary 29, 2004
        // Android port : Yahel Bouaziz <yahel at kayenko.com>
        // http://www.kayenko.com
        // ported april 5th, 2012

        // This is a compromise between Gaussian Blur and Box blur
        // It creates much better looking blurs than Box Blur, but is
        // 7x faster than my Gaussian Blur implementation.
        //
        // I called it Stack Blur because this describes best how this
        // filter works internally: it creates a kind of moving stack
        // of colors whilst scanning through the image. Thereby it
        // just has to add one new block of color to the right side
        // of the stack and remove the leftmost color. The remaining
        // colors on the topmost layer of the stack are either added on
        // or reduced by one, depending on if they are on the right or
        // on the left side of the stack.
        //
        // If you are using this algorithm in your code please add
        // the following line:
        //
        // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>

        Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);

        if (radius < 1) {
            return (null);
        }

        int w = bitmap.getWidth();
        int h = bitmap.getHeight();

        int[] pix = new int[w * h];
        //Log.e("pix", w + " " + h + " " + pix.length);
        bitmap.getPixels(pix, 0, w, 0, 0, w, h);

        int wm = w - 1;
        int hm = h - 1;
        int wh = w * h;
        int div = radius + radius + 1;

        int r[] = new int[wh];
        int g[] = new int[wh];
        int b[] = new int[wh];
        int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
        int vmin[] = new int[Math.max(w, h)];

        int divsum = (div + 1) >> 1;
        divsum *= divsum;
        int dv[] = new int[256 * divsum];
        for (i = 0; i < 256 * divsum; i++) {
            dv[i] = (i / divsum);
        }

        yw = yi = 0;

        int[][] stack = new int[div][3];
        int stackpointer;
        int stackstart;
        int[] sir;
        int rbs;
        int r1 = radius + 1;
        int routsum, goutsum, boutsum;
        int rinsum, ginsum, binsum;

        for (y = 0; y < h; y++) {
            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
            for (i = -radius; i <= radius; i++) {
                p = pix[yi + Math.min(wm, Math.max(i, 0))];
                sir = stack[i + radius];
                sir[0] = (p & 0xff0000) >> 16;
                sir[1] = (p & 0x00ff00) >> 8;
                sir[2] = (p & 0x0000ff);
                rbs = r1 - Math.abs(i);
                rsum += sir[0] * rbs;
                gsum += sir[1] * rbs;
                bsum += sir[2] * rbs;
                if (i > 0) {
                    rinsum += sir[0];
                    ginsum += sir[1];
                    binsum += sir[2];
                } else {
                    routsum += sir[0];
                    goutsum += sir[1];
                    boutsum += sir[2];
                }
            }
            stackpointer = radius;

            for (x = 0; x < w; x++) {

                r[yi] = dv[rsum];
                g[yi] = dv[gsum];
                b[yi] = dv[bsum];

                rsum -= routsum;
                gsum -= goutsum;
                bsum -= boutsum;

                stackstart = stackpointer - radius + div;
                sir = stack[stackstart % div];

                routsum -= sir[0];
                goutsum -= sir[1];
                boutsum -= sir[2];

                if (y == 0) {
                    vmin[x] = Math.min(x + radius + 1, wm);
                }
                p = pix[yw + vmin[x]];

                sir[0] = (p & 0xff0000) >> 16;
                sir[1] = (p & 0x00ff00) >> 8;
                sir[2] = (p & 0x0000ff);

                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];

                rsum += rinsum;
                gsum += ginsum;
                bsum += binsum;

                stackpointer = (stackpointer + 1) % div;
                sir = stack[(stackpointer) % div];

                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];

                rinsum -= sir[0];
                ginsum -= sir[1];
                binsum -= sir[2];

                yi++;
            }
            yw += w;
        }
        for (x = 0; x < w; x++) {
            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
            yp = -radius * w;
            for (i = -radius; i <= radius; i++) {
                yi = Math.max(0, yp) + x;

                sir = stack[i + radius];

                sir[0] = r[yi];
                sir[1] = g[yi];
                sir[2] = b[yi];

                rbs = r1 - Math.abs(i);

                rsum += r[yi] * rbs;
                gsum += g[yi] * rbs;
                bsum += b[yi] * rbs;

                if (i > 0) {
                    rinsum += sir[0];
                    ginsum += sir[1];
                    binsum += sir[2];
                } else {
                    routsum += sir[0];
                    goutsum += sir[1];
                    boutsum += sir[2];
                }

                if (i < hm) {
                    yp += w;
                }
            }
            yi = x;
            stackpointer = radius;
            for (y = 0; y < h; y++) {
                pix[yi] = 0xff000000 | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];

                rsum -= routsum;
                gsum -= goutsum;
                bsum -= boutsum;

                stackstart = stackpointer - radius + div;
                sir = stack[stackstart % div];

                routsum -= sir[0];
                goutsum -= sir[1];
                boutsum -= sir[2];

                if (x == 0) {
                    vmin[y] = Math.min(y + r1, hm) * w;
                }
                p = x + vmin[y];

                sir[0] = r[p];
                sir[1] = g[p];
                sir[2] = b[p];

                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];

                rsum += rinsum;
                gsum += ginsum;
                bsum += binsum;

                stackpointer = (stackpointer + 1) % div;
                sir = stack[stackpointer];

                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];

                rinsum -= sir[0];
                ginsum -= sir[1];
                binsum -= sir[2];

                yi += w;
            }
        }

        //Log.e("pix", w + " " + h + " " + pix.length);
        bitmap.setPixels(pix, 0, w, 0, 0, w, h);

        return (bitmap);
    }

    public static boolean downloadFile(String url, String savePath) {
        // the size of my buffer in bits
        int bufferSize = 8192;
        byte[] retVal = null;
        InputStream in = null;
        OutputStream fileos = null;
        OkHttpClient client = new OkHttpClient();
        HttpURLConnection urlConnection = null;

        try {
            urlConnection = client.open(new URL(url));
            fileos = new BufferedOutputStream(new FileOutputStream(savePath));

            in = new BufferedInputStream(urlConnection.getInputStream(), bufferSize);

            retVal = new byte[bufferSize];
            int length = 0;
            while ((length = in.read(retVal)) > -1) {
                fileos.write(retVal, 0, length);
            }

        } catch (IOException e) {
            return false;
        } finally {
            if (fileos != null) {
                try {
                    fileos.flush();
                    fileos.close();
                } catch (IOException e) {
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
            if (urlConnection != null)
                urlConnection.disconnect();
        }

        return true;
    }

    public static JSONObject getJSONObject(String url) {
        try {
            // Hack to work around bug with secure HTTP connections: https://github.com/square/okhttp/issues/184
            if (url.startsWith("https")) {
                HttpClient httpclient = new DefaultHttpClient();
                HttpGet httppost = new HttpGet(url);
                httppost.setHeader("Accept", "application/json");
                ResponseHandler<String> responseHandler = new BasicResponseHandler();
                String html = httpclient.execute(httppost, responseHandler);

                return new JSONObject(html);
            } else {
                OkApacheClient httpclient = new OkApacheClient();
                HttpGet httppost = new HttpGet(url);
                httppost.setHeader("Accept", "application/json");
                ResponseHandler<String> responseHandler = new BasicResponseHandler();
                String html = httpclient.execute(httppost, responseHandler);

                return new JSONObject(html);
            }
        } catch (Exception e) {
            return new JSONObject();
        }
    }

    public static String getStringFromJSONObject(JSONObject json, String name, String fallback) {
        try {
            String s = json.getString(name);
            if (s.equals("null"))
                return fallback;
            return s;
        } catch (Exception e) {
            return fallback;
        }
    }

    public static String removeIndexZero(String s) {
        if (!isEmpty(s))
            try {
                return String.valueOf(Integer.parseInt(s));
            } catch (NumberFormatException e) {
            }
        return s;
    }

    public static String addIndexZero(String s) {
        if (isEmpty(s))
            return "00";
        try {
            return String.format(Locale.ENGLISH, "%02d", Integer.parseInt(s));
        } catch (NumberFormatException e) {
            return "00";
        }
    }

    public static String addIndexZero(int number) {
        try {
            return String.format(Locale.ENGLISH, "%02d", number);
        } catch (Exception e) {
            return "00";
        }
    }

    public static String URLEncodeUTF8(String s) {
        return Uri.parse(s).toString();
    }

    public static Bitmap drawableToBitmap(Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
                Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }

    public static final int TYPE_MOVIE = 0, TYPE_SHOWS = 1;

    public static ArrayList<FileSource> getFileSources(int type, boolean onlyNetworkSources) {

        ArrayList<FileSource> filesources = new ArrayList<FileSource>();
        DbAdapterSources dbHelperSources = MizuuApplication.getSourcesAdapter();

        Cursor c = null;
        if (type == TYPE_MOVIE)
            c = dbHelperSources.fetchAllMovieSources();
        else
            c = dbHelperSources.fetchAllShowSources();

        while (c.moveToNext()) {
            if (onlyNetworkSources) {
                if (c.getInt(c.getColumnIndex(DbAdapterSources.KEY_FILESOURCE_TYPE)) == FileSource.SMB) {
                    filesources.add(new FileSource(c.getLong(c.getColumnIndex(DbAdapterSources.KEY_ROWID)),
                            c.getString(c.getColumnIndex(DbAdapterSources.KEY_FILEPATH)),
                            c.getInt(c.getColumnIndex(DbAdapterSources.KEY_FILESOURCE_TYPE)),
                            c.getString(c.getColumnIndex(DbAdapterSources.KEY_USER)),
                            c.getString(c.getColumnIndex(DbAdapterSources.KEY_PASSWORD)),
                            c.getString(c.getColumnIndex(DbAdapterSources.KEY_DOMAIN)),
                            c.getString(c.getColumnIndex(DbAdapterSources.KEY_TYPE))));
                }
            } else {
                filesources.add(new FileSource(c.getLong(c.getColumnIndex(DbAdapterSources.KEY_ROWID)),
                        c.getString(c.getColumnIndex(DbAdapterSources.KEY_FILEPATH)),
                        c.getInt(c.getColumnIndex(DbAdapterSources.KEY_FILESOURCE_TYPE)),
                        c.getString(c.getColumnIndex(DbAdapterSources.KEY_USER)),
                        c.getString(c.getColumnIndex(DbAdapterSources.KEY_PASSWORD)),
                        c.getString(c.getColumnIndex(DbAdapterSources.KEY_DOMAIN)),
                        c.getString(c.getColumnIndex(DbAdapterSources.KEY_TYPE))));
            }
        }
        c.close();

        return filesources;
    }

    public static NtlmPasswordAuthentication getAuthFromFilesource(FileSource source) {
        NtlmPasswordAuthentication auth;

        if (source == null) {
            auth = NtlmPasswordAuthentication.ANONYMOUS;
        } else {
            if (source.getDomain().isEmpty() && source.getUser().isEmpty() && source.getPassword().isEmpty()) {
                auth = NtlmPasswordAuthentication.ANONYMOUS;
            } else {
                auth = new NtlmPasswordAuthentication(source.getDomain(), source.getUser(), source.getPassword());
            }
        }
        return auth;
    }

    public static NtlmPasswordAuthentication getAuthFromFilepath(int type, String filepath) {

        ArrayList<FileSource> filesources = MizLib.getFileSources(type, true);
        FileSource source = null;

        for (int i = 0; i < filesources.size(); i++) {
            if (filepath.contains(filesources.get(i).getFilepath())) {
                source = filesources.get(i);
                continue;
            }
        }

        return getAuthFromFilesource(source);
    }

    public static int COVER = 1, BACKDROP = 2;

    public static SmbFile getCustomCoverArt(String filepath, NtlmPasswordAuthentication auth, int type)
            throws MalformedURLException, UnsupportedEncodingException, SmbException {

        String parentPath = filepath.substring(0, filepath.lastIndexOf("/"));
        if (!parentPath.endsWith("/"))
            parentPath += "/";

        String filename = filepath.substring(0, filepath.lastIndexOf(".")).replaceAll("part[1-9]|cd[1-9]", "")
                .trim();

        String[] list = MizuuApplication.getCifsFilesList(parentPath);
        if (list == null) {
            SmbFile s = new SmbFile(createSmbLoginString(URLEncoder.encode(auth.getDomain(), "utf-8"),
                    URLEncoder.encode(auth.getUsername(), "utf-8"), URLEncoder.encode(auth.getPassword(), "utf-8"),
                    parentPath, false));

            list = s.list();
            s = null;
            MizuuApplication.putCifsFilesList(parentPath, list);
        }

        String name = "", absolutePath = "", customCoverArt = "";

        if (type == COVER) {
            for (int i = 0; i < list.length; i++) {
                name = list[i];
                absolutePath = parentPath + list[i];
                if (name.equalsIgnoreCase("poster.jpg") || name.equalsIgnoreCase("poster.jpeg")
                        || name.equalsIgnoreCase("poster.tbn") || name.equalsIgnoreCase("folder.jpg")
                        || name.equalsIgnoreCase("folder.jpeg") || name.equalsIgnoreCase("folder.tbn")
                        || name.equalsIgnoreCase("cover.jpg") || name.equalsIgnoreCase("cover.jpeg")
                        || name.equalsIgnoreCase("cover.tbn")
                        || absolutePath.equalsIgnoreCase(filename + "-poster.jpg")
                        || absolutePath.equalsIgnoreCase(filename + "-poster.jpeg")
                        || absolutePath.equalsIgnoreCase(filename + "-poster.tbn")
                        || absolutePath.equalsIgnoreCase(filename + "-folder.jpg")
                        || absolutePath.equalsIgnoreCase(filename + "-folder.jpeg")
                        || absolutePath.equalsIgnoreCase(filename + "-folder.tbn")
                        || absolutePath.equalsIgnoreCase(filename + "-cover.jpg")
                        || absolutePath.equalsIgnoreCase(filename + "-cover.jpeg")
                        || absolutePath.equalsIgnoreCase(filename + "-cover.tbn")
                        || absolutePath.equalsIgnoreCase(filename + ".jpg")
                        || absolutePath.equalsIgnoreCase(filename + ".jpeg")
                        || absolutePath.equalsIgnoreCase(filename + ".tbn")) {
                    customCoverArt = absolutePath;
                    continue;
                }
            }
        } else {
            for (int i = 0; i < list.length; i++) {
                name = list[i];
                absolutePath = parentPath + list[i];
                if (name.equalsIgnoreCase("fanart.jpeg") || name.equalsIgnoreCase("fanart.tbn")
                        || name.equalsIgnoreCase("banner.jpg") || name.equalsIgnoreCase("banner.jpeg")
                        || name.equalsIgnoreCase("banner.tbn") || name.equalsIgnoreCase("backdrop.jpg")
                        || name.equalsIgnoreCase("backdrop.jpeg") || name.equalsIgnoreCase("backdrop.tbn")
                        || absolutePath.equalsIgnoreCase(filename + "-fanart.jpg")
                        || absolutePath.equalsIgnoreCase(filename + "-fanart.jpeg")
                        || absolutePath.equalsIgnoreCase(filename + "-fanart.tbn")
                        || absolutePath.equalsIgnoreCase(filename + "-banner.jpg")
                        || absolutePath.equalsIgnoreCase(filename + "-banner.jpeg")
                        || absolutePath.equalsIgnoreCase(filename + "-banner.tbn")
                        || absolutePath.equalsIgnoreCase(filename + "-backdrop.jpg")
                        || absolutePath.equalsIgnoreCase(filename + "-backdrop.jpeg")
                        || absolutePath.equalsIgnoreCase(filename + "-backdrop.tbn")) {
                    customCoverArt = absolutePath;
                    continue;
                }
            }
        }

        list = null;

        if (!customCoverArt.isEmpty())
            return new SmbFile(createSmbLoginString(URLEncoder.encode(auth.getDomain(), "utf-8"),
                    URLEncoder.encode(auth.getUsername(), "utf-8"), URLEncoder.encode(auth.getPassword(), "utf-8"),
                    customCoverArt, false));

        return null;
    }

    public static String[] subtitleFormats = new String[] { ".srt", ".sub", ".ssa", ".ssf", ".smi", ".txt", ".usf",
            ".ass", ".stp", ".idx", ".aqt", ".cvd", ".dks", ".jss", ".mpl", ".pjs", ".psb", ".rt", ".svcd",
            ".usf" };

    public static boolean isSubtitleFile(String s) {
        int count = subtitleFormats.length;
        for (int i = 0; i < count; i++)
            if (s.endsWith(subtitleFormats[i]))
                return true;
        return false;
    }

    public static List<SmbFile> getSubtitleFiles(String filepath, NtlmPasswordAuthentication auth)
            throws MalformedURLException, UnsupportedEncodingException {
        ArrayList<SmbFile> subs = new ArrayList<SmbFile>();

        String fileType = "";
        if (filepath.contains(".")) {
            fileType = filepath.substring(filepath.lastIndexOf("."));
        }

        int count = subtitleFormats.length;
        for (int i = 0; i < count; i++) {
            subs.add(new SmbFile(createSmbLoginString(URLEncoder.encode(auth.getDomain(), "utf-8"),
                    URLEncoder.encode(auth.getUsername(), "utf-8"), URLEncoder.encode(auth.getPassword(), "utf-8"),
                    filepath.replace(fileType, subtitleFormats[i]), false)));
        }

        return subs;
    }

    public static List<SmbFile> getDVDFiles(String filepath, NtlmPasswordAuthentication auth)
            throws MalformedURLException {
        ArrayList<SmbFile> subs = new ArrayList<SmbFile>();

        try {
            String s = filepath.substring(0, filepath.lastIndexOf("/"));
            SmbFile dir = new SmbFile(createSmbLoginString(URLEncoder.encode(auth.getDomain(), "utf-8"),
                    URLEncoder.encode(auth.getUsername(), "utf-8"), URLEncoder.encode(auth.getPassword(), "utf-8"),
                    s, true));
            SmbFile[] listFiles = dir.listFiles();
            int count = listFiles.length;
            for (int i = 0; i < count; i++) {
                if (!listFiles[i].getName().equalsIgnoreCase("video_ts.ifo"))
                    subs.add(listFiles[i]);
            }
        } catch (Exception e) {
        }

        return subs;
    }

    /**
     * A bit of a hack to properly delete files / folders from the OS
     * @param f
     * @return
     */
    public static boolean deleteFile(File f) {
        return f.delete();
    }

    public static String createSmbLoginString(String domain, String user, String password, String server,
            boolean isFolder) {
        // Create the string to fit the following syntax: smb://[[[domain;]username[:password]@]server[:port]/      
        StringBuilder sb = new StringBuilder();

        sb.append("smb://");

        user = user.replace("+", "%20");
        password = password.replace("+", "%20");
        domain = domain.replace("+", "%20");
        server = server.replace("+", "%20");

        // Only add domain, username and password details if the username isn't empty
        if (!user.isEmpty()) {
            // Add the domain details
            if (!domain.isEmpty())
                sb.append(domain + ";");

            // Add username
            sb.append(user);

            // Add password
            if (!password.isEmpty())
                sb.append(":" + password);

            sb.append("@");
        }

        sb.append(server.replace("smb://", ""));

        if (isFolder)
            if (!server.endsWith("/"))
                sb.append("/");

        return sb.toString();
    }

    public static void deleteShow(Context c, TvShow thisShow, boolean showToast) {
        // Create and open database
        DbAdapterTvShow dbHelper = MizuuApplication.getTvDbAdapter();
        boolean deleted = dbHelper.deleteShow(thisShow.getId());

        DbAdapterTvShowEpisode db = MizuuApplication.getTvEpisodeDbAdapter();
        deleted = deleted && db.deleteAllEpisodes(thisShow.getId());

        if (deleted) {
            try {
                // Delete cover art image
                File coverArt = new File(thisShow.getCoverPhoto());
                if (coverArt.exists() && coverArt.getAbsolutePath().contains("com.miz.mizuu")) {
                    MizLib.deleteFile(coverArt);
                }

                // Delete thumbnail image
                File thumbnail = new File(thisShow.getThumbnail());
                if (thumbnail.exists() && thumbnail.getAbsolutePath().contains("com.miz.mizuu")) {
                    MizLib.deleteFile(thumbnail);
                }

                // Delete backdrop image
                File backdrop = new File(thisShow.getBackdrop());
                if (backdrop.exists() && backdrop.getAbsolutePath().contains("com.miz.mizuu")) {
                    MizLib.deleteFile(backdrop);
                }

                // Delete episode images
                File dataFolder = getTvShowEpisodeFolder(c);
                File[] listFiles = dataFolder.listFiles();
                if (listFiles != null) {
                    int count = listFiles.length;
                    for (int i = 0; i < count; i++)
                        if (listFiles[i].getName().startsWith(thisShow.getId() + "_S"))
                            MizLib.deleteFile(listFiles[i]);
                }
            } catch (NullPointerException e) {
            } // No file to delete
        } else {
            if (showToast)
                Toast.makeText(c, c.getString(R.string.failedToRemoveShow), Toast.LENGTH_SHORT).show();
        }
    }

    public static String getYouTubeId(String url) {
        String pattern = "https?:\\/\\/(?:[0-9A-Z-]+\\.)?(?:youtu\\.be\\/|youtube\\.com\\S*[^\\w\\-\\s])([\\w\\-]{11})(?=[^\\w\\-]|$)(?![?=&+%\\w]*(?:['\"][^<>]*>|<\\/a>))[?=&+%\\w]*";

        Pattern compiledPattern = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
        Matcher matcher = compiledPattern.matcher(url);

        if (matcher.find(1))
            return getYTId(matcher.group(1));

        if (matcher.find(0))
            return getYTId(matcher.group(0));

        return url;
    }

    private static String getYTId(String url) {
        String result = url;

        if (result.contains("v=")) {
            result = result.substring(result.indexOf("v=") + 2);

            if (result.contains("&")) {
                result = result.substring(0, result.indexOf("&"));
            }
        }

        if (result.contains("youtu.be/")) {
            result = result.substring(result.indexOf("youtu.be/") + 9);

            if (result.contains("&")) {
                result = result.substring(0, result.indexOf("&"));
            }
        }

        return result;
    }

    private static String convertToHex(byte[] data) {
        StringBuilder buf = new StringBuilder();
        int count = data.length;
        for (int i = 0; i < count; i++) {
            int halfbyte = (data[i] >>> 4) & 0x0F;
            int two_halfs = 0;
            do {
                buf.append((0 <= halfbyte) && (halfbyte <= 9) ? (char) ('0' + halfbyte)
                        : (char) ('a' + (halfbyte - 10)));
                halfbyte = data[i] & 0x0F;
            } while (two_halfs++ < 1);
        }
        return buf.toString();
    }

    public static String SHA1(String text) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(text.getBytes("iso-8859-1"), 0, text.length());
            byte[] sha1hash = md.digest();
            return convertToHex(sha1hash);
        } catch (Exception e) {
            return "";
        }
    }

    public static boolean checkInMovieTrakt(Movie movie, Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return false;

        // Cancel the current check in to allow this one
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = new HttpPost("http://api.trakt.tv/movie/cancelcheckin/" + MizLib.TRAKT_API);
        httppost.setHeader("Content-type", "application/json");

        try {

            JSONObject holder = new JSONObject();
            holder.put("username", username);
            holder.put("password", password);

            StringEntity se = new StringEntity(holder.toString());
            se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
            httppost.setEntity(se);

            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

        } catch (Exception e) {
        }

        // Check in with the movie
        httpclient = new OkApacheClient();
        httppost = new HttpPost("http://api.trakt.tv/movie/checkin/" + MizLib.TRAKT_API);

        try {
            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
            nameValuePairs.add(new BasicNameValuePair("username", username));
            nameValuePairs.add(new BasicNameValuePair("password", password));
            nameValuePairs.add(new BasicNameValuePair("imdb_id", movie.getImdbId()));
            nameValuePairs.add(new BasicNameValuePair("tmdb_id", movie.getTmdbId()));
            nameValuePairs.add(new BasicNameValuePair("title", movie.getTitle()));
            nameValuePairs
                    .add(new BasicNameValuePair("year", movie.getReleaseYear().replace("(", "").replace(")", "")));
            nameValuePairs.add(new BasicNameValuePair("app_version",
                    c.getPackageManager().getPackageInfo(c.getPackageName(), 0).versionName));
            nameValuePairs.add(new BasicNameValuePair("app_date",
                    c.getPackageManager().getPackageInfo(c.getPackageName(), 0).versionName));
            httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

            return true;
        } catch (Exception e) {
        }

        return false;
    }

    public static boolean checkInEpisodeTrakt(TvShowEpisode episode, Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return false;

        // Cancel the current check in to allow this one
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = new HttpPost("http://api.trakt.tv/show/cancelcheckin/" + MizLib.TRAKT_API);
        httppost.setHeader("Content-type", "application/json");

        try {

            JSONObject holder = new JSONObject();
            holder.put("username", username);
            holder.put("password", password);

            StringEntity se = new StringEntity(holder.toString());
            se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
            httppost.setEntity(se);

            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

        } catch (Exception e) {
        }

        // Check in with the movie
        httpclient = new OkApacheClient();
        httppost = new HttpPost("http://api.trakt.tv/show/checkin/" + MizLib.TRAKT_API);

        try {
            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
            nameValuePairs.add(new BasicNameValuePair("username", username));
            nameValuePairs.add(new BasicNameValuePair("password", password));
            nameValuePairs.add(new BasicNameValuePair("tvdb_id", episode.getShowId()));
            nameValuePairs.add(new BasicNameValuePair("title", ""));
            nameValuePairs.add(new BasicNameValuePair("year", ""));
            nameValuePairs.add(new BasicNameValuePair("season", episode.getSeason()));
            nameValuePairs.add(new BasicNameValuePair("episode", episode.getEpisode()));
            nameValuePairs.add(new BasicNameValuePair("app_version",
                    c.getPackageManager().getPackageInfo(c.getPackageName(), 0).versionName));
            nameValuePairs.add(new BasicNameValuePair("app_date",
                    c.getPackageManager().getPackageInfo(c.getPackageName(), 0).versionName));
            httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

            return true;
        } catch (Exception e) {
        }

        return false;
    }

    public static boolean markMovieAsWatched(List<Movie> movies, Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return false;

        if (movies.size() == 0)
            return false;

        // Mark as seen / unseen
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = null;
        if (movies.get(0).hasWatched())
            httppost = new HttpPost("http://api.trakt.tv/movie/seen/" + MizLib.TRAKT_API);
        else
            httppost = new HttpPost("http://api.trakt.tv/movie/unseen/" + MizLib.TRAKT_API);

        try {
            JSONObject json = new JSONObject();
            json.put("username", username);
            json.put("password", password);

            JSONArray array = new JSONArray();
            int count = movies.size();
            for (int i = 0; i < count; i++) {
                JSONObject jsonMovie = new JSONObject();
                jsonMovie.put("imdb_id", movies.get(i).getImdbId());
                jsonMovie.put("tmdb_id", movies.get(i).getTmdbId());
                jsonMovie.put("year", movies.get(i).getReleaseYear());
                jsonMovie.put("title", movies.get(i).getTitle());
                array.put(jsonMovie);
            }
            json.put("movies", array);

            httppost.setEntity(new StringEntity(json.toString()));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

            return true;
        } catch (Exception e) {
        }

        return false;
    }

    public static boolean markEpisodeAsWatched(List<TvShowEpisode> episodes, Context c, boolean overrideWatched) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return false;

        if (episodes.size() == 0)
            return false;

        // Mark episode as seen / unseen
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = null;

        if (overrideWatched || episodes.get(0).hasWatched())
            httppost = new HttpPost("http://api.trakt.tv/show/episode/seen/" + MizLib.TRAKT_API);
        else
            httppost = new HttpPost("http://api.trakt.tv/show/episode/unseen/" + MizLib.TRAKT_API);

        try {
            JSONObject json = new JSONObject();
            json.put("username", username);
            json.put("password", password);
            json.put("imdb_id", "");
            json.put("tvdb_id", episodes.get(0).getShowId());
            json.put("title", "");
            json.put("year", "");

            JSONArray array = new JSONArray();
            int count = episodes.size();
            for (int i = 0; i < count; i++) {
                JSONObject jsonMovie = new JSONObject();
                jsonMovie.put("season", episodes.get(i).getSeason());
                jsonMovie.put("episode", episodes.get(i).getEpisode());
                array.put(jsonMovie);
            }
            json.put("episodes", array);

            httppost.setEntity(new StringEntity(json.toString()));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

            return true;
        } catch (Exception e) {
        }

        return false;
    }

    public static boolean markTvShowAsWatched(TraktTvShow show, Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return false;

        // Mark episode as seen / unseen
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = new HttpPost("http://api.trakt.tv/show/episode/seen/" + MizLib.TRAKT_API);

        try {
            JSONObject json = new JSONObject();
            json.put("username", username);
            json.put("password", password);
            json.put("tvdb_id", show.getId());
            json.put("title", show.getTitle());

            JSONArray array = new JSONArray();
            for (String season : show.getSeasons().keySet()) {
                Collection<String> episodes = show.getSeasons().get(season);
                for (String episode : episodes) {
                    JSONObject jsonShow = new JSONObject();
                    jsonShow.put("season", season);
                    jsonShow.put("episode", episode);
                    array.put(jsonShow);
                }
            }
            json.put("episodes", array);

            httppost.setEntity(new StringEntity(json.toString()));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

            return true;
        } catch (Exception e) {
        }

        return false;
    }

    public static boolean addMoviesToTraktLibrary(List<Movie> movies, Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return false;

        if (movies.size() == 0)
            return false;

        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = new HttpPost("http://api.trakt.tv/movie/library/" + MizLib.TRAKT_API);

        try {
            JSONObject json = new JSONObject();
            json.put("username", username);
            json.put("password", password);

            JSONArray array = new JSONArray();
            int count = movies.size();
            for (int i = 0; i < count; i++) {
                JSONObject jsonMovie = new JSONObject();
                jsonMovie.put("imdb_id", movies.get(i).getImdbId());
                jsonMovie.put("tmdb_id", movies.get(i).getTmdbId());
                jsonMovie.put("year", movies.get(i).getReleaseYear());
                jsonMovie.put("title", movies.get(i).getTitle());
                array.put(jsonMovie);
            }
            json.put("movies", array);

            httppost.setEntity(new StringEntity(json.toString()));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

            return true;
        } catch (Exception e) {
        }

        return false;
    }

    public static boolean movieWatchlist(List<Movie> movies, Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return false;

        if (movies.size() == 0)
            return false;

        // Mark as seen / unseen
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = null;
        if (movies.get(0).toWatch())
            httppost = new HttpPost("http://api.trakt.tv/movie/watchlist/" + MizLib.TRAKT_API);
        else
            httppost = new HttpPost("http://api.trakt.tv/movie/unwatchlist/" + MizLib.TRAKT_API);

        try {
            JSONObject json = new JSONObject();
            json.put("username", username);
            json.put("password", password);

            JSONArray array = new JSONArray();
            int count = movies.size();
            for (int i = 0; i < count; i++) {
                JSONObject jsonMovie = new JSONObject();
                jsonMovie.put("imdb_id", movies.get(i).getImdbId());
                jsonMovie.put("tmdb_id", movies.get(i).getTmdbId());
                jsonMovie.put("year", movies.get(i).getReleaseYear());
                jsonMovie.put("title", movies.get(i).getTitle());
                array.put(jsonMovie);
            }
            json.put("movies", array);

            httppost.setEntity(new StringEntity(json.toString()));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

            return true;
        } catch (Exception e) {
        }

        return false;
    }

    public static boolean movieFavorite(List<Movie> movies, Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return false;

        if (movies.size() == 0)
            return false;

        // Mark as seen / unseen
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = new HttpPost("http://api.trakt.tv/rate/movies/" + MizLib.TRAKT_API);

        try {
            JSONObject json = new JSONObject();
            json.put("username", username);
            json.put("password", password);

            JSONArray array = new JSONArray();
            int count = movies.size();
            for (int i = 0; i < count; i++) {
                JSONObject jsonMovie = new JSONObject();
                jsonMovie.put("imdb_id", movies.get(i).getImdbId());
                jsonMovie.put("tmdb_id", movies.get(i).getTmdbId());
                jsonMovie.put("year", movies.get(i).getReleaseYear());
                jsonMovie.put("title", movies.get(i).getTitle());
                jsonMovie.put("rating", movies.get(i).isFavourite() ? "love" : "unrate");
                array.put(jsonMovie);
            }
            json.put("movies", array);

            httppost.setEntity(new StringEntity(json.toString()));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

            return true;
        } catch (Exception e) {
        }

        return false;
    }

    public static boolean hasTraktAccount(Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return false;

        return true;
    }

    public static boolean addTvShowToLibrary(TraktTvShow show, Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return false;

        // Mark as seen / unseen
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = new HttpPost("http://api.trakt.tv/show/episode/library/" + MizLib.TRAKT_API);

        try {
            JSONObject json = new JSONObject();
            json.put("username", username);
            json.put("password", password);
            json.put("tvdb_id", show.getId());
            json.put("title", show.getTitle());

            JSONArray array = new JSONArray();
            for (String season : show.getSeasons().keySet()) {
                Collection<String> episodes = show.getSeasons().get(season);
                for (String episode : episodes) {
                    JSONObject jsonShow = new JSONObject();
                    jsonShow.put("season", season);
                    jsonShow.put("episode", episode);
                    array.put(jsonShow);
                }
            }
            json.put("episodes", array);

            httppost.setEntity(new StringEntity(json.toString()));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

            return true;
        } catch (Exception e) {
        }

        return false;
    }

    public static boolean tvShowFavorite(List<TvShow> shows, Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return false;

        if (shows.size() == 0)
            return false;

        // Mark as seen / unseen
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = new HttpPost("http://api.trakt.tv/rate/shows/" + MizLib.TRAKT_API);

        try {
            JSONObject json = new JSONObject();
            json.put("username", username);
            json.put("password", password);

            JSONArray array = new JSONArray();
            int count = shows.size();
            for (int i = 0; i < count; i++) {
                JSONObject jsonShow = new JSONObject();
                jsonShow.put("tvdb_id", shows.get(i).getId());
                jsonShow.put("title", shows.get(i).getTitle());
                jsonShow.put("rating", shows.get(i).isFavorite() ? "love" : "unrate");
                array.put(jsonShow);
            }
            json.put("shows", array);

            httppost.setEntity(new StringEntity(json.toString()));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            httpclient.execute(httppost, responseHandler);

            return true;
        } catch (Exception e) {
        }

        return false;
    }

    public static int WATCHED = 1, RATINGS = 2, WATCHLIST = 3, COLLECTION = 4;

    public static JSONArray getTraktMovieLibrary(Context c, int type) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return new JSONArray();

        // Mark as seen / unseen
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = null;
        if (type == WATCHED) {
            httppost = new HttpPost(
                    "http://api.trakt.tv/user/library/movies/watched.json/" + MizLib.TRAKT_API + "/" + username);
        } else if (type == RATINGS) {
            httppost = new HttpPost(
                    "http://api.trakt.tv/user/ratings/movies.json/" + MizLib.TRAKT_API + "/" + username + "/love");
        } else if (type == WATCHLIST) {
            httppost = new HttpPost(
                    "http://api.trakt.tv/user/watchlist/movies.json/" + MizLib.TRAKT_API + "/" + username);
        } else if (type == COLLECTION) {
            httppost = new HttpPost(
                    "http://api.trakt.tv/user/library/movies/collection.json/" + MizLib.TRAKT_API + "/" + username);
        }

        try {
            JSONObject json = new JSONObject();
            json.put("username", username);
            json.put("password", password);

            httppost.setEntity(new StringEntity(json.toString()));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            String result = httpclient.execute(httppost, responseHandler);

            return new JSONArray(result);
        } catch (Exception e) {
        }

        return new JSONArray();
    }

    public static JSONArray getTraktTvShowLibrary(Context c, int type) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return new JSONArray();

        // Mark as seen / unseen
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = null;
        if (type == WATCHED) {
            httppost = new HttpPost(
                    "http://api.trakt.tv/user/library/shows/watched.json/" + MizLib.TRAKT_API + "/" + username);
        } else if (type == RATINGS) {
            httppost = new HttpPost(
                    "http://api.trakt.tv/user/ratings/shows.json/" + MizLib.TRAKT_API + "/" + username + "/love");
        } else if (type == COLLECTION) {
            httppost = new HttpPost(
                    "http://api.trakt.tv/user/library/shows/collection.json/" + MizLib.TRAKT_API + "/" + username);
        }

        try {
            JSONObject json = new JSONObject();
            json.put("username", username);
            json.put("password", password);

            httppost.setEntity(new StringEntity(json.toString()));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            String result = httpclient.execute(httppost, responseHandler);

            return new JSONArray(result);
        } catch (Exception e) {
        }

        return new JSONArray();
    }

    public static boolean isMovieLibraryBeingUpdated(Context c) {
        ActivityManager manager = (ActivityManager) c.getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningServiceInfo> services = manager.getRunningServices(Integer.MAX_VALUE);
        int count = services.size();
        for (int i = 0; i < count; i++) {
            if (MovieLibraryUpdate.class.getName().equals(services.get(i).service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    public static boolean isTvShowLibraryBeingUpdated(Context c) {
        ActivityManager manager = (ActivityManager) c.getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningServiceInfo> services = manager.getRunningServices(Integer.MAX_VALUE);
        int count = services.size();
        for (int i = 0; i < count; i++) {
            if (TvShowsLibraryUpdate.class.getName().equals(services.get(i).service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    public static boolean isLocalCopyBeingDownloaded(Context c) {
        ActivityManager manager = (ActivityManager) c.getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningServiceInfo> services = manager.getRunningServices(Integer.MAX_VALUE);
        int count = services.size();
        for (int i = 0; i < count; i++) {
            if (MakeAvailableOffline.class.getName().equals(services.get(i).service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    public static final int HEIGHT = 100, WIDTH = 110;

    public static int getDisplaySize(Context c, int type) {
        WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();

        Point size = new Point();
        display.getSize(size);

        return type == HEIGHT ? size.y : size.x;
    }

    public static int getFileSizeLimit(Context c) {
        String limit = PreferenceManager.getDefaultSharedPreferences(c).getString("prefsIgnoreFilesSize",
                c.getString(R.string.smallFilesOption_1));
        if (limit.equals(c.getString(R.string.smallFilesOption_1))) {
            return 0;
        } else if (limit.equals(c.getString(R.string.smallFilesOption_2))) {
            return 50 * 1024 * 1024;
        } else if (limit.equals(c.getString(R.string.smallFilesOption_3))) {
            return 100 * 1024 * 1024;
        } else if (limit.equals(c.getString(R.string.smallFilesOption_4))) {
            return 150 * 1024 * 1024;
        } else if (limit.equals(c.getString(R.string.smallFilesOption_5))) {
            return 200 * 1024 * 1024;
        } else if (limit.equals(c.getString(R.string.smallFilesOption_6))) {
            return 250 * 1024 * 1024;
        } else if (limit.equals(c.getString(R.string.smallFilesOption_7))) {
            return 300 * 1024 * 1024;
        } else if (limit.equals(c.getString(R.string.smallFilesOption_8))) {
            return 350 * 1024 * 1024;
        } else if (limit.equals(c.getString(R.string.smallFilesOption_9))) {
            return 400 * 1024 * 1024;
        } else if (limit.equals(c.getString(R.string.smallFilesOption_10))) {
            return 450 * 1024 * 1024;
        } else if (limit.equals(c.getString(R.string.smallFilesOption_11))) {
            return 500 * 1024 * 1024;
        }
        return 50 * 1024 * 1024;
    }

    public static boolean findAndUpdateSpinner(Object root, int position) {
        if (root instanceof android.widget.Spinner) {
            // Found the Spinner
            Spinner spinner = (Spinner) root;
            spinner.setSelection(position);
            return true;
        } else if (root instanceof ViewGroup) {
            ViewGroup group = (ViewGroup) root;
            if (group.getId() != android.R.id.content) {
                // Found a container that isn't the container holding our screen layout
                for (int i = 0; i < group.getChildCount(); i++) {
                    if (findAndUpdateSpinner(group.getChildAt(i), position)) {
                        return true; // Found and done searching the View tree
                    }
                }
            }
        }

        return false; // Nothing found
    }

    public static File getOldDataFolder() {
        File dataFolder = new File(Environment.getExternalStorageDirectory().toString() + "/data/com.miz.mizuu");
        return dataFolder;
    }

    public static boolean oldDataFolderExists() {
        return getOldDataFolder().exists() && getOldDataFolder().list() != null;
    }

    public static File getDataFolder(Context c) {
        File f = c.getExternalFilesDir(null);
        f.mkdirs();
        return f;
    }

    public static File getMovieThumbFolder(Context c) {
        File f = new File(c.getExternalFilesDir(null) + "/movie-thumbs");
        f.mkdirs();
        return f;
    }

    public static File getMovieBackdropFolder(Context c) {
        File f = new File(c.getExternalFilesDir(null) + "/movie-backdrops");
        f.mkdirs();
        return f;
    }

    public static File getTvShowThumbFolder(Context c) {
        File f = new File(c.getExternalFilesDir(null) + "/tvshows-thumbs");
        f.mkdirs();
        return f;
    }

    public static File getTvShowBackdropFolder(Context c) {
        File f = new File(c.getExternalFilesDir(null) + "/tvshows-backdrops");
        f.mkdirs();
        return f;
    }

    public static File getTvShowEpisodeFolder(Context c) {
        File f = new File(c.getExternalFilesDir(null) + "/tvshows-episodes");
        f.mkdirs();
        return f;
    }

    public static File getTvShowSeasonFolder(Context c) {
        File f = new File(c.getExternalFilesDir(null) + "/tvshows-seasons");
        f.mkdirs();
        return f;
    }

    public static File getCacheFolder(Context c) {
        File f = new File(c.getExternalFilesDir(null) + "/app_cache");
        f.mkdirs();
        return f;
    }

    public static File getAvailableOfflineFolder(Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        boolean mAltOfflinePath = settings.getBoolean("prefsAltOfflinePath", false);

        File f;
        if (mAltOfflinePath)
            f = new File(altOfflinePath + "/offline_storage");
        else
            f = new File(c.getExternalFilesDir(null) + "/offline_storage");

        f.mkdirs();
        return f;
    }

    public static void copyFile(File src, File dst) throws IOException {
        InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(dst);

        // Transfer bytes from in to out
        byte[] buf = new byte[1024];
        int len;
        while ((len = in.read(buf)) > 0) {
            out.write(buf, 0, len);
        }
        in.close();
        out.close();
    }

    public static void moveFile(File src, File dst) throws IOException {
        copyFile(src, dst);
        src.delete();
    }

    public static void deleteRecursive(File fileOrDirectory) {
        if (fileOrDirectory.isDirectory()) {
            File[] listFiles = fileOrDirectory.listFiles();
            if (listFiles != null) {
                int count = listFiles.length;
                for (int i = 0; i < count; i++)
                    deleteRecursive(listFiles[i]);
            }
        }

        fileOrDirectory.delete();
    }

    public static String getTraktUserName(Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        return username;
    }

    public static JSONArray getTraktCalendar(Context c) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
        String username = settings.getString("traktUsername", "").trim();
        String password = settings.getString("traktPassword", "");

        if (username.isEmpty() || password.isEmpty())
            return new JSONArray();

        // Cancel the current check in to allow this one
        OkApacheClient httpclient = new OkApacheClient();
        HttpPost httppost = new HttpPost(
                "http://api.trakt.tv/user/calendar/shows.json/" + MizLib.TRAKT_API + "/" + username);
        httppost.setHeader("Content-type", "application/json");

        try {

            JSONObject holder = new JSONObject();
            holder.put("username", username);
            holder.put("password", password);

            StringEntity se = new StringEntity(holder.toString());
            se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
            httppost.setEntity(se);

            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            String result = httpclient.execute(httppost, responseHandler);

            return new JSONArray(result);
        } catch (Exception e) {
        }

        return new JSONArray();
    }

    public static String removeWikipediaNotes(String original) {
        original = original.trim().replaceAll("(?i)from wikipedia, the free encyclopedia.", "")
                .replaceAll("(?i)from wikipedia, the free encyclopedia", "");
        original = original.replaceAll("(?m)^[ \t]*\r?\n", "");
        if (original.contains("Description above from the Wikipedia")) {
            original = original.substring(0, original.lastIndexOf("Description above from the Wikipedia"));
        }

        return original.trim();
    }

    public static String getParentFolder(String filepath) {
        try {
            String pathWithoutEnding = filepath.substring(0, filepath.lastIndexOf("/"));
            return pathWithoutEnding;
        } catch (Exception e) {
            return "";
        }
    }

    public static void scheduleMovieUpdate(Context context) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);

        // Check if scheduled updates are enabled, and schedule the next update if this is the case
        if (settings.getInt(ScheduledUpdatesFragment.MOVIE_UPDATE_PREF,
                ScheduledUpdatesFragment.NOT_ENABLED) > ScheduledUpdatesFragment.AT_LAUNCH) {
            ScheduledUpdatesAlarmManager.cancelUpdate(ScheduledUpdatesAlarmManager.MOVIES, context);
            long duration = MizLib.HOUR;
            switch (settings.getInt(ScheduledUpdatesFragment.MOVIE_UPDATE_PREF,
                    ScheduledUpdatesFragment.NOT_ENABLED)) {
            case ScheduledUpdatesFragment.EVERY_2_HOURS:
                duration = MizLib.HOUR * 2;
                break;
            case ScheduledUpdatesFragment.EVERY_4_HOURS:
                duration = MizLib.HOUR * 4;
                break;
            case ScheduledUpdatesFragment.EVERY_6_HOURS:
                duration = MizLib.HOUR * 6;
                break;
            case ScheduledUpdatesFragment.EVERY_12_HOURS:
                duration = MizLib.HOUR * 12;
                break;
            case ScheduledUpdatesFragment.EVERY_DAY:
                duration = MizLib.DAY;
                break;
            case ScheduledUpdatesFragment.EVERY_WEEK:
                duration = MizLib.WEEK;
                break;
            }
            ScheduledUpdatesAlarmManager.startUpdate(ScheduledUpdatesAlarmManager.MOVIES, context, duration);
        }
    }

    public static void scheduleShowsUpdate(Context context) {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);

        // Check if scheduled updates are enabled, and schedule the next update if this is the case
        if (settings.getInt(ScheduledUpdatesFragment.SHOWS_UPDATE_PREF,
                ScheduledUpdatesFragment.NOT_ENABLED) > ScheduledUpdatesFragment.AT_LAUNCH) {
            ScheduledUpdatesAlarmManager.cancelUpdate(ScheduledUpdatesAlarmManager.SHOWS, context);
            long duration = MizLib.HOUR;
            switch (settings.getInt(ScheduledUpdatesFragment.SHOWS_UPDATE_PREF,
                    ScheduledUpdatesFragment.NOT_ENABLED)) {
            case ScheduledUpdatesFragment.EVERY_2_HOURS:
                duration = MizLib.HOUR * 2;
                break;
            case ScheduledUpdatesFragment.EVERY_4_HOURS:
                duration = MizLib.HOUR * 4;
                break;
            case ScheduledUpdatesFragment.EVERY_6_HOURS:
                duration = MizLib.HOUR * 6;
                break;
            case ScheduledUpdatesFragment.EVERY_12_HOURS:
                duration = MizLib.HOUR * 12;
                break;
            case ScheduledUpdatesFragment.EVERY_DAY:
                duration = MizLib.DAY;
                break;
            case ScheduledUpdatesFragment.EVERY_WEEK:
                duration = MizLib.WEEK;
                break;
            }
            ScheduledUpdatesAlarmManager.startUpdate(ScheduledUpdatesAlarmManager.SHOWS, context, duration);
        }
    }

    public static int flatPosition(ExpandableListView list, int groupPosition, int childPosition) {
        try {
            return list.getFlatListPosition(
                    ExpandableListView.getPackedPositionForChild(groupPosition, childPosition));
        } catch (Exception e) {
            return 0;
        }
    }

    public static boolean isImdbInstalled(Context c) {
        PackageManager pm = c.getPackageManager();
        try {
            pm.getPackageInfo("com.imdb.mobile", PackageManager.GET_ACTIVITIES);
            return true;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    public static void setTextOfCoverView(String text, View view) {
        if (view.getParent() instanceof ViewGroup) {
            ViewGroup group = (ViewGroup) view.getParent();
            if (group.getId() != android.R.id.content) {
                // Found a container that isn't the container holding our screen layout
                for (int i = 0; i < group.getChildCount(); i++) {
                    if (group.getChildAt(i).getId() == R.id.text) {
                        TextView tv = (TextView) group.getChildAt(i);
                        tv.setText(text);
                        tv.setVisibility(View.VISIBLE);
                        continue;
                    }
                }
            }
        }
    }

    public static int countOccurrences(String haystack, char needle) {
        int count = 0;
        char[] array = haystack.toCharArray();
        for (int i = 0; i < array.length; i++) {
            if (array[i] == needle) {
                ++count;
            }
        }
        return count;
    }

    public static DecryptedMovie decryptMovie(String filepath, String customTags) {
        DecryptedMovie mMovie = new DecryptedMovie();
        mMovie.setFilepath(filepath);
        mMovie.setFileNameYear(decryptYear(mMovie.getFileName()));
        mMovie.setDecryptedFileName(decryptName(mMovie.getFileName(), customTags));
        if (mMovie.hasParentName()) {
            mMovie.setParentNameYear(decryptYear(mMovie.getParentName()));
            mMovie.setDecryptedParentName(decryptName(mMovie.getParentName(), customTags));
        }

        return mMovie;
    }

    public static String decryptName(String input, String customTags) {
        String output = getNameFromFilename(input);
        output = fixAbbreviations(output);

        // Used to remove [SET {title}] from the beginning of filenames
        if (output.startsWith("[") && output.contains("]")) {
            String after = "";

            if (output.matches("(?i)^\\[SET .*\\].*?")) {
                try {
                    after = output.substring(output.indexOf("]") + 1, output.length());
                } catch (IndexOutOfBoundsException e) {
                }
            }

            if (!after.isEmpty())
                output = after;
        }

        output = output.replaceAll(WAREZ_PATTERN + "|\\)|\\(|\\[|\\]|\\{|\\}|\\'|\\<|\\>|\\-", "");

        // Improved support for French titles that start with C' or L'
        if (output.matches("(?i)^(c|l)(\\_|\\.)\\w.*?")) {
            StringBuilder sb = new StringBuilder(output);
            sb.replace(1, 2, "'");
            output = sb.toString();
        }

        if (!customTags.isEmpty()) {
            String[] custom = customTags.split("<MiZ>");
            int count = custom.length;
            for (int i = 0; i < count; i++)
                try {
                    output = output.replaceAll("(?i)" + custom[i], "");
                } catch (Exception e) {
                }
        }

        output = output.replaceAll("\\s\\-\\s|\\.|\\,|\\_", " "); // Remove separators
        output = output.trim().replaceAll("(?i)(part)$", ""); // Remove "part" in the end of the string

        return output.replaceAll(" +", " ").trim(); // replaceAll() needed to remove all instances of multiple spaces
    }

    private final static String YEAR_PATTERN = "(18|19|20)[0-9][0-9]";
    private final static String WAREZ_PATTERN = "(?i)(dvdscreener|dvdscreen|dvdscr|dvdrip|dvd5|dvd|xvid|divx|m\\-480p|m\\-576p|m\\-720p|m\\-864p|m\\-900p|m\\-1080p|m480p|m576p|m720p|m864p|m900p|m1080p|480p|576p|720p|864p|900p|1080p|1080i|720i|mhd|brrip|bdrip|brscreener|brscreen|brscr|aac|x264|bluray|dts|screener|hdtv|ac3|repack|2\\.1|5\\.1|ac3_6|7\\.1|h264|hdrip|ntsc|proper|readnfo|rerip|subbed|vcd|scvd|pdtv|sdtv|hqts|hdcam|multisubs|650mb|700mb|750mb|webdl|web-dl|bts|korrip|webrip|korsub|1link|sample|tvrip|tvr|extended.editions?|directors cut|tfe|unrated|\\(.*?torrent.*?\\)|\\[.*?\\]|\\(.*?\\)|\\{.*?\\}|part[0-9]|cd[0-9])";
    private final static String ABBREVIATION_PATTERN = "(?<=(^|[.])[\\S&&\\D])[.](?=[\\S&&\\D]([.]|$))";

    public static String getNameFromFilename(String input) {
        int lastIndex = 0;

        Pattern searchPattern = Pattern.compile(YEAR_PATTERN);
        Matcher searchMatcher = searchPattern.matcher(input);

        while (searchMatcher.find())
            lastIndex = searchMatcher.end();

        if (lastIndex > 0)
            try {
                return input.substring(0, lastIndex - 4);
            } catch (IndexOutOfBoundsException e) {
            }

        return input;
    }

    public static String fixAbbreviations(String input) {
        return input.replaceAll(ABBREVIATION_PATTERN, "");
    }

    public static String decryptYear(String input) {
        String result = "";
        Pattern searchPattern = Pattern.compile(YEAR_PATTERN);
        Matcher searchMatcher = searchPattern.matcher(input);

        while (searchMatcher.find()) {
            try {
                int lastIndex = searchMatcher.end();
                result = input.substring(lastIndex - 4, lastIndex);
            } catch (IndexOutOfBoundsException e) {
            }
        }

        return result;
    }

    public static DecryptedShowEpisode decryptEpisode(String filepath, String customTags) {
        DecryptedShowEpisode mEpisode = new DecryptedShowEpisode();
        mEpisode.setFilepath(filepath);
        mEpisode.setFileNameYear(decryptYear(mEpisode.getFileName()));
        mEpisode.setDecryptedFileName(
                decryptName(getStringBeforeSeasonAndEpisode(mEpisode.getFileName()), customTags));
        mEpisode.setEpisode(getEpisodeOrSeasonFromFileName(mEpisode.getFileName(), EPISODE));
        mEpisode.setSeason(getEpisodeOrSeasonFromFileName(mEpisode.getFileName(), SEASON));

        if (mEpisode.hasSeasonsFolder()) { // There's a season folder in the file structure - let's overwrite the season number with the one in the folder name
            mEpisode.setSeason(getSeasonFromFolderName(mEpisode.getSeasonFolder()));
        }

        if (mEpisode.hasParentName()) {
            mEpisode.setParentNameYear(decryptYear(mEpisode.getParentName()));
            mEpisode.setDecryptedParentName(
                    decryptName(getStringBeforeSeason(mEpisode.getParentName()), customTags));
        }

        return mEpisode;
    }

    public static boolean folderNameContainsSeasonNumber(String folderName) {

        // S##
        Pattern searchPattern = Pattern.compile("(?i)s(\\d){1,2}");
        Matcher searchMatcher = searchPattern.matcher(folderName);

        if (searchMatcher.find())
            return true;

        // Season ##
        searchPattern = Pattern.compile("(?i)season((\\s)+)(\\d){1,2}");
        searchMatcher = searchPattern.matcher(folderName);

        if (searchMatcher.find())
            return true;

        // ##
        searchPattern = Pattern.compile("(\\d){1,2}(?!\\S)");
        searchMatcher = searchPattern.matcher(folderName);

        if (searchMatcher.find())
            return true;

        return false;
    }

    public static int SEASON = 0, EPISODE = 1;

    public static int getEpisodeOrSeasonFromFileName(String input, int type) {
        int episode = 0, season = 0;

        // ##e##
        Pattern searchPattern = Pattern.compile("(?i)(\\d){1,4}e(\\d){1,3}");
        Matcher searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                String result = input.substring(searchMatcher.start(), searchMatcher.end());
                episode = Integer.valueOf(result.split("(?i)e")[1].trim());
                season = Integer.valueOf(result.split("(?i)e")[0].trim());

                if (type == SEASON)
                    return season;
                return episode;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // ##x##
        searchPattern = Pattern.compile("(?i)(\\d){1,4}x(\\d){1,3}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                String result = input.substring(searchMatcher.start(), searchMatcher.end());
                episode = Integer.valueOf(result.split("(?i)x")[1].trim());
                season = Integer.valueOf(result.split("(?i)x")[0].trim());

                if (type == SEASON)
                    return season;
                return episode;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // season ## episode ##
        searchPattern = Pattern.compile("(?i)season((\\s)+)(\\d){1,4}((\\s)+)episode((\\s)+)(\\d){1,3}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                String result = input.substring(searchMatcher.start(), searchMatcher.end());
                episode = Integer.valueOf(result.split("(?i)episode")[1].trim());
                season = Integer.valueOf(result.split("(?i)episode")[0].replaceAll("(?i)season", "").trim());

                if (type == SEASON)
                    return season;
                return episode;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // episode ##
        searchPattern = Pattern.compile("(?i)episode((\\s)+)(\\d){1,3}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                String result = input.substring(searchMatcher.start(), searchMatcher.end());
                episode = Integer.valueOf(result.split("(?i)episode")[1].trim());
                // No information found about the season number, return 0

                if (type == SEASON)
                    return season;
                return episode;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // #####
        searchPattern = Pattern.compile("(\\d){1,5}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                String result = input.substring(searchMatcher.start(), searchMatcher.end());

                switch (result.length()) {
                case 1: // # (episode)
                    episode = Integer.valueOf(result);
                    // No information found about the season number, return 0
                    break;
                case 2: // ## (episode)
                    episode = Integer.valueOf(result);
                    // No information found about the season number, return 0
                    break;
                case 3: // ### (season [1], episode [2])
                    episode = Integer.valueOf(result.substring(1, 3));
                    season = Integer.valueOf(result.substring(0, 1));
                    break;
                case 4: // #### (season [2], episode [2])
                    episode = Integer.valueOf(result.substring(2, 4));
                    season = Integer.valueOf(result.substring(0, 2));
                    break;
                case 5: // ##### (season [2], episode [3])
                    episode = Integer.valueOf(result.substring(2, 5));
                    season = Integer.valueOf(result.substring(0, 2));
                    break;
                case 6: // ##### (season [3], episode [3])
                    episode = Integer.valueOf(result.substring(3, 6));
                    season = Integer.valueOf(result.substring(0, 3));
                    break;
                case 7: // ##### (season [4], episode [3])
                    episode = Integer.valueOf(result.substring(4, 7));
                    season = Integer.valueOf(result.substring(0, 4));
                    break;
                }

                if (type == SEASON)
                    return season;
                return episode;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        return episode;
    }

    public static String getStringBeforeSeasonAndEpisode(String input) {
        String result = input;

        // s##e##
        Pattern searchPattern = Pattern.compile("(?i)s(\\d){1,4}e(\\d){1,3}");
        Matcher searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                result = input.substring(0, searchMatcher.start());
                return result;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // ##e##
        searchPattern = Pattern.compile("(?i)(\\d){1,4}e(\\d){1,3}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                result = input.substring(0, searchMatcher.start());
                return result;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // s##x##
        searchPattern = Pattern.compile("(?i)s(\\d){1,4}x(\\d){1,3}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                result = input.substring(0, searchMatcher.start());
                return result;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // ##x##
        searchPattern = Pattern.compile("(?i)(\\d){1,4}x(\\d){1,3}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                result = input.substring(0, searchMatcher.start());
                return result;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // season ## episode ##
        searchPattern = Pattern.compile("(?i)season((\\s)+)(\\d){1,4}((\\s)+)episode((\\s)+)(\\d){1,3}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                result = input.substring(0, searchMatcher.start());
                return result;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // episode ##
        searchPattern = Pattern.compile("(?i)episode((\\s)+)(\\d){1,3}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                result = input.substring(0, searchMatcher.start());
                return result;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // e#####
        searchPattern = Pattern.compile("(?i)e(\\d){1,5}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                result = input.substring(0, searchMatcher.start());
                return result;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // #####
        searchPattern = Pattern.compile("(\\d){1,5}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                result = input.substring(0, searchMatcher.start());
                return result;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        return result;
    }

    public static String getStringBeforeSeason(String input) {
        String result = input;

        // s##
        Pattern searchPattern = Pattern.compile("(?i)s(\\d){1,4}");
        Matcher searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                result = input.substring(0, searchMatcher.start());
                return result;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // season ## episode ##
        searchPattern = Pattern.compile("(?i)season((\\s)+)(\\d){1,4}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                result = input.substring(0, searchMatcher.start());
                return result;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        return result;
    }

    public static int getSeasonFromFolderName(String input) {
        int season = 0;

        // S##
        Pattern searchPattern = Pattern.compile("(?i)s(\\d){1,4}");
        Matcher searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                String result = input.substring(searchMatcher.start(), searchMatcher.end());
                season = Integer.valueOf(result.replaceAll("(?i)s", "").trim());

                return season;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // season ##
        searchPattern = Pattern.compile("(?i)season((\\s)+)(\\d){1,4}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                String result = input.substring(searchMatcher.start(), searchMatcher.end());
                season = Integer.valueOf(result.replaceAll("(?i)season", "").trim());

                return season;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        // ##
        searchPattern = Pattern.compile("(\\d){1,4}");
        searchMatcher = searchPattern.matcher(input);

        if (searchMatcher.find()) {
            try {
                String result = input.substring(searchMatcher.start(), searchMatcher.end());

                switch (result.length()) {
                case 1: // # (episode)
                    season = Integer.valueOf(result);
                    // No information found about the season number, return 0
                    break;
                case 2: // ## (episode)
                    season = Integer.valueOf(result);
                    // No information found about the season number, return 0
                    break;
                case 3: // ### (season [1], episode [2])
                    season = Integer.valueOf(result.substring(0, 1));
                    break;
                case 4: // #### (season [2], episode [2])
                    season = Integer.valueOf(result.substring(0, 2));
                    break;
                case 5: // ##### (season [2], episode [3])
                    season = Integer.valueOf(result.substring(0, 2));
                    break;
                case 6: // ##### (season [3], episode [3])
                    season = Integer.valueOf(result.substring(0, 3));
                    break;
                case 7: // ##### (season [4], episode [3])
                    season = Integer.valueOf(result.substring(0, 4));
                    break;
                }

                return season;
            } catch (IndexOutOfBoundsException e) {
            }
        }

        return season;
    }

    /**
     * Decode and sample down a bitmap from a file to the requested width and height.
     *
     * @param filename The full path of the file to decode
     * @param reqWidth The requested width of the resulting bitmap
     * @param reqHeight The requested height of the resulting bitmap
     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
     *         that are equal to or greater than the requested width and height
     */
    public static Bitmap decodeSampledBitmapFromFile(String filename, int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filename, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inMutable = true;
        options.inPreferredConfig = Config.RGB_565;

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;

        return BitmapFactory.decodeFile(filename, options);
    }

    /**
     * Decode and sample down a bitmap from resources to the requested width and height.
     *
     * @param res The resources object containing the image data
     * @param resId The resource id of the image data
     * @param reqWidth The requested width of the resulting bitmap
     * @param reqHeight The requested height of the resulting bitmap
     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
     *         that are equal to or greater than the requested width and height
     */
    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    /**
     * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
     * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
     * the closest inSampleSize that will result in the final decoded bitmap having a width and
     * height equal to or larger than the requested width and height. This implementation does not
     * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
     * results in a larger bitmap which isn't as useful for caching purposes.
     *
     * @param options An options object with out* params already populated (run through a decode*
     *            method with inJustDecodeBounds==true
     * @param reqWidth The requested width of the resulting bitmap
     * @param reqHeight The requested height of the resulting bitmap
     * @return The value to be used for inSampleSize
     */
    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);

            // Choose the smallest ratio as inSampleSize value, this will guarantee a final image
            // with both dimensions larger than or equal to the requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

            // This offers some additional logic in case the image has a strange
            // aspect ratio. For example, a panorama may have a much larger
            // width than height. In these cases the total pixels might still
            // end up being too large to fit comfortably in memory, so we should
            // be more aggressive with sample down the image (=larger inSampleSize).

            final float totalPixels = width * height;

            // Anything more than 2x the requested pixels we'll sample down further
            final float totalReqPixelsCap = reqWidth * reqHeight * 2;

            while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
                inSampleSize++;
            }
        }
        return inSampleSize;
    }

    @SuppressWarnings("deprecation")
    public static long getFreeMemory() {
        StatFs stat = new StatFs(Environment.getDataDirectory().getPath());
        if (hasJellyBeanMR2())
            return stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
        else
            return stat.getAvailableBlocks() * stat.getBlockSize();
    }

    private static String[] MEDIA_APPS = new String[] { "com.imdb.mobile", "com.google.android.youtube",
            "com.ted.android", "com.google.android.videos", "se.mtg.freetv.tv3_dk", "tv.twitch.android.viewer",
            "com.netflix.mediaclient", "com.gotv.crackle.handset", "net.flixster.android", "com.google.tv.alf",
            "com.viki.android", "com.mobitv.client.mobitv", "com.hulu.plus.jp", "com.hulu.plus",
            "com.mobitv.client.tv", "air.com.vudu.air.DownloaderTablet", "com.hbo.android.app", "com.HBO",
            "bbc.iplayer.android", "air.uk.co.bbc.android.mediaplayer", "com.rhythmnewmedia.tvdotcom",
            "com.cnettv.app", "com.xfinity.playnow" };

    public static boolean isMediaApp(ApplicationInfo ai) {
        for (int i = 0; i < MEDIA_APPS.length; i++)
            if (MEDIA_APPS[i].equals(ai.packageName))
                return true;
        return false;
    }

    private static int mRuntimeInMinutes;

    public static String getRuntimeInMinutesOrHours(String runtime, String hour, String minute) {
        mRuntimeInMinutes = Integer.valueOf(runtime);
        if (mRuntimeInMinutes >= 60) {
            return (mRuntimeInMinutes / 60) + hour;
        }
        return mRuntimeInMinutes + minute;
    }

    public static int getPartNumberFromFilepath(String filepath) {
        if (filepath.matches(".*part[1-9].*"))
            filepath = filepath.substring(filepath.lastIndexOf("part") + 4, filepath.length());
        else if (filepath.matches(".*cd[1-9].*"))
            filepath = filepath.substring(filepath.lastIndexOf("cd") + 2, filepath.length());

        filepath = filepath.substring(0, 1);

        try {
            return Integer.valueOf(filepath);
        } catch (NumberFormatException nfe) {
            return 0;
        }
    }

    public static List<String> getSplitParts(String filepath, NtlmPasswordAuthentication auth)
            throws MalformedURLException, UnsupportedEncodingException, SmbException {
        ArrayList<String> parts = new ArrayList<String>();

        String fileType = "";
        if (filepath.contains(".")) {
            fileType = filepath.substring(filepath.lastIndexOf("."));
        }

        if (filepath.matches(".*part[1-9].*"))
            filepath = filepath.substring(0, filepath.lastIndexOf("part") + 4);
        else if (filepath.matches(".*cd[1-9].*"))
            filepath = filepath.substring(0, filepath.lastIndexOf("cd") + 2);

        if (auth == null) { // Check if it's a local file
            File temp;
            for (int i = 1; i < 10; i++) {
                temp = new File(filepath + i + fileType);
                if (temp.exists())
                    parts.add(temp.getAbsolutePath());
            }
        } else { // It's a network file
            SmbFile temp;
            for (int i = 1; i < 10; i++) {
                temp = new SmbFile(createSmbLoginString(URLEncoder.encode(auth.getDomain(), "utf-8"),
                        URLEncoder.encode(auth.getUsername(), "utf-8"),
                        URLEncoder.encode(auth.getPassword(), "utf-8"), filepath + i + fileType, false));
                if (temp.exists())
                    parts.add(temp.getPath());
            }
        }

        return parts;
    }

    public static String transformSmbPath(String smbPath) {
        if (smbPath.contains("smb") && smbPath.contains("@"))
            return "smb://" + smbPath.substring(smbPath.indexOf("@") + 1);
        return smbPath.replace("/smb:/", "smb://");
    }

    public static int getNavigationBarHeight(Context context) {
        Resources resources = context.getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            return resources.getDimensionPixelSize(resourceId);
        }
        return 0;
    }

    public static int getNavigationBarWidth(Context context) {
        Resources resources = context.getResources();
        int resourceId = resources.getIdentifier("navigation_bar_width", "dimen", "android");
        if (resourceId > 0) {
            return resources.getDimensionPixelSize(resourceId);
        }
        return 0;
    }

    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);
        final float roundPx = pixels;

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        return output;
    }

    public static String getLatestBackdropPath(Context c) {
        File latestMovie = lastFileModified(getMovieBackdropFolder(c));
        File latestShow = lastFileModified(getTvShowBackdropFolder(c));
        if (latestMovie != null && latestShow != null) {
            if (latestMovie.lastModified() > latestShow.lastModified())
                return latestMovie.getAbsolutePath();
            return latestShow.getAbsolutePath();
        } else if (latestMovie != null) {
            return latestMovie.getAbsolutePath();
        } else if (latestShow != null) {
            return latestShow.getAbsolutePath();
        }
        return "";
    }

    public static File lastFileModified(File dir) {
        File[] files = dir.listFiles(new FileFilter() {
            public boolean accept(File file) {
                return file.isFile();
            }
        });

        if (files != null) {
            long lastMod = Long.MIN_VALUE;
            File choice = null;
            for (int i = 0; i < files.length; i++) {
                if (files[i].lastModified() > lastMod) {
                    choice = files[i];
                    lastMod = files[i].lastModified();
                }
            }
            return choice;
        }
        return null;
    }

    public static String getFileExtension(String path) {
        String extension = "";

        int i = path.lastIndexOf('.');
        if (i > 0)
            extension = path.substring(i + 1);

        return extension;
    }

    public static boolean isValidFilename(String name) {
        return !(name.startsWith(".") && MizLib.getCharacterCountInString(name, '.') == 1)
                && !name.startsWith("._");
    }

    public static boolean exists(String URLName) {
        try {
            HttpURLConnection.setFollowRedirects(false);
            HttpURLConnection con = (HttpURLConnection) new URL(URLName).openConnection();
            con.setRequestMethod("HEAD");
            con.setConnectTimeout(10000);
            return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * Determines if the device uses navigation controls as the primary navigation from a number of factors.
     * @param context Application Context
     * @return True if the device uses navigation controls, false otherwise.
     */
    public static boolean usesNavigationControl(Context context) {
        Configuration configuration = context.getResources().getConfiguration();
        if (configuration.navigation == Configuration.NAVIGATION_NONAV) {
            return false;
        } else if (configuration.touchscreen == Configuration.TOUCHSCREEN_FINGER) {
            return false;
        } else if (configuration.navigation == Configuration.NAVIGATION_DPAD) {
            return true;
        } else if (configuration.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH) {
            return true;
        } else if (configuration.touchscreen == Configuration.TOUCHSCREEN_UNDEFINED) {
            return true;
        } else if (configuration.navigationHidden == Configuration.NAVIGATIONHIDDEN_YES) {
            return true;
        } else if (configuration.uiMode == Configuration.UI_MODE_TYPE_TELEVISION) {
            return true;
        }
        return false;
    }

    public static int getFileSize(URL url) {
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("HEAD");
            conn.getInputStream();
            return conn.getContentLength();
        } catch (IOException e) {
            return -1;
        } finally {
            conn.disconnect();
        }
    }

    public static String getPrettyTime(Context context, int minutes) {
        if (minutes == 0)
            return context.getString(R.string.stringNA);
        ;
        try {
            int hours = (minutes / 60);
            minutes = (minutes % 60);
            String hours_string = hours + " "
                    + context.getResources().getQuantityString(R.plurals.hour, hours, hours);
            String minutes_string = minutes + " "
                    + context.getResources().getQuantityString(R.plurals.minute, minutes, minutes);
            if (hours > 0) {
                if (minutes == 0)
                    return hours_string;
                else
                    return hours_string + " " + minutes_string;
            } else {
                return minutes_string;
            }
        } catch (Exception e) { // Fall back if something goes wrong
            if (minutes > 0)
                return String.valueOf(minutes);
            return context.getString(R.string.stringNA);
        }
    }

    public static String getPrettyDate(Context context, String date) {
        if (!MizLib.isEmpty(date)) {
            try {
                String[] dateArray = date.split("-");
                Calendar cal = Calendar.getInstance();
                cal.set(Integer.parseInt(dateArray[0]), Integer.parseInt(dateArray[1]) - 1,
                        Integer.parseInt(dateArray[2]));

                return DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()).format(cal.getTime());
            } catch (Exception e) { // Fall back if something goes wrong
                return date;
            }
        } else {
            return context.getString(R.string.stringNA);
        }
    }

    public static String getPrettyDate(Context context, long millis) {
        if (millis > 0) {
            try {
                Calendar cal = Calendar.getInstance();
                cal.setTimeInMillis(millis);

                return DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()).format(cal.getTime());
            } catch (Exception e) { // Fall back if something goes wrong
                return String.valueOf(millis);
            }
        } else {
            return context.getString(R.string.stringNA);
        }
    }

    public static Comparator<WebMovie> getWebMovieDateComparator() {
        return new Comparator<WebMovie>() {
            @Override
            public int compare(WebMovie o1, WebMovie o2) {
                // Dates are always presented as YYYY-MM-DD, so removing
                // the hyphens will easily provide a great way of sorting.

                int firstDate = 0, secondDate = 0;
                String first = "", second = "";

                if (o1.getDate() != null)
                    first = o1.getDate().replace("-", "");

                if (!(first.equals("null") | first.isEmpty()))
                    firstDate = Integer.valueOf(first);

                if (o2.getDate() != null)
                    second = o2.getDate().replace("-", "");

                if (!(second.equals("null") | second.isEmpty()))
                    secondDate = Integer.valueOf(second);

                // This part is reversed to get the highest numbers first
                if (firstDate < secondDate)
                    return 1; // First date is lower than second date - put it second
                else if (firstDate > secondDate)
                    return -1; // First date is greater than second date - put it first

                return 0; // They're equal
            }
        };
    }

    private static String[] mAdultKeywords = new String[] { "adult", "sex", "porn", "explicit", "penis", "vagina",
            "asshole", "blowjob", "cock", "fuck", "dildo", "kamasutra", "masturbat", "squirt", "slutty", "cum",
            "cunt" };

    public static boolean isAdultContent(Context context, String title) {
        // Check if the user has enabled adult content - if so, nothing should
        // be blocked and the method should return false regardless of the title
        if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean("prefsIncludeAdultContent", false))
            return false;

        String lowerCase = title.toLowerCase(Locale.getDefault());

        // Run through the keywords and check
        for (int i = 0; i < mAdultKeywords.length; i++)
            if (lowerCase.contains(mAdultKeywords[i]))
                return true;

        // Certain titles include "XXX" (all caps), so test this against the normal-case title as a last check
        return title.contains("XXX");
    }

    public static String getMimeType(String filepath, boolean useWildcard) {
        if (useWildcard)
            return "video/*";

        HashMap<String, String> mimeTypes = new HashMap<String, String>();
        mimeTypes.put("3gp", "video/3gpp");
        mimeTypes.put("aaf", "application/octet-stream");
        mimeTypes.put("mp4", "video/mp4");
        mimeTypes.put("ts", "video/mp2t");
        mimeTypes.put("webm", "video/webm");
        mimeTypes.put("m4v", "video/x-m4v");
        mimeTypes.put("mkv", "video/x-matroska");
        mimeTypes.put("divx", "video/x-divx");
        mimeTypes.put("xvid", "video/x-xvid");
        mimeTypes.put("rec", "application/octet-stream");
        mimeTypes.put("avi", "video/avi");
        mimeTypes.put("flv", "video/x-flv");
        mimeTypes.put("f4v", "video/x-f4v");
        mimeTypes.put("moi", "application/octet-stream");
        mimeTypes.put("mpeg", "video/mpeg");
        mimeTypes.put("mpg", "video/mpeg");
        mimeTypes.put("mts", "video/mts");
        mimeTypes.put("m2ts", "video/mp2t");
        mimeTypes.put("ogv", "video/ogg");
        mimeTypes.put("rm", "application/vnd.rn-realmedia");
        mimeTypes.put("rmvb", "application/vnd.rn-realmedia-vbr");
        mimeTypes.put("mov", "video/quicktime");
        mimeTypes.put("wmv", "video/x-ms-wmv");
        mimeTypes.put("iso", "application/octet-stream");
        mimeTypes.put("vob", "video/dvd");
        mimeTypes.put("ifo", "application/octet-stream");
        mimeTypes.put("wtv", "video/wtv");
        mimeTypes.put("pyv", "video/vnd.ms-playready.media.pyv");
        mimeTypes.put("ogm", "video/ogg");
        mimeTypes.put("img", "application/octet-stream");

        String mime = mimeTypes.get(getExtension(filepath));
        if (mime == null)
            return "video/*";
        return mime;
    }

    /*
     * START OF APACHE COMMONS CODE
     * from http://svn.apache.org/repos/asf/commons/proper/io/trunk/src/main/java/org/apache/commons/io/FilenameUtils.java
     */

    /*
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */

    private static final int NOT_FOUND = -1;
    public static final char EXTENSION_SEPARATOR = '.';
    private static final char UNIX_SEPARATOR = '/';
    private static final char WINDOWS_SEPARATOR = '\\';

    public static String getExtension(final String filename) {
        if (filename == null) {
            return null;
        }
        final int index = indexOfExtension(filename);
        if (index == NOT_FOUND) {
            return "";
        } else {
            return filename.substring(index + 1);
        }
    }

    public static int indexOfExtension(final String filename) {
        if (filename == null) {
            return NOT_FOUND;
        }
        final int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);
        final int lastSeparator = indexOfLastSeparator(filename);
        return lastSeparator > extensionPos ? NOT_FOUND : extensionPos;
    }

    public static int indexOfLastSeparator(final String filename) {
        if (filename == null) {
            return NOT_FOUND;
        }
        final int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR);
        final int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);
        return Math.max(lastUnixPos, lastWindowsPos);
    }

    /*
     * END OF APACHE COMMONS CODE
     */

    public static boolean isNumber(String runtime) {
        return TextUtils.isDigitsOnly(runtime);
    }
}