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 android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ApplicationInfo;
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.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.os.StatFs;
import android.preference.PreferenceManager;
import android.support.v8.renderscript.Allocation;
import android.support.v8.renderscript.Element;
import android.support.v8.renderscript.RenderScript;
import android.support.v8.renderscript.ScriptIntrinsicBlur;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.Toast;

import com.miz.db.DbAdapterMovies;
import com.miz.db.DbAdapterSources;
import com.miz.db.DbAdapterTvShowEpisodes;
import com.miz.db.DbAdapterTvShows;
import com.miz.mizuu.MizuuApplication;
import com.miz.mizuu.R;
import com.miz.mizuu.TvShow;
import com.miz.mizuu.fragments.ScheduledUpdatesFragment;
import com.miz.service.MovieLibraryUpdate;
import com.miz.service.TvShowsLibraryUpdate;
import com.miz.utils.FileUtils;
import com.miz.utils.ViewUtils;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
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.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

import static com.miz.functions.PreferenceKeys.INCLUDE_ADULT_CONTENT;
import static com.miz.functions.PreferenceKeys.SCHEDULED_UPDATES_MOVIE;
import static com.miz.functions.PreferenceKeys.SCHEDULED_UPDATES_TVSHOWS;
import static com.miz.functions.PreferenceKeys.TMDB_BASE_URL;
import static com.miz.functions.PreferenceKeys.TMDB_BASE_URL_TIME;
import static com.miz.functions.PreferenceKeys.TRAKT_USERNAME;

@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 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 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;

    private MizLib() {
    } // No instantiation

    public static String getTmdbApiKey(Context context) {
        String key = context.getString(R.string.tmdb_api_key);
        if (TextUtils.isEmpty(key) || key.equals("add_your_own"))
            throw new RuntimeException("You need to add a TMDb API key!");
        return key;
    }

    /* public static String getTvdbApiKey(Context context) {
    String key = context.getString(R.string.tvdb_api_key);
    if (TextUtils.isEmpty(key) || key.equals("add_your_own"))
        throw new RuntimeException("You need to add a TVDb API key!");
    return key;
     }*/

    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 (TextUtils.isEmpty(s))
            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 (TextUtils.isEmpty(s))
            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])).append(" ");
        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 (TextUtils.isEmpty(s))
            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(" ").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 (TextUtils.isEmpty(s))
            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 (!TextUtils.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) {
        return c != null && (c.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
    }

    /**
     * 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) {
        if (c != null) {
            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 combined height of the ActionBar and Status bar to the top of a given View
     * @param c
     * @param v
     */
    public static void addActionBarAndStatusBarPadding(Context c, View v) {
        int mActionBarHeight = 0, mStatusBarHeight = 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)

        if (hasKitKat())
            mStatusBarHeight = convertDpToPixels(c, 25);

        v.setPadding(0, mActionBarHeight + mStatusBarHeight, 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);
    }

    /**
     * Add a margin with a combined height of the ActionBar and Status bar to the top of a given View contained in a FrameLayout
     * @param c
     * @param v
     */
    public static void addActionBarAndStatusBarMargin(Context c, View v) {
        int mActionBarHeight = 0, mStatusBarHeight = 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)

        if (hasKitKat())
            mStatusBarHeight = convertDpToPixels(c, 25);

        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT);
        params.setMargins(0, mActionBarHeight + mStatusBarHeight, 0, 0);

        v.setLayoutParams(params);
    }

    /**
     * Add a margin with a combined height of the ActionBar and Status bar to the top of a given View contained in a FrameLayout
     * @param c
     * @param v
     */
    public static void addActionBarAndStatusBarMargin(Context c, View v, FrameLayout.LayoutParams layoutParams) {
        int mActionBarHeight = 0, mStatusBarHeight = 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)

        if (hasKitKat())
            mStatusBarHeight = convertDpToPixels(c, 25);

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

    /**
     * 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 addActionBarMarginBottom(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, 0, 0, mActionBarHeight);
        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 hasICSMR1() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1;
    }

    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 boolean isKitKat() {
        return Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT;
    }

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

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

    public static int getLargeNotificationWidth(Context c) {
        Resources r = c.getResources();
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 360, 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 actionBarHeight = 0;
        TypedValue tv = new TypedValue();
        if (c.getTheme().resolveAttribute(R.attr.actionBarSize, tv, true))
            actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, c.getResources().getDisplayMetrics());
        else
            actionBarHeight = 0; // No ActionBar style (pre-Honeycomb or ActionBar not in theme)

        return actionBarHeight;
    }

    public static int getActionBarAndStatusBarHeight(Context c) {
        int actionBarHeight = getActionBarHeight(c);
        int statusBarHeight = ViewUtils.getStatusBarHeight(c);

        // We're only interested in returning the combined
        // height, if we're running on KitKat or above
        return hasKitKat() ? actionBarHeight + statusBarHeight : actionBarHeight;
    }

    public static 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 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(Math.max(size.x, size.y) / (mImageThumbSize + mImageThumbSpacing));

        if (numColumns > 0) {
            final int columnWidth = (Math.max(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(Math.max(size.x, size.y) / (mImageThumbSize + mImageThumbSpacing));

        if (numColumns > 0) {
            final int columnWidth = (Math.max(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(Math.max(size.x, size.y) / (mImageThumbSize + mImageThumbSpacing));

        if (numColumns > 0) {
            final int columnWidth = (Math.max(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 = Math.max(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";
        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 = Math.min(size.x, size.y);

        if (width >= 780)
            return "w780";
        if (width >= 400)
            return "w500";
        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(Math.max(size.x, size.y) / (mImageThumbSize + mImageThumbSpacing));

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

            if (columnWidth > 400)
                return "h632";

            if (columnWidth >= 300)
                return "w300";
        }

        return "w185";
    }

    public static boolean checkFileTypes(String file) {
        // We don't want to include files that start with ._ or .DS_Store
        if (file.matches("(?i).*[/][\\.](?:_|DS_Store).*[\\.].*$"))
            return false;

        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 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 RenderScript to blur the bitmap very fast.
     * @param context
     * @param originalBitmap
     * @param radius
     * @return
     */
    public static Bitmap fastBlur(Context context, Bitmap originalBitmap, int radius) {
        final RenderScript rs = RenderScript.create(context);

        final Allocation input = Allocation.createFromBitmap(rs, originalBitmap);
        final Allocation output = Allocation.createTyped(rs, input.getType());
        final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        script.setRadius(radius);
        script.setInput(input);
        script.forEach(output);
        output.copyTo(originalBitmap);

        return originalBitmap;
    }

    public static boolean downloadFile(String url, String savePath) {
        if (TextUtils.isEmpty(url))
            return false;

        InputStream in = null;
        OutputStream fileos = null;

        try {
            int bufferSize = 8192;
            byte[] retVal = null;

            Request request = new Request.Builder().url(url).build();

            Response response = MizuuApplication.getOkHttpClient().newCall(request).execute();
            if (!response.isSuccessful())
                return false;

            fileos = new BufferedOutputStream(new FileOutputStream(savePath));
            in = new BufferedInputStream(response.body().byteStream(), bufferSize);

            retVal = new byte[bufferSize];
            int length = 0;
            while ((length = in.read(retVal)) > -1) {
                fileos.write(retVal, 0, length);
            }
        } catch (Exception e) {
            // The download failed, so let's delete whatever was downloaded
            deleteFile(new File(savePath));

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

            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
        }

        return true;
    }

    public static JSONObject getJSONObject(Context context, String url) {
        final OkHttpClient client = MizuuApplication.getOkHttpClient();

        Request request = new Request.Builder().url(url).get().build();

        try {
            Response response = client.newCall(request).execute();

            if (response.code() >= 429) {
                // HTTP error 429 and above means that we've exceeded the query limit
                // for TMDb. Sleep for 5 seconds and try again.
                Thread.sleep(5000);
                response = client.newCall(request).execute();
            }
            return new JSONObject(response.body().string());
        } catch (Exception e) { // IOException and JSONException
            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 int getInteger(String number) {
        try {
            return Integer.valueOf(number);
        } catch (NumberFormatException nfe) {
            return 0;
        }
    }

    public static int getInteger(double number) {
        try {
            return (int) number;
        } catch (Exception e) {
            return 0;
        }
    }

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

    public static String addIndexZero(String s) {
        if (TextUtils.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 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();

        ColumnIndexCache cache = new ColumnIndexCache();

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

        return filesources;
    }

    public static SmbLogin getLoginFromFilesource(FileSource source) {
        if (source == null || TextUtils.isEmpty(source.getDomain()) && TextUtils.isEmpty(source.getUser())
                && TextUtils.isEmpty(source.getPassword())) {
            return new SmbLogin();
        } else {
            return new SmbLogin(source.getDomain(), source.getUser(), source.getPassword());
        }
    }

    public static SmbLogin getLoginFromFilepath(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);
                break;
            }
        }

        return getLoginFromFilesource(source);
    }

    public static int COVER = 1, BACKDROP = 2;

    public static SmbFile getCustomCoverArt(String filepath, SmbLogin 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(auth.getDomain(), auth.getUsername(), auth.getPassword(),
                    parentPath, false));

            try {
                list = s.list();

                MizuuApplication.putCifsFilesList(parentPath, list);
            } catch (Exception e) {
                return null;
            }
        }

        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;
                    break;
                }
            }
        } else {
            for (int i = 0; i < list.length; i++) {
                name = list[i];
                absolutePath = parentPath + list[i];
                if (name.equalsIgnoreCase("fanart.jpg") || 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;
                    break;
                }
            }
        }

        if (!TextUtils.isEmpty(customCoverArt))
            return new SmbFile(createSmbLoginString(auth.getDomain(), auth.getUsername(), auth.getPassword(),
                    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, SmbLogin 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(auth.getDomain(), auth.getUsername(), auth.getPassword(),
                    filepath.replace(fileType, subtitleFormats[i]), false)));
        }

        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("smb://");

        try {
            user = URLEncoder.encode(user, "utf-8");
        } catch (UnsupportedEncodingException e) {
        }
        try {
            password = URLEncoder.encode(password, "utf-8");
        } catch (UnsupportedEncodingException e) {
        }
        try {
            domain = URLEncoder.encode(domain, "utf-8");
        } catch (UnsupportedEncodingException e) {
        }

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

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

            // Add username
            sb.append(user);

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

            sb.append("@");
        }

        sb.append(server);

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

        return sb.toString();
    }

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

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

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

                // Delete thumbnail image
                File thumbnail = 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 = MizuuApplication.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 Request getJsonPostRequest(String url, JSONObject holder) {
        return new Request.Builder().url(url).addHeader("Content-type", "application/json")
                .post(RequestBody.create(MediaType.parse("application/json"), holder.toString())).build();
    }

    public static Request getTraktAuthenticationRequest(String url, String username, String password)
            throws JSONException {
        JSONObject holder = new JSONObject();
        holder.put("username", username);
        holder.put("password", password);

        return new Request.Builder().url(url).addHeader("Content-type", "application/json")
                .addHeader("trakt-api-key", "43efecdf3f16820856d75c4b4d40d1b1d6d9dd1485dd0edc933d7694ce427178")
                .addHeader("trakt-api-version", "2").addHeader("trakt-user-login", username)
                .post(RequestBody.create(MediaType.parse("application/json"), holder.toString())).build();
    }

    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 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) {
        return 25 * 1024 * 1024; // 25 MB
    }

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

    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 {
            return filepath.substring(0, filepath.lastIndexOf("/"));
        } 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(SCHEDULED_UPDATES_MOVIE,
                ScheduledUpdatesFragment.NOT_ENABLED) > ScheduledUpdatesFragment.AT_LAUNCH) {
            ScheduledUpdatesAlarmManager.cancelUpdate(ScheduledUpdatesAlarmManager.MOVIES, context);
            long duration = MizLib.HOUR;
            switch (settings.getInt(SCHEDULED_UPDATES_MOVIE, 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(SCHEDULED_UPDATES_TVSHOWS,
                ScheduledUpdatesFragment.NOT_ENABLED) > ScheduledUpdatesFragment.AT_LAUNCH) {
            ScheduledUpdatesAlarmManager.cancelUpdate(ScheduledUpdatesAlarmManager.SHOWS, context);
            long duration = MizLib.HOUR;
            switch (settings.getInt(SCHEDULED_UPDATES_TVSHOWS, 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 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 String decryptImdbId(String filename) {
        Pattern p = Pattern.compile("(tt\\d{7})");
        Matcher m = p.matcher(filename);
        if (m.find())
            return m.group(1);
        return null;
    }

    public static String decryptName(String input, String customTags) {
        if (TextUtils.isEmpty(input))
            return "";

        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 (Exception e) {
                }
            }

            if (!TextUtils.isEmpty(after))
                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 (!TextUtils.isEmpty(customTags)) {
            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
        output = output.trim().replaceAll("(?i)(?:s|season[ ._-]*)\\d{1,4}.*", ""); // Remove "season####" 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 (Exception 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 (Exception e) {
            }
        }

        return result;
    }

    /**
     * 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, SmbLogin 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(auth.getDomain(), auth.getUsername(), auth.getPassword(),
                        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 File getRandomBackdropFile(Context c) {
        ArrayList<File> files = new ArrayList<File>();

        File[] f = MizuuApplication.getMovieBackdropFolder(c).listFiles();
        if (f != null)
            Collections.addAll(files, f);

        f = MizuuApplication.getTvShowBackdropFolder(c).listFiles();
        if (f != null)
            Collections.addAll(files, f);

        if (files.size() > 0) {
            Random rndm = new Random();
            return files.get(rndm.nextInt(files.size()));
        }

        return new File("");
    }

    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 getPrettyRuntime(Context context, int minutes) {
        if (minutes == 0) {
            return context.getString(R.string.stringNA);
        }

        int hours = (minutes / 60);
        minutes = (minutes % 60);

        if (hours > 0) {
            if (minutes == 0) {
                return hours + " " + context.getResources().getQuantityString(R.plurals.hour, hours, hours);
            } else {
                return hours + " " + context.getResources().getQuantityString(R.plurals.hour_short, hours, hours)
                        + " " + minutes + " "
                        + context.getResources().getQuantityString(R.plurals.minute_short, minutes, minutes);
            }
        } else {
            return minutes + " " + context.getResources().getQuantityString(R.plurals.minute, minutes, minutes);
        }
    }

    public static String getPrettyDate(Context context, String date) {
        if (!TextUtils.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 MizLib
                        .toCapitalFirstChar(cal.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault())
                                + " " + cal.get(Calendar.YEAR));
            } catch (Exception e) { // Fall back if something goes wrong
                return date;
            }
        } else {
            return context.getString(R.string.stringNA);
        }
    }

    public static String getPrettyDatePrecise(Context context, String date) {
        if (!TextUtils.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.getRawDate() != null)
                    first = o1.getRawDate().replace("-", "");

                if (!TextUtils.isEmpty(first))
                    firstDate = Integer.valueOf(first);

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

                if (!TextUtils.isEmpty(second))
                    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(INCLUDE_ADULT_CONTENT, 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 boolean isNumber(String runtime) {
        return TextUtils.isDigitsOnly(runtime);
    }

    public static boolean isValidTmdbId(String id) {
        return !TextUtils.isEmpty(id) && !id.equals(DbAdapterMovies.UNIDENTIFIED_ID) && isNumber(id);
    }

    /**
     * Helper method to remove a ViewTreeObserver correctly, i.e.
     * avoiding the deprecated method on API level 16+.
     * @param vto
     * @param victim
     */
    @SuppressWarnings("deprecation")
    public static void removeViewTreeObserver(ViewTreeObserver vto, OnGlobalLayoutListener victim) {
        if (MizLib.hasJellyBean()) {
            vto.removeOnGlobalLayoutListener(victim);
        } else {
            vto.removeGlobalOnLayoutListener(victim);
        }
    }

    public static String getTmdbImageBaseUrl(Context context) {
        long time = PreferenceManager.getDefaultSharedPreferences(context).getLong(TMDB_BASE_URL_TIME, 0);
        long currentTime = System.currentTimeMillis();

        // We store the TMDb base URL for 24 hours
        if (((currentTime - time) < DAY
                && PreferenceManager.getDefaultSharedPreferences(context).contains(TMDB_BASE_URL))
                | Looper.getMainLooper().getThread() == Thread.currentThread()) {
            return PreferenceManager.getDefaultSharedPreferences(context).getString(TMDB_BASE_URL, "");
        }

        try {
            JSONObject configuration = getJSONObject(context,
                    "https://api.themoviedb.org/3/configuration?api_key=" + getTmdbApiKey(context));
            String baseUrl = configuration.getJSONObject("images").getString("secure_base_url");

            Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
            editor.putString(TMDB_BASE_URL, baseUrl);
            editor.putLong(TMDB_BASE_URL_TIME, System.currentTimeMillis());
            editor.commit();

            return baseUrl;
        } catch (JSONException e) {
            return null;
        }
    }

    public static void showSelectFileDialog(Context context, ArrayList<Filepath> paths,
            final Dialog.OnClickListener listener) {
        String[] items = new String[paths.size()];
        for (int i = 0; i < paths.size(); i++)
            items[i] = paths.get(i).getFilepath();

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle(context.getString(R.string.selectFile));
        builder.setItems(items, listener);
        builder.show();
    }

    public static String getFilenameWithoutExtension(String filename) {
        try {
            return filename.substring(0, filename.lastIndexOf("."));
        } catch (IndexOutOfBoundsException e) {
            return filename;
        }
    }

    public static void copyDatabase(Context context) {
        try {
            FileUtils.copyFile(context.getDatabasePath("mizuu_data"),
                    new File(Environment.getExternalStorageDirectory(), "mizuu_data.db"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}