com.aniruddhc.acemusic.player.FoldersFragment.FilesFoldersFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.aniruddhc.acemusic.player.FoldersFragment.FilesFoldersFragment.java

Source

/*
 * Copyright (C) 2014 Saravan Pantham
 *
 * 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.aniruddhc.acemusic.player.FoldersFragment;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

import com.aniruddhc.acemusic.player.AsyncTasks.AsyncCopyMoveTask;
import com.aniruddhc.acemusic.player.AsyncTasks.AsyncDeleteTask;
import com.aniruddhc.acemusic.player.Helpers.UIElementsHelper;
import com.aniruddhc.acemusic.player.MainActivity.MainActivity;
import com.aniruddhc.acemusic.player.R;
import com.aniruddhc.acemusic.player.Utils.Common;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.comparator.NameFileComparator;

import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * FilesFoldersFragment. Contained within MainActivity.
 * 
 * @author Saravan Pantham
 */
public class FilesFoldersFragment extends Fragment {

    //Context.
    private Context mContext;
    private FilesFoldersFragment mFilesFoldersFragment;
    private Common mApp;

    //UI Elements.
    private ListView listView;

    //Folder parameter ArrayLists.
    private String rootDir;
    public static String currentDir;
    private List<String> fileFolderNameList = null;
    private List<String> fileFolderPathList = null;
    private List<String> fileFolderSizeList = null;
    private List<Integer> fileFolderTypeList = null;

    //File size/unit dividers
    private final long kiloBytes = 1024;
    private final long megaBytes = kiloBytes * kiloBytes;
    private final long gigaBytes = megaBytes * kiloBytes;
    private final long teraBytes = gigaBytes * kiloBytes;

    //List of subdirectories within a directory (Used by "Play Folder Recursively").
    private ArrayList<String> subdirectoriesList = new ArrayList<String>();

    //Flag that determines whether hidden files are displayed or not.
    private boolean SHOW_HIDDEN_FILES = false;

    //Temp file for copy/move operations.
    public File copyMoveSourceFile;

    //Flag that indicates if copyMoveSourceFile should be moved or copied.
    public boolean shouldMoveCopiedFile = false;
    public boolean mIsPasteShown = false;

    //HashMap to store the each folder's previous scroll/position state.
    private HashMap<String, Parcelable> mFolderStateMap;

    //Handler.
    private Handler mHandler = new Handler();

    public static final int FOLDER = 0;
    public static final int FILE = 1;
    public static final int AUDIO_FILE = 3;
    public static final int PICTURE_FILE = 4;
    public static final int VIDEO_FILE = 5;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_folders, container, false);
        mContext = getActivity().getApplicationContext();
        mFilesFoldersFragment = this;
        mApp = (Common) mContext;
        mFolderStateMap = new HashMap<String, Parcelable>();

        //Set the hidden files flag.
        SHOW_HIDDEN_FILES = mApp.getSharedPreferences().getBoolean("SHOW_HIDDEN_FILES", false);

        listView = (ListView) rootView.findViewById(R.id.folders_list_view);
        listView.setFastScrollEnabled(true);
        listView.setVisibility(View.INVISIBLE);

        //Set the background color based on the theme.
        rootView.setBackgroundColor(UIElementsHelper.getBackgroundColor(mContext));

        //Apply the ListView params.
        //Apply the ListViews' dividers.
        if (mApp.getCurrentTheme() == Common.DARK_THEME) {
            listView.setDivider(mContext.getResources().getDrawable(R.drawable.icon_list_divider));
        } else {
            listView.setDivider(mContext.getResources().getDrawable(R.drawable.icon_list_divider_light));
        }

        listView.setDividerHeight(1);

        //KitKat translucent navigation/status bar.
        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
            int topPadding = Common.getStatusBarHeight(mContext);

            //Calculate navigation bar height.
            int navigationBarHeight = 0;
            int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
            if (resourceId > 0) {
                navigationBarHeight = getResources().getDimensionPixelSize(resourceId);
            }

            if (rootView != null) {
                rootView.setPadding(0, topPadding, 0, 0);
            }

            listView.setClipToPadding(false);
            listView.setPadding(0, 0, 0, navigationBarHeight);
        }

        rootDir = mApp.getSharedPreferences().getString("DEFAULT_FOLDER",
                Environment.getExternalStorageDirectory().getPath());
        currentDir = rootDir;
        mHandler.postDelayed(new Runnable() {

            @Override
            public void run() {
                slideUpListView();
            }

        }, 250);
        return rootView;
    }

    /**
     * Slides in the ListView.
     */
    private void slideUpListView() {

        getDir(rootDir, null);

        TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 2.0f, Animation.RELATIVE_TO_SELF,
                0.0f);

        animation.setDuration(600);
        animation.setInterpolator(new AccelerateDecelerateInterpolator());
        animation.setAnimationListener(new AnimationListener() {

            @Override
            public void onAnimationEnd(Animation arg0) {

            }

            @Override
            public void onAnimationRepeat(Animation arg0) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onAnimationStart(Animation arg0) {
                listView.setVisibility(View.VISIBLE);

            }

        });

        listView.startAnimation(animation);
    }

    /**
     * Retrieves the folder hierarchy for the specified folder.
     *
     * @param dirPath The path of the new folder.
     * @param restoreState The state of the ListView that should be restored. Pass
     *                     null if the ListView's position should not be restored.
     */
    private void getDir(String dirPath, Parcelable restoreState) {

        ((MainActivity) getActivity()).showFolderFragmentActionItems(dirPath, getActivity().getMenuInflater(),
                ((MainActivity) getActivity()).getMenu(), mIsPasteShown);

        fileFolderNameList = new ArrayList<String>();
        fileFolderPathList = new ArrayList<String>();
        fileFolderSizeList = new ArrayList<String>();
        fileFolderTypeList = new ArrayList<Integer>();

        File f = new File(dirPath);
        File[] files = f.listFiles();

        if (files != null) {

            //Sort the files by name.
            Arrays.sort(files, NameFileComparator.NAME_INSENSITIVE_COMPARATOR);

            for (int i = 0; i < files.length; i++) {

                File file = files[i];
                if (file.isHidden() == SHOW_HIDDEN_FILES && file.canRead()) {

                    if (file.isDirectory()) {

                        /*
                        * Starting with Android 4.2, /storage/emulated/legacy/...
                        * is a symlink that points to the actual directory where
                        * the user's files are stored. We need to detect the
                        * actual directory's file path here.
                        */
                        String filePath;
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
                            filePath = getRealFilePath(file.getAbsolutePath());
                        else
                            filePath = file.getAbsolutePath();

                        fileFolderPathList.add(filePath);
                        fileFolderNameList.add(file.getName());
                        File[] listOfFiles = file.listFiles();

                        if (listOfFiles != null) {
                            fileFolderTypeList.add(FOLDER);
                            if (listOfFiles.length == 1) {
                                fileFolderSizeList.add("" + listOfFiles.length + " item");
                            } else {
                                fileFolderSizeList.add("" + listOfFiles.length + " items");
                            }

                        } else {
                            fileFolderTypeList.add(FOLDER);
                            fileFolderSizeList.add("Unknown items");
                        }

                    } else {

                        try {
                            String path = file.getCanonicalPath();
                            fileFolderPathList.add(path);
                        } catch (IOException e) {
                            continue;
                        }

                        fileFolderNameList.add(file.getName());
                        String fileName = "";
                        try {
                            fileName = file.getCanonicalPath();
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }

                        //Add the file element to fileFolderTypeList based on the file type.
                        if (getFileExtension(fileName).equalsIgnoreCase("mp3")
                                || getFileExtension(fileName).equalsIgnoreCase("3gp")
                                || getFileExtension(fileName).equalsIgnoreCase("mp4")
                                || getFileExtension(fileName).equalsIgnoreCase("m4a")
                                || getFileExtension(fileName).equalsIgnoreCase("aac")
                                || getFileExtension(fileName).equalsIgnoreCase("ts")
                                || getFileExtension(fileName).equalsIgnoreCase("flac")
                                || getFileExtension(fileName).equalsIgnoreCase("mid")
                                || getFileExtension(fileName).equalsIgnoreCase("xmf")
                                || getFileExtension(fileName).equalsIgnoreCase("mxmf")
                                || getFileExtension(fileName).equalsIgnoreCase("midi")
                                || getFileExtension(fileName).equalsIgnoreCase("rtttl")
                                || getFileExtension(fileName).equalsIgnoreCase("rtx")
                                || getFileExtension(fileName).equalsIgnoreCase("ota")
                                || getFileExtension(fileName).equalsIgnoreCase("imy")
                                || getFileExtension(fileName).equalsIgnoreCase("ogg")
                                || getFileExtension(fileName).equalsIgnoreCase("mkv")
                                || getFileExtension(fileName).equalsIgnoreCase("wav")) {

                            //The file is an audio file.
                            fileFolderTypeList.add(AUDIO_FILE);
                            fileFolderSizeList.add("" + getFormattedFileSize(file.length()));

                        } else if (getFileExtension(fileName).equalsIgnoreCase("jpg")
                                || getFileExtension(fileName).equalsIgnoreCase("gif")
                                || getFileExtension(fileName).equalsIgnoreCase("png")
                                || getFileExtension(fileName).equalsIgnoreCase("bmp")
                                || getFileExtension(fileName).equalsIgnoreCase("webp")) {

                            //The file is a picture file.
                            fileFolderTypeList.add(PICTURE_FILE);
                            fileFolderSizeList.add("" + getFormattedFileSize(file.length()));

                        } else if (getFileExtension(fileName).equalsIgnoreCase("3gp")
                                || getFileExtension(fileName).equalsIgnoreCase("mp4")
                                || getFileExtension(fileName).equalsIgnoreCase("3gp")
                                || getFileExtension(fileName).equalsIgnoreCase("ts")
                                || getFileExtension(fileName).equalsIgnoreCase("webm")
                                || getFileExtension(fileName).equalsIgnoreCase("mkv")) {

                            //The file is a video file.
                            fileFolderTypeList.add(VIDEO_FILE);
                            fileFolderSizeList.add("" + getFormattedFileSize(file.length()));

                        } else {

                            //We don't have an icon for this file type so give it the generic file flag.
                            fileFolderTypeList.add(FILE);
                            fileFolderSizeList.add("" + getFormattedFileSize(file.length()));

                        }

                    }

                }

            }

        }

        FoldersListViewAdapter foldersListViewAdapter = new FoldersListViewAdapter(getActivity(), this,
                fileFolderNameList, fileFolderTypeList, fileFolderSizeList, fileFolderPathList);

        listView.setAdapter(foldersListViewAdapter);
        foldersListViewAdapter.notifyDataSetChanged();

        //Restore the ListView's previous state.
        if (restoreState != null) {
            listView.onRestoreInstanceState(restoreState);
        } else if (mFolderStateMap.containsKey(dirPath)) {
            listView.onRestoreInstanceState(mFolderStateMap.get(dirPath));
        }

        listView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> arg0, View view, int index, long arg3) {

                //Store the current folder's state in the HashMap.
                if (mFolderStateMap.size() == 3) {
                    mFolderStateMap.clear();
                }

                mFolderStateMap.put(currentDir, listView.onSaveInstanceState());

                String newPath = fileFolderPathList.get(index);
                if ((Integer) view.getTag(R.string.folder_list_item_type) == FOLDER)
                    currentDir = newPath;

                //Check if the selected item is a folder or a file.
                if (fileFolderTypeList.get(index) == FOLDER) {
                    getDir(newPath, null);
                } else {
                    int fileIndex = 0;
                    for (int i = 0; i < index; i++) {
                        if (fileFolderTypeList.get(i) == AUDIO_FILE)
                            fileIndex++;
                    }

                    play(fileFolderTypeList.get(index), fileIndex, currentDir);
                }

            }

        });

    }

    /**
     * Refreshes the ListView with the current dataset.
     */
    public void refreshListView() {
        //Update the ListView.
        getDir(currentDir, listView.onSaveInstanceState());
    }

    /**
     * Resolves the /storage/emulated/legacy paths to
     * their true folder path representations. Required
     * for Nexii and other devices with no SD card.
     */
    @SuppressLint("SdCardPath")
    private String getRealFilePath(String filePath) {

        if (filePath.equals("/storage/emulated/0") || filePath.equals("/storage/emulated/0/")
                || filePath.equals("/storage/emulated/legacy") || filePath.equals("/storage/emulated/legacy/")
                || filePath.equals("/storage/sdcard0") || filePath.equals("/storage/sdcard0/")
                || filePath.equals("/sdcard") || filePath.equals("/sdcard/") || filePath.equals("/mnt/sdcard")
                || filePath.equals("/mnt/sdcard/")) {

            return Environment.getExternalStorageDirectory().toString();
        }

        return filePath;
    }

    /**
     * Calculates the parent dir of the current dir and calls getDir().
     * Returns true if the parent dir is the rootDir
     */
    public boolean getParentDir() {

        if (currentDir.equals("/"))
            return true;

        //Get the current folder's parent folder.
        File currentFolder = new File(currentDir);
        String parentFolder = "";
        try {
            parentFolder = currentFolder.getParentFile().getCanonicalPath();
        } catch (Exception e) {
            e.printStackTrace();
        }

        FilesFoldersFragment.currentDir = parentFolder;
        getDir(parentFolder, null);
        return false;

    }

    /**
     * Takes in a file size value and formats it.
     */
    public String getFormattedFileSize(final long value) {

        final long[] dividers = new long[] { teraBytes, gigaBytes, megaBytes, kiloBytes, 1 };
        final String[] units = new String[] { "TB", "GB", "MB", "KB", "bytes" };

        if (value < 1) {
            return "";
        }

        String result = null;
        for (int i = 0; i < dividers.length; i++) {
            final long divider = dividers[i];
            if (value >= divider) {
                result = format(value, divider, units[i]);
                break;
            }

        }

        return result;
    }

    public String format(final long value, final long divider, final String unit) {
        final double result = divider > 1 ? (double) value / (double) divider : (double) value;

        return new DecimalFormat("#,##0.#").format(result) + " " + unit;
    }

    public String getFileExtension(String fileName) {
        String fileNameArray[] = fileName.split("\\.");
        String extension = fileNameArray[fileNameArray.length - 1];

        return extension;

    }

    /**
     * This method goes through a folder recursively and saves all its
     * subdirectories to an ArrayList (subdirectoriesList). 
     */
    public void iterateThruFolder(String path) {

        File root = new File(path);
        File[] list = root.listFiles();

        if (list == null) {
            return;
        }

        for (File f : list) {

            if (f.isDirectory()) {
                iterateThruFolder(f.getAbsolutePath());

                if (!subdirectoriesList.contains(f.getPath())) {
                    subdirectoriesList.add(f.getPath());
                }

            }

        }

    }

    /**
     * Plays the specified file/folder.
     *
     * @param itemType Specifies whether the input path is a file path
     *                 or a folder path.
     * @param index The index of the first song to play. Pass 0 if itemType
     *              is FOLDER.
     * @param folderPath The path of the folder that should be played.
     */
    public void play(int itemType, int index, String folderPath) {
        //Build the query's selection clause.
        String querySelection = MediaStore.Audio.Media.DATA + " LIKE " + "'" + folderPath.replace("'", "''")
                + "/%'";

        //Exclude all subfolders from this playback sequence if we're playing a file.
        if (itemType == AUDIO_FILE) {
            for (int i = 0; i < fileFolderPathList.size(); i++) {
                if (fileFolderTypeList.get(i) == FOLDER)
                    querySelection += " AND " + MediaStore.Audio.Media.DATA + " NOT LIKE " + "'"
                            + fileFolderPathList.get(i).replace("'", "''") + "/%'";

            }

            mApp.getPlaybackKickstarter().initPlayback(mContext, querySelection, Common.PLAY_ALL_IN_FOLDER, index,
                    true, false);

        } else if (itemType == FOLDER) {
            mApp.getPlaybackKickstarter().initPlayback(mContext, querySelection, Common.PLAY_ALL_IN_FOLDER, index,
                    true, false);
        } else {
            Toast.makeText(mContext, R.string.cant_play_this_file, Toast.LENGTH_SHORT).show();
        }

    }

    /**
     * Displays a "Rename" dialog and renames the specified file/folder.
     *
     * @param path The path of the folder/file that needs to be renamed.
     */
    public void rename(String path) {

        final File renameFile = new File(path);
        final AlertDialog renameAlertDialog = new AlertDialog.Builder(getActivity()).create();
        final EditText fileEditText = new EditText(getActivity());

        fileEditText.setHint(R.string.file_name);
        fileEditText.setSingleLine(true);
        fileEditText.setText(renameFile.getName());

        renameAlertDialog.setView(fileEditText);
        renameAlertDialog.setTitle(R.string.rename);
        renameAlertDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
                mContext.getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        renameAlertDialog.dismiss();
                    }

                });

        renameAlertDialog.setButton(DialogInterface.BUTTON_POSITIVE,
                mContext.getResources().getString(R.string.rename), new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                        //Check if the new file name is empty.
                        if (fileEditText.getText().toString().isEmpty()) {
                            Toast.makeText(getActivity(), R.string.enter_a_name_for_folder, Toast.LENGTH_LONG)
                                    .show();
                        } else {

                            File newNameFile = null;
                            try {
                                newNameFile = new File(renameFile.getParentFile().getCanonicalPath() + "/"
                                        + fileEditText.getText().toString());
                            } catch (IOException e) {
                                e.printStackTrace();
                                Toast.makeText(getActivity(), R.string.folder_could_not_be_renamed,
                                        Toast.LENGTH_LONG).show();
                                return;
                            }

                            try {
                                if (renameFile.isDirectory())
                                    FileUtils.moveDirectory(renameFile, newNameFile);
                                else
                                    FileUtils.moveFile(renameFile, newNameFile);

                            } catch (IOException e) {
                                e.printStackTrace();
                                Toast.makeText(getActivity(), R.string.folder_could_not_be_renamed,
                                        Toast.LENGTH_LONG).show();
                                return;
                            }

                            Toast.makeText(getActivity(), R.string.folder_renamed, Toast.LENGTH_SHORT).show();
                            renameAlertDialog.dismiss();
                            refreshListView();

                        }

                    }

                });

        renameAlertDialog.show();

    }

    /**
     * Stores the specified file/folder's path in a temp variable and displays
     * the "Paste" option in the ActionBar.
     *
     * @param path The path of the file/folder to copy/move.
     * @param shouldMove Pass true if the file/folder should be moved instead of copied.
     */
    public void copyMove(String path, boolean shouldMove) {
        shouldMoveCopiedFile = shouldMove;
        copyMoveSourceFile = new File(path);
        if (!copyMoveSourceFile.exists()) {
            Toast.makeText(mContext, R.string.cant_copy_this_file_folder, Toast.LENGTH_SHORT).show();
            return;
        }

        //Show the paste option in the ActionBar.
        mIsPasteShown = true;
        ((MainActivity) getActivity()).showFolderFragmentActionItems(currentDir, getActivity().getMenuInflater(),
                ((MainActivity) getActivity()).getMenu(), true);

    }

    /**
     * Pastes the specified file into the current directory.
     *
     * @param file The file to paste into the current directory.
     */
    public void pasteIntoCurrentDir(File file) {
        mIsPasteShown = false;
        AsyncCopyMoveTask task = new AsyncCopyMoveTask(mContext, file, new File(currentDir + "/" + file.getName()),
                this, shouldMoveCopiedFile);
        task.execute();
    }

    /**
     * Deletes the specified file.
     *
     * @param file The file to delete.
     */
    public void deleteFile(File file) {
        int fileType;
        if (file.isDirectory())
            fileType = FOLDER;
        else
            fileType = FILE;

        AsyncDeleteTask task = new AsyncDeleteTask(getActivity(), this, file, fileType);
        task.execute();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mContext = null;
        listView = null;

    }

    /*
     * Getter methods. 
     */
    public String getCurrentDir() {
        return currentDir;
    }

    /*
     * Setter methods.
     */
    public void setCurrentDir(String currentDir) {
        this.currentDir = currentDir;
    }

}