com.pdftron.pdf.utils.Utils.java Source code

Java tutorial

Introduction

Here is the source code for com.pdftron.pdf.utils.Utils.java

Source

//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2016 by PDFTron Systems Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

package com.pdftron.pdf.utils;

import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.RectF;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.StatFs;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.util.Base64;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import com.pdftron.common.PDFNetException;
import com.pdftron.pdf.tools.BuildConfig;
import com.pdftron.pdf.tools.R;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.NumberFormat;
import java.util.Locale;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

public class Utils {

    /**
     * Checks if this device is running Honeycomb or higher.
     *
     * @return true if Honeycomb or higher, false otherwise.
     */
    public static boolean isHoneycomb() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
    }

    /**
     * Checks if this device is running any Honeycomb version (APIs 11, 12 or 13).
     *
     * @return true if any of the Honeycomb APIs, false otherwise.
     */
    public static boolean isHoneycombOnly() {
        return Build.VERSION.SDK_INT == Build.VERSION_CODES.HONEYCOMB
                || Build.VERSION.SDK_INT == Build.VERSION_CODES.HONEYCOMB_MR1
                || Build.VERSION.SDK_INT == Build.VERSION_CODES.HONEYCOMB_MR2;
    }

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

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

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

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

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

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

    /**
     * Checks if this device is running KitKat or higher.
     *
     * @return true if KitKat or higher, false otherwise.
     */
    public static boolean isKitKat() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    }

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

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

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

    public static String copyResourceToTempFolder(Context context, int resId, boolean force, String resourceName)
            throws PDFNetException {
        if (context == null) {
            throw new PDFNetException("", 0L, "com.pdftron.pdf.PDFNet", "copyResourceToTempFolder()",
                    "Context cannot be null to initialize resource file.");
        } else {
            File resFile = new File(context.getFilesDir() + File.separator + "resourceName");
            if (!resFile.exists() || force) {
                File filesDir = context.getFilesDir();
                StatFs stat = new StatFs(filesDir.getPath());
                long size = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
                if (size < 2903023L) {
                    throw new PDFNetException("", 0L, "com.pdftron.pdf.PDFNet", "copyResourceToTempFolder()",
                            "Not enough space available to copy resources file.");
                }

                Resources rs = context.getResources();

                try {
                    InputStream e = rs.openRawResource(resId);
                    FileOutputStream fos = context.openFileOutput(resourceName, 0);
                    byte[] buffer = new byte[1024];

                    int read;
                    while ((read = e.read(buffer)) != -1) {
                        fos.write(buffer, 0, read);
                    }

                    e.close();
                    fos.flush();
                    fos.close();

                } catch (Resources.NotFoundException var13) {
                    throw new PDFNetException("", 0L, "com.pdftron.pdf.PDFNet", "initializeResource()",
                            "Resource file ID does not exist.");
                } catch (FileNotFoundException var14) {
                    throw new PDFNetException("", 0L, "com.pdftron.pdf.PDFNet", "initializeResource()",
                            "Resource file not found.");
                } catch (IOException var15) {
                    throw new PDFNetException("", 0L, "com.pdftron.pdf.PDFNet", "initializeResource()",
                            "Error writing resource file to internal storage.");
                } catch (Exception var16) {
                    throw new PDFNetException("", 0L, "com.pdftron.pdf.PDFNet", "initializeResource()",
                            "Unknown error.");
                }
            }

            return context.getFilesDir().getAbsolutePath();
        }
    }

    public static boolean isTablet(Context context) {
        boolean result = false;
        try {
            result = context.getResources().getBoolean(R.bool.isTablet);
        } catch (Exception e) {
            // Do nothing
        }
        return result;
    }

    // PDFViewCtrlTools utility methods
    public static com.pdftron.pdf.Rect convertFromPageRectToScreenRect(com.pdftron.pdf.PDFViewCtrl pdfViewCtrl,
            com.pdftron.pdf.Rect rect, int pageNum) throws PDFNetException {
        rect.normalize();

        double x1 = rect.getX1();
        double y1 = rect.getY1();
        double x2 = rect.getX2();
        double y2 = rect.getY2();

        double[] pts1 = pdfViewCtrl.convPagePtToScreenPt(x1, y1, pageNum);
        double[] pts2 = pdfViewCtrl.convPagePtToScreenPt(x2, y2, pageNum);

        com.pdftron.pdf.Rect retRect = new com.pdftron.pdf.Rect(pts1[0], pts1[1], pts2[0], pts2[1]);
        retRect.normalize();

        return retRect;
    }

    public static com.pdftron.pdf.Rect convertFromPageRectToScrollViewerRect(
            com.pdftron.pdf.PDFViewCtrl pdfViewCtrl, com.pdftron.pdf.Rect rect, int pageNum)
            throws PDFNetException {
        rect.normalize();

        double x1 = rect.getX1();
        double y1 = rect.getY1();
        double x2 = rect.getX2();
        double y2 = rect.getY2();

        double[] pts1 = pdfViewCtrl.convPagePtToHorizonalScrollingPt(x1, y1, pageNum);
        double[] pts2 = pdfViewCtrl.convPagePtToHorizonalScrollingPt(x2, y2, pageNum);

        com.pdftron.pdf.Rect retRect = new com.pdftron.pdf.Rect(pts1[0], pts1[1], pts2[0], pts2[1]);

        return retRect;
    }

    public static void snapPointToRect(PointF point, RectF rect) {
        if (rect != null) {
            if (point.x < rect.left) {
                point.x = rect.left;
            } else if (point.x > rect.right) {
                point.x = rect.right;
            }
            if (point.y < rect.top) {
                point.y = rect.top;
            } else if (point.y > rect.bottom) {
                point.y = rect.bottom;
            }
        }
    }

    // check if the system language is Persian/Arabic
    public static boolean isSystemLanguagePerAra() {
        if (!isJellyBeanMR1()) {
            return false;
        }
        final int directionality = Character.getDirectionality(Locale.getDefault().getDisplayName().charAt(0));
        if (directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT
                || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) {
            return true;
        }
        return false;
    }

    // check if layout direction is right-to-left
    public static boolean isRtlLayout(Context context) {
        if (isJellyBeanMR1()) {
            Configuration config = context.getResources().getConfiguration();
            if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
                return true;
            }
        }
        return false;
    }

    public static boolean isRightToLeftString(String str) {
        // decision is made based on the first character belonging to RTL/LTR unicode values
        // as defined in the Core (UnicodeUtils)
        if (str == null || str.length() == 0) {
            return false;
        }

        int sz = str.length();
        for (int i = 0; i < sz; i++) {
            char u = str.charAt(i);
            if ((0x0590 <= u && u <= 0x05FF) || (0x0600 <= u && u <= 0x06FF) || (0x0750 <= u && u <= 0x077F)
                    || (0xFB50 <= u && u <= 0xFDFF) || (0xFE70 <= u && u <= 0xFEFF)) {
                return true;
            }
            if ((0x0041 <= u && u <= 0x005A) || (0x0061 <= u && u <= 0x007A) || (0xFB00 <= u && u <= 0xFB06)) {
                return false;
            }
        }
        return false;
    }

    public static boolean hasRightToLeftChar(String str) {
        if (str == null || str.length() == 0) {
            return false;
        }

        int sz = str.length();
        for (int i = 0; i < sz; i++) {
            char u = str.charAt(i);
            if ((0x0590 <= u && u <= 0x05FF) || (0x0600 <= u && u <= 0x06FF) || (0x0750 <= u && u <= 0x077F)
                    || (0xFB50 <= u && u <= 0xFDFF) || (0xFE70 <= u && u <= 0xFEFF)) {
                return true;
            }
        }
        return false;
    }

    // get digits based on the Locale
    public static String getLocaleDigits(String input) {
        String output = "";
        for (int i = 0; i < input.length(); i++) {
            char ch = input.charAt(i);
            if (ch >= '0' && ch <= '9') {
                output += String.format("%d", (int) ch - (int) '0');
            } else {
                output += ch;
            }
        }
        return output;
    }

    /**
     * @param string the string to be checked against
     * @return true if the string is null or empty, false otherwise
     */
    public static boolean isNullOrEmpty(String string) {
        return string == null || string.trim().isEmpty();
    }

    public static String getUriColumnInfo(Context context, Uri contentUri, String column) {
        Cursor cursor = null;
        try {
            String[] proj = { column };
            cursor = context.getContentResolver().query(contentUri, proj, // Which columns to return
                    null, // WHERE clause; which rows to return (all rows)
                    null, // WHERE clause selection arguments (none)
                    null); // Order-by clause (ascending by name)
            if (null != cursor) {
                cursor.moveToFirst();
                int column_index = cursor.getColumnIndexOrThrow(proj[0]);
                return cursor.getString(column_index);
            }
            return "";
        } catch (Exception e) {
            AnalyticsHandlerAdapter.getInstance().sendException(e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return "";
    }

    public static String getRealPathFromImageURI(Context context, Uri contentUri) {
        if (null == context || null == contentUri) {
            return "";
        }
        Cursor cursor = null;
        try {
            // can post image
            String[] proj = { MediaStore.Images.Media.DATA };
            cursor = context.getContentResolver().query(contentUri, proj, // Which columns to return
                    null, // WHERE clause; which rows to return (all rows)
                    null, // WHERE clause selection arguments (none)
                    null); // Order-by clause (ascending by name)
            if (null != cursor) {
                cursor.moveToFirst();
                int column_index = cursor.getColumnIndexOrThrow(proj[0]);
                return cursor.getString(column_index);
            }
            return "";
        } catch (Exception e) {
            AnalyticsHandlerAdapter.getInstance().sendException(e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return "";
    }

    public static Bitmap getBitmapFromImageUri(Context context, Uri imageUri, String backupFilepath) {
        if (null == context || null == imageUri) {
            return null;
        }
        try {
            Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), imageUri);
            if (bitmap == null) {
                // Add image to the output file
                if (!isNullOrEmpty(backupFilepath)) {
                    FileInputStream fileInputStream = new FileInputStream(backupFilepath);
                    bitmap = BitmapFactory.decodeStream(fileInputStream);
                }
            }
            return bitmap;
        } catch (Exception e) {
            AnalyticsHandlerAdapter.getInstance().sendException(e);
        }
        return null;
    }

    public static String getUriDisplayName(Context context, Uri contentUri) {
        String displayName = null;
        String[] projection = { OpenableColumns.DISPLAY_NAME };
        Cursor cursor = null;

        if (contentUri.getScheme().equalsIgnoreCase("file")) {
            return contentUri.getLastPathSegment();
        }
        try {
            cursor = context.getContentResolver().query(contentUri, projection, null, null, null);
            if (cursor != null && cursor.moveToFirst()) {
                int nameIndex = cursor.getColumnIndexOrThrow(projection[0]);
                if (nameIndex >= 0) {
                    displayName = cursor.getString(nameIndex);
                }
            }
        } catch (Exception e) {
            displayName = null;
        }
        if (cursor != null) {
            cursor.close();
        }
        return displayName;
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    public static void showToast(Context context, String string) {
        if (context != null) {
            Toast toast = Toast.makeText(context, string, Toast.LENGTH_LONG);
            toast.setGravity(Gravity.CENTER, 0, 0);
            if (Utils.isRtlLayout(context)) {
                toast.getView().setTextDirection(View.TEXT_DIRECTION_RTL);
            }
            toast.show();
            return;
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    public static void showToast(Context context, int stringId) {
        if (context != null) {
            Toast toast = Toast.makeText(context, stringId, Toast.LENGTH_LONG);
            toast.setGravity(Gravity.CENTER, 0, 0);
            if (Utils.isRtlLayout(context)) {
                toast.getView().setTextDirection(View.TEXT_DIRECTION_RTL);
            }
            toast.show();
        }
    }

    public static void showToast(Context context, String string, int duration) {
        if (context != null) {
            Toast toast = Toast.makeText(context, string, duration);
            if (Utils.isRtlLayout(context)) {
                toast.getView().setTextDirection(View.TEXT_DIRECTION_RTL);
            }
            toast.show();
        }
    }

    public static void showToast(Context context, int stringId, int duration) {
        if (context != null) {
            Toast toast = Toast.makeText(context, stringId, duration);
            if (Utils.isRtlLayout(context)) {
                toast.getView().setTextDirection(View.TEXT_DIRECTION_RTL);
            }
            toast.show();
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    public static Toast showToast(Context context, Toast toast, String text, int duration) {
        if (toast != null) {
            toast.cancel();
        }
        toast = Toast.makeText(context, text, duration);
        if (Utils.isRtlLayout(context)) {
            toast.getView().setTextDirection(View.TEXT_DIRECTION_RTL);
        }
        toast.show();
        return toast;
    }

    public static Toast showToast(Context context, Toast toast, int stringId, int duration) {
        if (toast != null) {
            toast.cancel();
        }
        toast = Toast.makeText(context, stringId, duration);
        if (Utils.isRtlLayout(context)) {
            toast.getView().setTextDirection(View.TEXT_DIRECTION_RTL);
        }
        toast.show();
        return toast;
    }

    /**
     * Gets the size of the display, in pixels.
     *
     * @param context the Context
     * @param outSize A Point object to receive the size information
     */
    public static void getDisplaySize(Context context, Point outSize) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        if (outSize == null) {
            outSize = new Point();
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
            display.getSize(outSize);
        } else {
            outSize.set(display.getWidth(), display.getHeight());
        }
    }

    public static boolean isOfficeDocument(String filePath) {
        String ext = FilenameUtils.getExtension(filePath);
        if (ext == null)
            return false;
        String[] fileTypes = Constants.FILE_NAME_EXTENSIONS_DOC;
        if (filePath != null) {
            for (String type : fileTypes) {
                if (ext.equalsIgnoreCase(type)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static boolean isImageFile(String filePath) {
        String ext = FilenameUtils.getExtension(filePath);
        if (ext == null)
            return false;
        String[] fileTypes = Constants.FILE_NAME_EXTENSIONS_IMAGE;
        if (filePath != null) {
            for (String type : fileTypes) {
                if (ext.equalsIgnoreCase(type)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static boolean isNotPDF(String filePath) {
        if (isOfficeDocument(filePath) || isImageFile(filePath))
            return true;

        return false;
    }

    public static String decryptIt(Context context, String value) {
        String cryptoPass = context.getString(context.getApplicationInfo().labelRes);
        try {
            DESKeySpec keySpec = new DESKeySpec(cryptoPass.getBytes("UTF8"));
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey key = keyFactory.generateSecret(keySpec);

            byte[] encrypedPwdBytes = Base64.decode(value, Base64.DEFAULT);
            // cipher is not thread safe
            Cipher cipher = Cipher.getInstance("DES");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decrypedValueBytes = (cipher.doFinal(encrypedPwdBytes));

            String decrypedValue = new String(decrypedValueBytes);
            Log.d("MiscUtils", "Decrypted: " + value + " -> " + decrypedValue);
            return decrypedValue;

        } catch (Exception e) {
            Log.e(e.getClass().getName(), e.getMessage());
        }
        return value;
    }

    public static String encryptIt(Context context, String value) {
        String cryptoPass = context.getString(context.getApplicationInfo().labelRes);
        try {
            DESKeySpec keySpec = new DESKeySpec(cryptoPass.getBytes("UTF8"));
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey key = keyFactory.generateSecret(keySpec);

            byte[] clearText = value.getBytes("UTF8");
            // Cipher is not thread safe
            Cipher cipher = Cipher.getInstance("DES");
            cipher.init(Cipher.ENCRYPT_MODE, key);

            String encrypedValue = Base64.encodeToString(cipher.doFinal(clearText), Base64.DEFAULT);
            Log.d("MiscUtils", "Encrypted: " + value + " -> " + encrypedValue);
            return encrypedValue;

        } catch (Exception e) {
            Log.d(e.getClass().getName(), e.getMessage());
        }
        return value;
    }

    public static AlertDialog.Builder getAlertDialogNoTitleBuilder(Context context, int messageId) {
        return getAlertDialogBuilder(context, context.getResources().getString(messageId), "");
    }

    public static AlertDialog.Builder getAlertDialogBuilder(Context context, int messageId, int titleId) {
        return getAlertDialogBuilder(context, context.getResources().getString(messageId),
                context.getResources().getString(titleId));
    }

    public static AlertDialog.Builder getAlertDialogBuilder(Context context, String message, String title) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);

        builder.setMessage(message).setCancelable(true);

        if (!isNullOrEmpty(title)) {
            builder.setTitle(title);
        }
        return builder;
    }

    static Toast sToast = null;
    static String sToastStr = null;

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    public static void showCommonToast(@NonNull Context context, String str, int duration) {
        if (sToast == null) {
            sToast = Toast.makeText(context, str, duration);
        } else if (str != sToastStr) {
            sToast.cancel();
            sToast = Toast.makeText(context, str, duration);
        }

        sToastStr = str;
        if (Utils.isRtlLayout(context)) {
            sToast.getView().setTextDirection(View.TEXT_DIRECTION_RTL);
        } else {
            sToast.getView().setTextDirection(View.TEXT_DIRECTION_LTR);
        }
        sToast.show();
    }

    public static void showCommonToast(@NonNull Context context, int stringId) {
        String str = context.getResources().getString(stringId);
        showCommonToast(context, str, Toast.LENGTH_LONG);
    }

    public static void showCommonToast(@NonNull Context context, int stringId, int duration) {
        String str = context.getResources().getString(stringId);
        showCommonToast(context, str, duration);
    }

    public static void showCommonToast(@NonNull Context context, String str) {
        showCommonToast(context, str, Toast.LENGTH_LONG);
    }

    public static void debugLogD(String tag, String msg) {
        if (BuildConfig.DEBUG) {
            Log.d(tag, msg);
        }
    }

    public static void debugLogV(String tag, String msg) {
        if (BuildConfig.DEBUG) {
            Log.v(tag, msg);
        }
    }

    /**
     * Get a human readable format of a byte size.
     * <p/>
     * See http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
     *
     * @param bytes the number of bytes
     * @param si    true for SI units or false for binary units
     * @return the human readable format of the byte size.
     */
    public static String humanReadableByteCount(long bytes, boolean si) {
        int unit = si ? 1000 : 1024;
        if (bytes < unit) {
            if (Utils.isSystemLanguagePerAra()) {
                return String.format("%dB", bytes); // force put B in front of the number
            }
            return bytes + " B";
        }
        int exp = (int) (Math.log(bytes) / Math.log(unit));
        //String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");
        String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + "";
        if (Utils.isSystemLanguagePerAra()) {
            return String.format("%.1f%sB", bytes / Math.pow(unit, exp), pre); // force put B in front of the number
        }
        return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
    }

    public static Long getReadableByteValue(long bytes, boolean si) {
        int unit = si ? 1000 : 1024;
        if (bytes < unit)
            return bytes;
        int exp = (int) (Math.log(bytes) / Math.log(unit));
        return (long) (bytes / Math.pow(unit, exp));
    }

    public static String getReadableByteUnit(long bytes, boolean si) {
        int unit = si ? 1000 : 1024;
        if (bytes < unit)
            return "B";
        int exp = (int) (Math.log(bytes) / Math.log(unit));
        //String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");
        String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + "";
        return pre;
    }

    public static String getByteCount(long bytes) {
        return NumberFormat.getNumberInstance().format(bytes);
    }

    public static String getDisplayNameFromImageUri(Context context, Uri imageUri, String backupFilepath) {
        if (null == context || null == imageUri) {
            return null;
        }
        try {
            String filename = Utils.getUriColumnInfo(context, imageUri, MediaStore.Images.Media.DISPLAY_NAME);
            if (Utils.isNullOrEmpty(filename)) {
                if (!Utils.isNullOrEmpty(backupFilepath)) {
                    filename = FilenameUtils.getBaseName(backupFilepath);
                }
            }
            return FilenameUtils.removeExtension(filename);
        } catch (Exception e) {
            AnalyticsHandlerAdapter.getInstance().sendException(e);
        }
        return null;
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public static boolean isSdCardFile(Context context, File file) {
        if (!Utils.isKitKat()) {
            // not applicable for below kitkat
            return false;
        }
        if (file != null) {
            if (file.getParentFile() == null || file.getAbsolutePath().equals("/storage")) {
                // File cannot be on a SD-card
                return false;
            }
            final File storageDirectory = Environment.getExternalStorageDirectory();
            final File[] rootDirs = context.getExternalFilesDirs(null);
            if (rootDirs != null && rootDirs.length > 0) {
                for (File dir : rootDirs) {
                    if (dir != null) {
                        try {
                            if (!FilenameUtils.equals(storageDirectory.getAbsolutePath(), dir.getAbsolutePath())
                                    && !FileUtils.directoryContains(storageDirectory, dir)) {
                                while (dir.getParentFile() != null
                                        && !dir.getAbsolutePath().equalsIgnoreCase("/storage")) {
                                    if (FilenameUtils.equals(file.getAbsolutePath(), dir.getAbsolutePath())
                                            || FileUtils.directoryContains(dir, file)) {
                                        // The current folder is on the SD-card
                                        return true;
                                    }
                                    dir = dir.getParentFile();
                                }
                            }
                        } catch (IOException ignored) {
                        }
                    }
                }
            }
        }
        return false;
    }

    public static void showAlertDialogWithLink(Context context, String message, String title) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);

        builder.setMessage(Html.fromHtml(message)).setCancelable(true).setPositiveButton(R.string.ok,
                new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
        if (title.length() > 0) {
            builder.setTitle(title);
        }
        final AlertDialog d = builder.create();
        d.show();

        // Make the textview clickable. Must be called after show()
        ((TextView) d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
    }
}