/* * �Copyright 2013 Jose F. Maldonado� * * This file is part of aFileDialog. * * aFileDialog is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * aFileDialog is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with aFileDialog. If not, see <http://www.gnu.org/licenses/>. */ package ar.com.daidalos.afiledialog; import java.io.*; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.os.Environment; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import ar.com.daidalos.afiledialog.view.FileItem; import com.example.avoscloud_demo.R; /** * This class implements the common features of a file chooser. */ class FileChooserCore { // ----- Attributes ----- // /** * The file chooser in which all the operations are performed. */ private FileChooser chooser; /** * The listeners for the event of select a file. */ private List<OnFileSelectedListener> listeners; /** * A regular expression for filter the files. */ private String filter; /** * A boolean indicating if only the files that can be selected (they pass the filter) must be show. */ private boolean showOnlySelectable; /** * A boolean indicating if the user can create files. */ private boolean canCreateFiles; /** * A boolean indicating if the chooser is going to be used to select folders. */ private boolean folderMode; /** * A file that indicates the folder that is currently being displayed. */ private File currentFolder; /** * This attribut allows to override the default value of the labels. */ private FileChooserLabels labels; /** * A boolean that indicates if a confirmation dialog must be displaying when selecting a file. */ private boolean showConfirmationOnSelect; /** * A boolean that indicates if a confirmation dialog must be displaying when creating a file. */ private boolean showConfirmationOnCreate; /** * A boolean indicating if the folder's full path must be show in the title. */ private boolean showFullPathInTitle; // ---- Static attributes ----- // /** * Static attribute for save the folder displayed by default. */ private static File defaultFolder; /** * Static constructor. */ static { defaultFolder = null; } // ----- Constructor ----- // /** * Creates an instance of this class. * * @param fileChooser The graphical file chooser. */ public FileChooserCore(FileChooser fileChooser) { // Initialize attributes. this.chooser = fileChooser; this.listeners = new LinkedList<OnFileSelectedListener>(); this.filter = null; this.showOnlySelectable = false; this.setCanCreateFiles(false); this.setFolderMode(false); this.currentFolder = null; this.labels = null; this.showConfirmationOnCreate = false; this.showConfirmationOnSelect = false; this.showFullPathInTitle = false; // Add listener for the buttons. LinearLayout root = this.chooser.getRootLayout(); Button addButton = (Button) root.findViewById(R.id.buttonAdd); addButton.setOnClickListener(addButtonClickListener); Button okButton = (Button) root.findViewById(R.id.buttonOk); okButton.setOnClickListener(okButtonClickListener); } // ----- Events methods ----- // /** * Implementation of the click listener for when the add button is clicked. */ private View.OnClickListener addButtonClickListener = new View.OnClickListener() { public void onClick(View v) { // Get the current context. Context context = v.getContext(); // Create an alert dialog. AlertDialog.Builder alert = new AlertDialog.Builder(context); // Define the dialog's labels. String title = context.getString(FileChooserCore.this.folderMode? R.string.daidalos_create_folder : R.string.daidalos_create_file); if(FileChooserCore.this.labels != null && FileChooserCore.this.labels.createFileDialogTitle != null) title = FileChooserCore.this.labels.createFileDialogTitle; String message = context.getString(FileChooserCore.this.folderMode? R.string.daidalos_enter_folder_name : R.string.daidalos_enter_file_name); if(FileChooserCore.this.labels != null && FileChooserCore.this.labels.createFileDialogMessage != null) message = FileChooserCore.this.labels.createFileDialogMessage; String posButton = (FileChooserCore.this.labels != null && FileChooserCore.this.labels.createFileDialogAcceptButton != null)? FileChooserCore.this.labels.createFileDialogAcceptButton : context.getString(R.string.daidalos_accept); String negButton = (FileChooserCore.this.labels != null && FileChooserCore.this.labels.createFileDialogCancelButton != null)? FileChooserCore.this.labels.createFileDialogCancelButton : context.getString(R.string.daidalos_cancel); // Set the title and the message. alert.setTitle( title ); alert.setMessage( message ); // Set an EditText view to get the file's name. final EditText input = new EditText(context); alert.setView(input); // Set the 'ok' and 'cancel' buttons. alert.setPositiveButton(posButton, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String fileName = input.getText().toString(); // Verify if a value has been entered. if(fileName != null && fileName.length() > 0) { // Notify the listeners. FileChooserCore.this.notifyListeners(FileChooserCore.this.currentFolder, fileName); } } }); alert.setNegativeButton(negButton, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Do nothing, automatically the dialog is going to be closed. } }); // Show the dialog. alert.show(); } }; /** * Implementation of the click listener for when the ok button is clicked. */ private View.OnClickListener okButtonClickListener = new View.OnClickListener() { public void onClick(View v) { // Notify the listeners. FileChooserCore.this.notifyListeners(FileChooserCore.this.currentFolder, null); } }; /** * Implementation of the click listener for when a file item is clicked. */ private FileItem.OnFileClickListener fileItemClickListener = new FileItem.OnFileClickListener() { public void onClick(FileItem source) { // Verify if the item is a folder. File file = source.getFile(); if(file.isDirectory()) { // Open the folder. FileChooserCore.this.loadFolder(file); } else { // Notify the listeners. FileChooserCore.this.notifyListeners(file, null); } } }; /** * Add a listener for the event of a file selected. * * @param listener The listener to add. */ public void addListener(OnFileSelectedListener listener) { this.listeners.add(listener); } /** * Removes a listener for the event of a file selected. * * @param listener The listener to remove. */ public void removeListener(OnFileSelectedListener listener) { this.listeners.remove(listener); } /** * Removes all the listeners for the event of a file selected. */ public void removeAllListeners() { this.listeners.clear(); } /** * Interface definition for a callback to be invoked when a file is selected. */ public interface OnFileSelectedListener { /** * Called when a file has been selected. * * @param file The file selected. */ void onFileSelected(File file); /** * Called when an user wants to be create a file. * * @param folder The file's parent folder. * @param name The file's name. */ void onFileSelected(File folder, String name); } /** * Notify to all listeners that a file has been selected or created. * * @param file The file or folder selected or the folder in which the file must be created. * @param name The name of the file that must be created or 'null' if a file was selected (instead of being created). */ private void notifyListeners(final File file, final String name) { // Determine if a file has been selected or created. final boolean creation = name != null && name.length() > 0; // Verify if a confirmation dialog must be show. if((creation && this.showConfirmationOnCreate || !creation && this.showConfirmationOnSelect)) { // Create an alert dialog. Context context = this.chooser.getContext(); AlertDialog.Builder alert = new AlertDialog.Builder(context); // Define the dialog's labels. String message = null; if(FileChooserCore.this.labels != null && ((creation && FileChooserCore.this.labels.messageConfirmCreation != null) || (!creation && FileChooserCore.this.labels.messageConfirmSelection != null))) { message = creation? FileChooserCore.this.labels.messageConfirmCreation : FileChooserCore.this.labels.messageConfirmSelection; } else { if(FileChooserCore.this.folderMode) { message = context.getString(creation? R.string.daidalos_confirm_create_folder : R.string.daidalos_confirm_select_folder); } else { message = context.getString(creation? R.string.daidalos_confirm_create_file : R.string.daidalos_confirm_select_file); } } if(message != null) message = message.replace("$file_name", name!=null? name : file.getName()); String posButton = (FileChooserCore.this.labels != null && FileChooserCore.this.labels.labelConfirmYesButton != null)? FileChooserCore.this.labels.labelConfirmYesButton : context.getString(R.string.daidalos_yes); String negButton = (FileChooserCore.this.labels != null && FileChooserCore.this.labels.labelConfirmNoButton != null)? FileChooserCore.this.labels.labelConfirmNoButton : context.getString(R.string.daidalos_no); // Set the message and the 'yes' and 'no' buttons. alert.setMessage( message ); alert.setPositiveButton(posButton, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Notify to listeners. for(int i=0; i<FileChooserCore.this.listeners.size(); i++) { if(creation) { FileChooserCore.this.listeners.get(i).onFileSelected(file, name); } else { FileChooserCore.this.listeners.get(i).onFileSelected(file); } } } }); alert.setNegativeButton(negButton, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Do nothing, automatically the dialog is going to be closed. } }); // Show the dialog. alert.show(); } else { // Notify to listeners. for(int i=0; i<FileChooserCore.this.listeners.size(); i++) { if(creation) { FileChooserCore.this.listeners.get(i).onFileSelected(file, name); } else { FileChooserCore.this.listeners.get(i).onFileSelected(file); } } } } // ----- Get and set methods ----- // /** * Allows to define if a confirmation dialog must be show when selecting a file. * * @param show 'true' for show the confirmation dialog, 'false' for not show the dialog. */ public void setShowConfirmationOnSelect(boolean show) { this.showConfirmationOnSelect = show; } /** * Allows to define if a confirmation dialog must be show when creating a file. * * @param show 'true' for show the confirmation dialog, 'false' for not show the dialog. */ public void setShowConfirmationOnCreate(boolean show) { this.showConfirmationOnCreate = show; } /** * Allows to define if, in the title, must be show only the current folder's name or the full file's path.. * * @param show 'true' for show the full path, 'false' for show only the name. */ public void setShowFullPathInTitle(boolean show) { this.showFullPathInTitle = show; } /** * Defines the value of the labels. * * @param label The labels. */ public void setLabels(FileChooserLabels labels) { this.labels = labels; // Verify if the buttons for add a file or select a folder has been modified. if(labels != null) { LinearLayout root = this.chooser.getRootLayout(); if(labels.labelAddButton != null) { Button addButton = (Button) root.findViewById(R.id.buttonAdd); addButton.setText(labels.labelAddButton); } if(labels.labelSelectButton != null) { Button okButton = (Button) root.findViewById(R.id.buttonOk); okButton.setText(labels.labelSelectButton); } } } /** * Set a regular expression to filter the files that can be selected. * * @param filter A regular expression. */ public void setFilter(String filter) { if(filter == null || filter.length() == 0 ) { this.filter = null; } else { this.filter = filter; } // Reload the list of files. this.loadFolder(this.currentFolder); } /** * Defines if the chooser is going to be used to select folders, instead of files. * * @param folderMode 'true' for select folders or 'false' for select files. */ public void setFolderMode(boolean folderMode) { this.folderMode = folderMode; // Show or hide the 'Ok' button. updateButtonsLayout(); // Reload the list of files. this.loadFolder(this.currentFolder); } /** * Defines if the user can create files, instead of only select files. * * @param canCreate 'true' if the user can create files or 'false' if it can only select them. */ public void setCanCreateFiles(boolean canCreate) { this.canCreateFiles = canCreate; // Show or hide the 'Add' button. updateButtonsLayout(); } /** * Defines if only the files that can be selected (they pass the filter) must be show. * * @param show 'true' if only the files that can be selected must be show or 'false' if all the files must be show. */ public void setShowOnlySelectable(boolean show) { this.showOnlySelectable = show; // Reload the list of files. this.loadFolder(this.currentFolder); } /** * Returns the current folder. * * @return The current folder. */ public File getCurrentFolder() { return this.currentFolder; } // ----- Miscellaneous methods ----- // /** * Changes the height of the layout for the buttons, according if the buttons are visible or not. */ private void updateButtonsLayout() { // Get the buttons layout. LinearLayout root = this.chooser.getRootLayout(); LinearLayout buttonsLayout = (LinearLayout) root.findViewById(R.id.linearLayoutButtons); // Verify if the 'Add' button is visible or not. View addButton = root.findViewById(R.id.buttonAdd); addButton.setVisibility(this.canCreateFiles? View.VISIBLE : View.INVISIBLE); addButton.getLayoutParams().width = this.canCreateFiles? ViewGroup.LayoutParams.MATCH_PARENT : 0; // Verify if the 'Ok' button is visible or not. View okButton = root.findViewById(R.id.buttonOk); okButton.setVisibility(this.folderMode? View.VISIBLE : View.INVISIBLE); okButton.getLayoutParams().width = this.folderMode? ViewGroup.LayoutParams.MATCH_PARENT : 0; // If both buttons are invisible, hide the layout. ViewGroup.LayoutParams params = buttonsLayout.getLayoutParams(); if(this.canCreateFiles || this.folderMode) { // Show the layout. params.height = ViewGroup.LayoutParams.WRAP_CONTENT; // If only the 'Ok' button is visible, put him first. Otherwise, put 'Add' first. buttonsLayout.removeAllViews(); if(this.folderMode && !this.canCreateFiles) { buttonsLayout.addView(okButton); buttonsLayout.addView(addButton); } else { buttonsLayout.addView(addButton); buttonsLayout.addView(okButton); } } else { // Hide the layout. params.height = 0; } } /** * Loads all the files of the SD card root. */ public void loadFolder() { this.loadFolder(defaultFolder); } /** * Loads all the files of a folder in the file chooser. * * If no path is specified ('folderPath' is null) the root folder of the SD card is going to be used. * * @param folderPath The folder's path. */ public void loadFolder(String folderPath) { // Get the file path. File path = null; if(folderPath != null && folderPath.length() > 0) { path = new File(folderPath); } this.loadFolder(path); } /** * Loads all the files of a folder in the file chooser. * * If no path is specified ('folder' is null) the root folder of the SD card is going to be used. * * @param folder The folder. */ public void loadFolder(File folder) { // Remove previous files. LinearLayout root = this.chooser.getRootLayout(); LinearLayout layout = (LinearLayout) root.findViewById(R.id.linearLayoutFiles); layout.removeAllViews(); // Get the file path. if(folder == null || !folder.exists()) { if(defaultFolder != null) { this.currentFolder = defaultFolder; } else { this.currentFolder = Environment.getExternalStorageDirectory(); } } else { this.currentFolder = folder; } // Verify if the path exists. if(this.currentFolder.exists() && layout != null) { List<FileItem> fileItems = new LinkedList<FileItem>(); // Add the parent folder. if(this.currentFolder.getParent() != null) { File parent = new File(this.currentFolder.getParent()); if(parent.exists()) { fileItems.add(new FileItem(this.chooser.getContext(), parent, "..")); } } // Verify if the file is a directory. if(this.currentFolder.isDirectory()) { // Get the folder's files. File[] fileList = this.currentFolder.listFiles(); if(fileList != null) { // Order the files alphabetically and separating folders from files. Arrays.sort(fileList, new Comparator<File>() { public int compare(File file1, File file2) { if(file1 != null && file2 != null) { if(file1.isDirectory() && (!file2.isDirectory())) return -1; if(file2.isDirectory() && (!file1.isDirectory())) return 1; return file1.getName().compareTo(file2.getName()); } return 0; } }); // Iterate all the files in the folder. for(int i=0; i<fileList.length; i++) { // Verify if file can be selected (is a directory or folder mode is not activated and the file pass the filter, if defined). boolean selectable = true; if(!fileList[i].isDirectory()) { selectable = !this.folderMode && (this.filter == null || fileList[i].getName().matches(this.filter)); } // Verify if the file must be show. if(selectable || !this.showOnlySelectable) { // Create the file item and add it to the list. FileItem fileItem = new FileItem(this.chooser.getContext(), fileList[i]); fileItem.setSelectable(selectable); fileItems.add(fileItem); } } } // Set the name of the current folder. String currentFolderName = this.showFullPathInTitle? this.currentFolder.getPath() : this.currentFolder.getName(); this.chooser.setCurrentFolderName(currentFolderName); } else { // The file is not a folder, add only this file. fileItems.add(new FileItem(this.chooser.getContext(), this.currentFolder)); } // Add click listener and add the FileItem objects to the layout. for(int i=0; i<fileItems.size(); i++) { fileItems.get(i).addListener(this.fileItemClickListener); layout.addView(fileItems.get(i)); } // Refresh default folder. defaultFolder = this.currentFolder; } } }