/FileExtraction/Java_unseen/PowerExplorer_PowerFileExplorer/PowerFileExplorer/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java
package com.amaze.filemanager.filesystem;

import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.provider.DocumentFile;
import android.util.Log;

import net.gnu.explorer.R;
import com.amaze.filemanager.database.CloudHandler;
import com.amaze.filemanager.exceptions.RootNotPermittedException;
import com.amaze.filemanager.ui.icons.MimeTypes;
import com.amaze.filemanager.utils.AppConfig;
import com.amaze.filemanager.utils.DataUtils;
import com.amaze.filemanager.utils.OTGUtil;
import com.amaze.filemanager.utils.OpenMode;
import com.amaze.filemanager.utils.RootUtils;
import com.amaze.filemanager.utils.cloud.CloudUtil;
import com.amaze.filemanager.utils.files.GenericCopyUtil;
import com.cloudrail.si.interfaces.CloudStorage;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
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.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;
import android.os.AsyncTask;
import net.gnu.explorer.ExplorerActivity;
import java.util.*;

/**
 * Utility class for helping parsing file systems.
 * <p>
 * Created by Arpit on 04-06-2015.
 */
public abstract class FileUtil {
    /**
     * Determine the camera folder. There seems to be no Android API to work for real devices, so this is a best guess.
     *
     * @return the default camera folder.
     **/
    //TODO the function?

    /**
     * Copy a file. The target file may even be on external SD card for Kitkat.
     *
     * @param source The source file
     * @param target The target file
     * @return true if the copying was successful.
     */
    @SuppressWarnings("null")
    private static boolean copyFile(final File source, final File target, Context context) {
        FileInputStream inStream = null;
        OutputStream outStream = null;
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            inStream = new FileInputStream(source);

            // First try the normal way
            if (isWritable(target)) {
                // standard way
                outStream = new FileOutputStream(target);
                inChannel = inStream.getChannel();
                outChannel = ((FileOutputStream) outStream).getChannel();
                inChannel.transferTo(0, inChannel.size(), outChannel);
            } else {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    // Storage Access Framework
                    DocumentFile targetDocument = getDocumentFile(target, false, context);
                    outStream =
                            context.getContentResolver().openOutputStream(targetDocument.getUri());
                } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
                    // Workaround for Kitkat ext SD card
                    Uri uri = MediaStoreHack.getUriFromFile(target.getAbsolutePath(), context);
                    outStream = context.getContentResolver().openOutputStream(uri);
                } else {
                    return false;
                }

                if (outStream != null) {
                    // Both for SAF and for Kitkat, write to output stream.
                    byte[] buffer = new byte[16384]; // MAGIC_NUMBER
                    int bytesRead;
                    while ((bytesRead = inStream.read(buffer)) != -1) {
                        outStream.write(buffer, 0, bytesRead);
                    }
                }

            }
        } catch (Exception e) {
            Log.e("AmazeFileUtils",
                    "Error when copying file from " + source.getAbsolutePath() + " to " + target.getAbsolutePath(), e);
            return false;
        } finally {
            try {
                inStream.close();
            } catch (Exception e) {
                // ignore exception
            }

            try {
                outStream.close();
            } catch (Exception e) {
                // ignore exception
            }

            try {
                inChannel.close();
            } catch (Exception e) {
                // ignore exception
            }

            try {
                outChannel.close();
            } catch (Exception e) {
                // ignore exception
            }
        }
        return true;
    }

    public static OutputStream getOutputStream(final File target, Context context) throws Exception {
        return getOutputStream(target, context, 0);
    }

    public static OutputStream getOutputStream(final File target, Context context, long s) throws Exception {
        OutputStream outStream = null;
        try {
            // First try the normal way
            if (isWritable(target)) {
                // standard way
                outStream = new FileOutputStream(target);
            } else {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    // Storage Access Framework
                    DocumentFile targetDocument = getDocumentFile(target, false, context);
                    outStream = context.getContentResolver().openOutputStream(targetDocument.getUri());
                } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
                    // Workaround for Kitkat ext SD card
                    return MediaStoreHack.getOutputStream(context, target.getPath());
                }
            }
        } catch (Exception e) {
            Log.e("AmazeFileUtils",
                    "Error when copying file from " + target.getAbsolutePath(), e);
            throw new Exception();
        }
        return outStream;
    }

    /**
     * Writes uri stream from external application to the specified path
     * @param uris
     * @param contentResolver
     * @param currentPath
     */
    public static final void writeUriToStorage(final ExplorerActivity mainActivity, final ArrayList<Uri> uris,
                                               final ContentResolver contentResolver, final String currentPath) {

        //AppConfig.runInBackground(new AppConfig.CustomAsyncCallbacks() {
		new AsyncTask<Void, Void, Void>() {

            @Override
            public Void doInBackground(Void... o) {

                for (Uri uri : uris) {

                    BufferedInputStream bufferedInputStream = null;
                    try {
                        bufferedInputStream = new BufferedInputStream(contentResolver.openInputStream(uri));
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }

                    BufferedOutputStream bufferedOutputStream = null;

                    try {

                        DocumentFile documentFile = DocumentFile.fromSingleUri(mainActivity, uri);
                        String finalFilePath = currentPath + "/" + documentFile.getName();
                        DataUtils dataUtils = DataUtils.getInstance();

                        HFile hFile = new HFile(OpenMode.UNKNOWN, currentPath);
                        hFile.generateMode(mainActivity);

                        switch (hFile.getMode()) {
                            case FILE:
                            case ROOT:
                                bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(finalFilePath));
                                break;
                            case SMB:
                                OutputStream outputStream = new SmbFile(finalFilePath).getOutputStream();
                                bufferedOutputStream = new BufferedOutputStream(outputStream);
                                break;
                            case DROPBOX:
                                CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX);
                                cloudStorageDropbox.upload(CloudUtil.stripPath(OpenMode.DROPBOX, finalFilePath),
                                        bufferedInputStream, documentFile.length(), true);
                                break;
                            case BOX:
                                CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX);
                                cloudStorageBox.upload(CloudUtil.stripPath(OpenMode.BOX, finalFilePath),
                                        bufferedInputStream, documentFile.length(), true);
                                break;
                            case ONEDRIVE:
                                CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE);
                                cloudStorageOneDrive.upload(CloudUtil.stripPath(OpenMode.ONEDRIVE, finalFilePath),
                                        bufferedInputStream, documentFile.length(), true);
                                break;
                            case GDRIVE:
                                CloudStorage cloudStorageGDrive = dataUtils.getAccount(OpenMode.GDRIVE);
                                cloudStorageGDrive.upload(CloudUtil.stripPath(OpenMode.GDRIVE, finalFilePath),
                                        bufferedInputStream, documentFile.length(), true);
                                break;
                            case OTG:
                                DocumentFile documentTargetFile = OTGUtil.getDocumentFile(finalFilePath,
                                        mainActivity, true);

                                bufferedOutputStream = new BufferedOutputStream(contentResolver
                                        .openOutputStream(documentTargetFile.getUri()),
                                        GenericCopyUtil.DEFAULT_BUFFER_SIZE);
                                break;
                            default:
                                return null;
                        }

                        int count = 0;
                        byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE];

                        while (count != -1) {

                            count = bufferedInputStream.read(buffer);
                            if (count != -1) {

                                bufferedOutputStream.write(buffer, 0, count);
                            }
                        }
                        bufferedOutputStream.flush();

                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {

                        try {

                            if (bufferedInputStream != null) {
                                bufferedInputStream.close();
                            }
                            if (bufferedOutputStream != null) {
                                bufferedOutputStream.close();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                return null;
            }

            @Override
            public void onPostExecute(Void result) {
                //return null;
            }

            @Override
            public void onPreExecute() {
                //return null;
            }

//            @Override
//            public Void publishResult(Object... result) {
//                return null;
//            }
//
//            @Override
//            public <T> T[] params() {
//                return null;
//            }
        }.execute();
    }

    /**
     * Delete a file. May be even on external SD card.
     *
     * @param file the file to be deleted.
     * @return True if successfully deleted.
     */
    static boolean deleteFile(@NonNull final File file, Context context) {
        // First try the normal deletion.
        if (file == null || !file.exists()) 
			return true;
        if (file.delete() || deleteFilesInFolder(file, context))
            return true;

        // Try with Storage Access Framework.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && FileUtil.isOnExtSdCard(file, context)) {
            final DocumentFile document = getDocumentFile(file, false, context);
			if (document != null) {
				return document.delete();
			}
        }

        // Try the Kitkat workaround.
        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
            final ContentResolver resolver = context.getContentResolver();

            try {
                final Uri uri = MediaStoreHack.getUriFromFile(file.getAbsolutePath(), context);
                resolver.delete(uri, null, null);
                return !file.exists();
            } catch (Exception e) {
                Log.e("AmazeFileUtils", "Error when deleting file " + file.getAbsolutePath(), e);
                return false;
            }
        }

        return !file.exists();
    }

    private static boolean rename(File f, String name, boolean root) throws RootNotPermittedException {
        String newPath = f.getParent() + "/" + name;
        if (f.getParentFile().canWrite()) {
            return f.renameTo(new File(newPath));
        } else if (root) {
            RootUtils.rename(f.getPath(), newPath);
            return true;
        }
        return false;
    }

    /**
     * Rename a folder. In case of extSdCard in Kitkat, the old folder stays in place, but files are moved.
     *
     * @param source The source folder.
     * @param target The target folder.
     * @return true if the renaming was successful.
     */
    static boolean renameFolder(@NonNull final File source, @NonNull final File target,
                                Context context) throws RootNotPermittedException {
        // First try the normal rename.
        if (rename(source, target.getName(), false)) {
            return true;
        }
        if (target.exists()) {
            return false;
        }

        // Try the Storage Access Framework if it is just a rename within the same parent folder.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
                && source.getParent().equals(target.getParent()) && FileUtil.isOnExtSdCard(source, context)) {
            DocumentFile document = getDocumentFile(source, true, context);
            if (document.renameTo(target.getName())) {
                return true;
            }
        }

        // Try the manual way, moving files individually.
        if (!mkdir(target, context)) {
            return false;
        }

        File[] sourceFiles = source.listFiles();

        if (sourceFiles == null) {
            return true;
        }

        for (File sourceFile : sourceFiles) {
            String fileName = sourceFile.getName();
            File targetFile = new File(target, fileName);
            if (!copyFile(sourceFile, targetFile, context)) {
                // stop on first error
                return false;
            }
        }
        // Only after successfully copying all files, delete files on source folder.
        for (File sourceFile : sourceFiles) {
            if (!deleteFile(sourceFile, context)) {
                // stop on first error
                return false;
            }
        }
        return true;
    }

    /**
     * Get a temp file.
     *
     * @param file The base file for which to create a temp file.
     * @return The temp file.
     */
    public static File getTempFile(@NonNull final File file, Context context) {
        File extDir = context.getExternalFilesDir(null);
        return new File(extDir, file.getName());
    }

    /**
     * Create a folder. The folder may even be on external SD card for Kitkat.
     *
     * @deprecated use {@link #mkdirs(Context, HFile)}
     * @param file  The folder to be created.
     * @return True if creation was successful.
     */
    public static boolean mkdir(final File file, Context context) {
        if(file==null)
            return false;
        if (file.exists()) {
            // nothing to create.
            return file.isDirectory();
        }

        // Try the normal way
        if (file.mkdirs()) {
            return true;
        }

        // Try with Storage Access Framework.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && FileUtil.isOnExtSdCard(file, context)) {
            DocumentFile document = getDocumentFile(file, true, context);
            // getDocumentFile implicitly creates the directory.
            return document.exists();
        }

        // Try the Kitkat workaround.
        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
            try {
                return MediaStoreHack.mkdir(context, file);
            } catch (IOException e) {
                return false;
            }
        }

        return false;
    }

    public static boolean mkdirs(Context context, HFile file) {
        boolean isSuccessful = true;
        switch (file.mode) {
            case SMB:
                try {
                    SmbFile smbFile = new SmbFile(file.getPath());
                    smbFile.mkdirs();
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                    isSuccessful =  false;
                } catch (SmbException e) {
                    e.printStackTrace();
                    isSuccessful = false;
                }
                break;
            case OTG:
                DocumentFile documentFile = OTGUtil.getDocumentFile(file.getPath(), context, true);
                isSuccessful = documentFile != null;
                break;
            case FILE:
                isSuccessful = mkdir(new File(file.getPath()), context);
                break;
            default:
                isSuccessful = true;
                break;
        }

        return isSuccessful;
    }

    public static boolean mkfile(final File file,Context context) throws IOException {
        if(file==null)
            return false;
        if (file.exists()) {
            // nothing to create.
            return !file.isDirectory();
        }

        // Try the normal way
        try {
            if (file.createNewFile()) {
                return true;
            }
        } catch (IOException e) {
            //e.printStackTrace();
        }

        // Try with Storage Access Framework.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && FileUtil.isOnExtSdCard(file, context)) {
            DocumentFile document = getDocumentFile(file.getParentFile(), true, context);
            // getDocumentFile implicitly creates the directory.
            try {
                return document.createFile(MimeTypes.getMimeType(file), file.getName()) != null;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
            try {
                return MediaStoreHack.mkfile(context, file);
            } catch (Exception e) {
                return false;
            }
        }
        return false;
    }

    /**
     * Delete a folder.
     *
     * @param file The folder name.
     * @return true if successful.
     */
    private static boolean rmdir1(final File file, Context context) {
        if (file == null)
            return false;
        boolean b = true;
        for (File file1 : file.listFiles()) {
            if (file1.isDirectory()) {
                if (!rmdir1(file1, context)) b = false;
            } else {
                if (!deleteFile(file1, context)) b = false;
            }
        }
        return b;
    }

    private static boolean rmdir(final File file, Context context) {
        if (file == null)
            return false;
        if (!file.exists()) {
            return true;
        }
        if (!file.isDirectory()) {
            return false;
        }
        String[] fileList = file.list();
        if (fileList != null && fileList.length > 0) {
            //  empty the folder.
            rmdir1(file, context);
        }
        String[] fileList1 = file.list();
        if (fileList1 != null && fileList1.length > 0) {
            // Delete only empty folder.
            return false;
        }
        // Try the normal way
        if (file.delete()) {
            return true;
        }

        // Try with Storage Access Framework.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            DocumentFile document = getDocumentFile(file, true, context);
            return document.delete();
        }

        // Try the Kitkat workaround.
        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
            ContentResolver resolver = context.getContentResolver();
            ContentValues values = new ContentValues();
            values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
            resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

            // Delete the created entry, such that content provider will delete the file.
            resolver.delete(MediaStore.Files.getContentUri("external"), MediaStore.MediaColumns.DATA + "=?",
                    new String[]{file.getAbsolutePath()});
        }

        return !file.exists();
    }

    /**
     * Delete all files in a folder.
     *
     * @param folder the folder
     * @return true if successful.
     */
    private static final boolean deleteFilesInFolder(final File folder, Context context) {
        if (folder == null)
            return false;
        boolean totalSuccess = folder.delete();
        if (!totalSuccess && folder.isDirectory()) {//TODO remove recursive
            final LinkedList<File> folderQueue = new LinkedList<File>();
			folderQueue.push(folder);
			final LinkedList<File> allFolders = new LinkedList<File>();
			File fi = null;
			File[] fs;
			int size = 1;
			while (size > 0) {
				fi = folderQueue.pop();
				size--;
				allFolders.push(fi);
				fs = fi.listFiles();
				if (fs != null)
					for (File f2 : fs) {
						if (!f2.delete()) {
							folderQueue.push(f2);
							size++;
						} 
					}
			}
			size = allFolders.size() - 1;
			while (size-- > 0) {
				allFolders.pop().delete();
			}
			totalSuccess = allFolders.pop().delete();
//			for (File child : listFiles) {
//                deleteFilesInFolder(child, context);
//            }
//            if (!folder.delete())
//                totalSuccess = false;
        } 
        return totalSuccess;
    }

    /**
     * Check if a file is readable.
     *
     * @param file The file
     * @return true if the file is reabable.
     */
    public static boolean isReadable(final File file) {
        if (file == null)
            return false;
        if (!file.exists()) return false;

        boolean result;
        try {
            result = file.canRead();
        } catch (SecurityException e) {
            return false;
        }

        return result;
    }

    /**
     * Check if a file is writable. Detects write issues on external SD card.
     *
     * @param file The file
     * @return true if the file is writable.
     */
    public static boolean isWritable(final File file) {
        if (file == null)
            return false;
        boolean isExisting = file.exists();

        try {
            FileOutputStream output = new FileOutputStream(file, true);
            try {
                output.close();
            } catch (IOException e) {
                // do nothing.
            }
        } catch (FileNotFoundException e) {
            return false;
        }
        boolean result = file.canWrite();

        // Ensure that file is not created during this process.
        if (!isExisting) {
            file.delete();
        }

        return result;
    }

    // Utility methods for Android 5

    /**
     * Check for a directory if it is possible to create files within this directory, either via normal writing or via
     * Storage Access Framework.
     *
     * @param folder The directory
     * @return true if it is possible to write in this directory.
     */
    public static boolean isWritableNormalOrSaf(final File folder, Context c) {
        // Verify that this is a directory.
        if (folder == null)
            return false;
        if (!folder.exists() || !folder.isDirectory()) {
            return false;
        }

        // Find a non-existing file in this directory.
        int i = 0;
        File file;
        do {
            String fileName = "AugendiagnoseDummyFile" + (++i);
            file = new File(folder, fileName);
        } while (file.exists());

        // First check regular writability
        if (isWritable(file)) {
            return true;
        }

        // Next check SAF writability.
        DocumentFile document = getDocumentFile(file, false, c);

        if (document == null) {
            return false;
        }

        // This should have created the file - otherwise something is wrong with access URL.
        boolean result = document.canWrite() && file.exists();

        // Ensure that the dummy file is not remaining.
        deleteFile(file, c);
        return result;
    }

    /**
     * Get a list of external SD card paths. (Kitkat or higher.)
     *
     * @return A list of external SD card paths.
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    private static String[] getExtSdCardPaths(Context context) {
        List<String> paths = new ArrayList<>();
        for (File file : context.getExternalFilesDirs("external")) {
            if (file != null && !file.equals(context.getExternalFilesDir("external"))) {
                int index = file.getAbsolutePath().lastIndexOf("/Android/data");
                if (index < 0) {
                    Log.w("AmazeFileUtils", "Unexpected external file dir: " + file.getAbsolutePath());
                } else {
                    String path = file.getAbsolutePath().substring(0, index);
                    try {
                        path = new File(path).getCanonicalPath();
                    } catch (IOException e) {
                        // Keep non-canonical path.
                    }
                    paths.add(path);
                }
            }
        }
        if (paths.isEmpty()) paths.add("/storage/sdcard1");
        return paths.toArray(new String[0]);
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static String[] getExtSdCardPathsForActivity(Context context) {
        List<String> paths = new ArrayList<>();
        for (File file : context.getExternalFilesDirs("external")) {
            if (file != null) {
                int index = file.getAbsolutePath().lastIndexOf("/Android/data");
                if (index < 0) {
                    Log.w("AmazeFileUtils", "Unexpected external file dir: " + file.getAbsolutePath());
                } else {
                    String path = file.getAbsolutePath().substring(0, index);
                    try {
                        path = new File(path).getCanonicalPath();
                    } catch (IOException e) {
                        // Keep non-canonical path.
                    }
                    paths.add(path);
                }
            }
        }
        if (paths.isEmpty()) paths.add("/storage/sdcard1");
        return paths.toArray(new String[0]);
    }

    /**
     * Determine the main folder of the external SD card containing the given file.
     *
     * @param file the file.
     * @return The main folder of the external SD card containing this file, if the file is on an SD card. Otherwise,
     * null is returned.
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    private static String getExtSdCardFolder(final File file, Context context) {
        String[] extSdPaths = getExtSdCardPaths(context);
        try {
            for (int i = 0; i < extSdPaths.length; i++) {
                if (file.getCanonicalPath().startsWith(extSdPaths[i])) {
                    return extSdPaths[i];
                }
            }
        } catch (IOException e) {
            return null;
        }
        return null;
    }

    /**
     * Determine if a file is on external sd card. (Kitkat or higher.)
     *
     * @param file The file.
     * @return true if on external sd card.
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static boolean isOnExtSdCard(final File file, Context c) {
        return getExtSdCardFolder(file, c) != null;
    }

    /**
     * Get a DocumentFile corresponding to the given file (for writing on ExtSdCard on Android 5). If the file is not
     * existing, it is created.
     *
     * @param file        The file.
     * @param isDirectory flag indicating if the file should be a directory.
     * @return The DocumentFile
     */
    public static DocumentFile getDocumentFile(final File file, final boolean isDirectory, Context context) {
        final String baseFolder = getExtSdCardFolder(file, context);
        boolean originalDirectory = false;
        if (baseFolder == null) {
            return null;
        }

        String relativePath = null;
        try {
            final String fullPath = file.getCanonicalPath();
            if (!baseFolder.equals(fullPath))
                relativePath = fullPath.substring(baseFolder.length() + 1);
            else 
				originalDirectory = true;
        } catch (IOException e) {
            return null;
        } catch (Exception f) {
            originalDirectory = true;
            //continue
        }
        final String as = PreferenceManager.getDefaultSharedPreferences(context).getString("URI", null);

        Uri treeUri = null;
        if (as != null) 
			treeUri = Uri.parse(as);
        if (treeUri == null) {
            return null;
        }

        // start with root of SD card and then parse through document tree.
        DocumentFile document = DocumentFile.fromTreeUri(context, treeUri);
		if (originalDirectory) 
			return document;
        final String[] parts = relativePath.split("\\/");
		//Log.d("FileUtil", "relativePath " + relativePath);
        for (int i = 0; i < parts.length; i++) {
            //Log.d("FileUtil", "parts[] " + parts[i]);
			if (document == null) {
				return null;
			}
			DocumentFile nextDocument = document.findFile(parts[i]);

            if (nextDocument == null) {
                if ((i < parts.length - 1) || isDirectory) {
                    nextDocument = document.createDirectory(parts[i]);
                } else {
                    nextDocument = document.createFile("image", parts[i]);
                }
            }
            //Log.d("FileUtil", "nextDocument " + nextDocument);
			document = nextDocument;
        }

        return document;
    }

    // Utility methods for Kitkat

    /**
     * Copy a resource file into a private target directory, if the target does not yet exist. Required for the Kitkat
     * workaround.
     *
     * @param resource   The resource file.
     * @param folderName The folder below app folder where the file is copied to.
     * @param targetName The name of the target file.
     * @return the dummy file.
     * @throws IOException
     */
    private static File copyDummyFile(final int resource, final String folderName, final String targetName, Context context)
            throws IOException {
        File externalFilesDir = context.getExternalFilesDir(folderName);
        if (externalFilesDir == null) {
            return null;
        }
        File targetFile = new File(externalFilesDir, targetName);

        if (!targetFile.exists()) {
            InputStream in = null;
            OutputStream out = null;
            try {
                in = context.getResources().openRawResource(resource);
                out = new FileOutputStream(targetFile);
                byte[] buffer = new byte[4096]; // MAGIC_NUMBER
                int bytesRead;
                while ((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException ex) {
                        // do nothing
                    }
                }
                if (out != null) {
                    try {
                        out.close();
                    } catch (IOException ex) {
                        // do nothing
                    }
                }
            }
        }
        return targetFile;
    }

    /**
     * Checks whether the target path exists or is writable
     * @param f the target path
     * @param context
     * @return 1 if exists or writable, 0 if not writable
     */
    public static int checkFolder(final String f,Context context) {
        if(f==null)return 0;
        if(f.startsWith("smb://")
                || f.startsWith(OTGUtil.PREFIX_OTG)
                || f.startsWith(CloudHandler.CLOUD_PREFIX_BOX)
                || f.startsWith(CloudHandler.CLOUD_PREFIX_GOOGLE_DRIVE)
                || f.startsWith(CloudHandler.CLOUD_PREFIX_DROPBOX)
                || f.startsWith(CloudHandler.CLOUD_PREFIX_ONE_DRIVE)
                )
            return 1;

        File folder=new File(f);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && FileUtil.isOnExtSdCard(folder, context)) {
            if (!folder.exists() || !folder.isDirectory()) {
                return 0;
            }

            // On Android 5, trigger storage access framework.
            if (FileUtil.isWritableNormalOrSaf(folder, context)) {
                return 1;

            }
        } else if (Build.VERSION.SDK_INT == 19 && FileUtil.isOnExtSdCard(folder, context)) {
            // Assume that Kitkat workaround works
            return 1;
        } else if (folder.canWrite()) {
            return 1;
        } else {
            return 0;
        }
        return 0;
    }

    /**
     * Copy the dummy image and dummy mp3 into the private folder, if not yet there. Required for the Kitkat workaround.
     *
     * @return the dummy mp3.
     */
//    private static File copyDummyFiles(Context c) {
//        try {
//            copyDummyFile(R.drawable.ic_launcher, "mkdirFiles", "albumart.jpg", c);
//            return copyDummyFile(R.raw.temptrack, "mkdirFiles", "temptrack.mp3", c);
//
//        } catch (IOException e) {
//            Log.e("AmazeFileUtils", "Could not copy dummy files.", e);
//            return null;
//        }
//    }

    static class MediaFile {
        private static final String NO_MEDIA = ".nomedia";
        private static final String ALBUM_ART_URI = "content://media/external/audio/albumart";
        private static final String[] ALBUM_PROJECTION = {BaseColumns._ID, MediaStore.Audio.AlbumColumns.ALBUM_ID, "media_type"};

        private static File getExternalFilesDir(Context context) {


            try {
                Method method = Context.class.getMethod("getExternalFilesDir", String.class);
                return (File) method.invoke(context, (String) null);
            } catch (SecurityException ex) {
                //   Log.d(Maui.LOG_TAG, "Unexpected reflection error.", ex);
                return null;
            } catch (NoSuchMethodException ex) {
                //     Log.d(Maui.LOG_TAG, "Unexpected reflection error.", ex);
                return null;
            } catch (IllegalArgumentException ex) {
                // Log.d(Maui.LOG_TAG, "Unexpected reflection error.", ex);
                return null;
            } catch (IllegalAccessException ex) {
                //Log.d(Maui.LOG_TAG, "Unexpected reflection error.", ex);
                return null;
            } catch (InvocationTargetException ex) {
                //Log.d(Maui.LOG_TAG, "Unexpected reflection error.", ex);
                return null;
            }
        }


        private final File file;
        private final Context context;
        private final ContentResolver contentResolver;
        Uri filesUri;

        MediaFile(Context context, File file) {
            this.file = file;
            this.context = context;
            contentResolver = context.getContentResolver();
            filesUri = MediaStore.Files.getContentUri("external");
        }

        /**
         * Deletes the file. Returns true if the file has been successfully deleted or otherwise does not exist. This operation is not
         * recursive.
         */
        public boolean delete()
                throws IOException {

            if (!file.exists()) {
                return true;
            }

            boolean directory = file.isDirectory();
            if (directory) {
                // Verify directory does not contain any files/directories within it.
                String[] files = file.list();
                if (files != null && files.length > 0) {
                    return false;
                }
            }

            String where = MediaStore.MediaColumns.DATA + "=?";
            String[] selectionArgs = new String[]{file.getAbsolutePath()};

            // Delete the entry from the media database. This will actually delete media files (images, audio, and video).
            contentResolver.delete(filesUri, where, selectionArgs);

            if (file.exists()) {
                // If the file is not a media file, create a new entry suggesting that this location is an image, even
                // though it is not.
                ContentValues values = new ContentValues();
                values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
                contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

                // Delete the created entry, such that content provider will delete the file.
                contentResolver.delete(filesUri, where, selectionArgs);
            }

            return !file.exists();
        }

        public File getFile() {
            return file;
        }

        private int getTemporaryAlbumId() {
            final File temporaryTrack;
            try {
                temporaryTrack = installTemporaryTrack();
            } catch (IOException ex) {
                return 0;
            }

            final String[] selectionArgs = {temporaryTrack.getAbsolutePath()};
            Cursor cursor = contentResolver.query(filesUri, ALBUM_PROJECTION, MediaStore.MediaColumns.DATA + "=?",
                    selectionArgs, null);
            if (cursor == null || !cursor.moveToFirst()) {
                if (cursor != null) {
                    cursor.close();
                    cursor = null;
                }
                ContentValues values = new ContentValues();
                values.put(MediaStore.MediaColumns.DATA, temporaryTrack.getAbsolutePath());
                values.put(MediaStore.MediaColumns.TITLE, "{MediaWrite Workaround}");
                values.put(MediaStore.MediaColumns.SIZE, temporaryTrack.length());
                values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mpeg");
                values.put(MediaStore.Audio.AudioColumns.IS_MUSIC, true);
                contentResolver.insert(filesUri, values);
            }
            cursor = contentResolver.query(filesUri, ALBUM_PROJECTION, MediaStore.MediaColumns.DATA + "=?",
                    selectionArgs, null);
            if (cursor == null) {
                return 0;
            }
            if (!cursor.moveToFirst()) {
                cursor.close();
                return 0;
            }
            int id = cursor.getInt(0);
            int albumId = cursor.getInt(1);
            int mediaType = cursor.getInt(2);
            cursor.close();

            ContentValues values = new ContentValues();
            boolean updateRequired = false;
            if (albumId == 0) {
                values.put(MediaStore.Audio.AlbumColumns.ALBUM_ID, 13371337);
                updateRequired = true;
            }
            if (mediaType != 2) {
                values.put("media_type", 2);
                updateRequired = true;
            }
            if (updateRequired) {
                contentResolver.update(filesUri, values, BaseColumns._ID + "=" + id, null);
            }
            cursor = contentResolver.query(filesUri, ALBUM_PROJECTION, MediaStore.MediaColumns.DATA + "=?",
                    selectionArgs, null);
            if (cursor == null) {
                return 0;
            }

            try {
                if (!cursor.moveToFirst()) {
                    return 0;
                }
                return cursor.getInt(1);
            } finally {
                cursor.close();
            }
        }

        private File installTemporaryTrack()
                throws IOException {
            File externalFilesDir = getExternalFilesDir(context);
            if (externalFilesDir == null) {
                return null;
            }
            File temporaryTrack = new File(externalFilesDir, "temptrack.mp3");
            if (!temporaryTrack.exists()) {
                InputStream in = null;
                OutputStream out = null;
                try {
                    in = context.getResources().openRawResource(R.raw.temptrack);
                    out = new FileOutputStream(temporaryTrack);
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) != -1) {
                        out.write(buffer, 0, bytesRead);
                    }
                } finally {
                    if (in != null) {
                        try {
                            in.close();
                        } catch (IOException ex) {
                            return null;
                        }
                    }
                    if (out != null) {
                        try {
                            out.close();
                        } catch (IOException ex) {
                            return null;
                        }
                    }
                }
            }
            return temporaryTrack;
        }

        public boolean mkdir()
                throws IOException {
            if (file.exists()) {
                return file.isDirectory();
            }

            File tmpFile = new File(file, ".MediaWriteTemp");
            int albumId = getTemporaryAlbumId();

            if (albumId == 0) {
                throw new IOException("Fail");
            }

            Uri albumUri = Uri.parse(ALBUM_ART_URI + '/' + albumId);
            ContentValues values = new ContentValues();
            values.put(MediaStore.MediaColumns.DATA, tmpFile.getAbsolutePath());

            if (contentResolver.update(albumUri, values, null, null) == 0) {
                values.put(MediaStore.Audio.AlbumColumns.ALBUM_ID, albumId);
                contentResolver.insert(Uri.parse(ALBUM_ART_URI), values);
            }

            try {
                ParcelFileDescriptor fd = contentResolver.openFileDescriptor(albumUri, "r");
                fd.close();
            } finally {
                MediaFile tmpMediaFile = new MediaFile(context, tmpFile);
                tmpMediaFile.delete();
            }

            return file.exists();
        }

        /**
         * Returns an OutputStream to write to the file. The file will be truncated immediately.
         */
        public OutputStream write(long size)
                throws IOException {

            if (NO_MEDIA.equals(file.getName().trim())) {
                throw new IOException("Unable to create .nomedia file via media content provider API.");
            }

            if (file.exists() && file.isDirectory()) {
                throw new IOException("File exists and is a directory.");
            }

            // Delete any existing entry from the media database.
            // This may also delete the file (for media types), but that is irrelevant as it will be truncated momentarily in any case.
            String where = MediaStore.MediaColumns.DATA + "=?";
            String[] selectionArgs = new String[]{file.getAbsolutePath()};
            contentResolver.delete(filesUri, where, selectionArgs);

            ContentValues values = new ContentValues();
            values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
            values.put(MediaStore.MediaColumns.SIZE, size);
            Uri uri = contentResolver.insert(filesUri, values);

            if (uri == null) {
                // Should not occur.
                throw new IOException("Internal error.");
            }

            return contentResolver.openOutputStream(uri);
        }

    }

}
