fiore@0: /* fiore@0: CCmI Diagram Editor for Android fiore@0: fiore@0: Copyright (C) 2012 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) fiore@0: fiore@0: This program is free software: you can redistribute it and/or modify fiore@0: it under the terms of the GNU General Public License as published by fiore@0: the Free Software Foundation, either version 3 of the License, or fiore@0: (at your option) any later version. fiore@0: fiore@0: This program is distributed in the hope that it will be useful, fiore@0: but WITHOUT ANY WARRANTY; without even the implied warranty of fiore@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fiore@0: GNU General Public License for more details. fiore@0: fiore@0: You should have received a copy of the GNU General Public License fiore@0: along with this program. If not, see . fiore@0: */ fiore@0: package uk.ac.qmul.eecs.ccmi.activities; fiore@0: fiore@0: import java.io.File; fiore@0: import java.io.FileFilter; fiore@0: import java.util.ArrayList; fiore@0: import java.util.List; fiore@0: fiore@0: import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService.SoundEvent; fiore@0: import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService; fiore@0: import uk.ac.qmul.eecs.ccmi.accessibility.AccessibleDialogBuilder; fiore@0: import android.content.Intent; fiore@0: import android.net.Uri; fiore@0: import android.os.Bundle; fiore@0: import android.os.Environment; fiore@0: import android.support.v4.app.DialogFragment; fiore@0: import android.view.View; fiore@0: import android.widget.AdapterView; fiore@0: import android.widget.AdapterView.OnItemClickListener; fiore@0: import android.widget.AdapterView.OnItemLongClickListener; fiore@0: import android.widget.ArrayAdapter; fiore@0: import android.widget.EditText; fiore@0: import android.widget.TextView; fiore@0: fiore@0: /** fiore@0: * An abstract activity for file selection. fiore@0: * fiore@0: * The file system is shown as follows: the current directory is displayed fiore@0: * on a the header and all the files or folder contained in it are displayed in a list view. Clicking on a list item fiore@0: * will only take effect if the item is a directory. The selected directory will become the current directory. fiore@0: * In order to select the parent directory a click on the back button is needed. fiore@0: * fiore@0: * Being an {@code AccessibleActivity} the view can be explored though sound and haptics by hovering on it. fiore@0: * in order to scroll the list a double-finger scroll is required or, if the device does not support multitouch, a fiore@0: * scroll button will appear on the bottom for scrolling. fiore@0: * fiore@0: */ fiore@0: public abstract class FileSelectorActivity extends AccessibleActivity implements fiore@0: OnItemClickListener, OnItemLongClickListener { fiore@0: fiore@0: private File[] currentFileList; fiore@0: private File currentFile; fiore@0: private FileFilter fileFilter; fiore@0: fiore@0: @Override fiore@0: public void onCreate(Bundle savedInstanceState) { fiore@0: super.onCreate(savedInstanceState); fiore@0: fiore@0: list.setOnItemClickListener(this); fiore@0: list.setOnItemLongClickListener(this); fiore@0: fiore@0: final CharSequence extension = getIntent().getCharSequenceExtra("extension"); fiore@0: fileFilter = new FileFilter() { fiore@0: /* only accepts directories and .ccmi files */ fiore@0: public boolean accept(File file) { fiore@0: if (file.isDirectory()) { fiore@0: return true; fiore@0: } fiore@0: fiore@0: if(extension == null) fiore@0: return true; fiore@0: else fiore@0: return file.getName().endsWith(extension.toString()); fiore@0: } fiore@0: }; fiore@0: fiore@0: update(new File(Environment.getExternalStorageDirectory().getPath())); fiore@0: } fiore@0: fiore@0: /* false if new list is empty, true otherwise */ fiore@0: private boolean update(File file) { fiore@0: fiore@0: currentFile = file; fiore@0: currentFileList = currentFile.listFiles(fileFilter); fiore@0: fiore@0: setHeaderText(currentFile.getName()); fiore@0: List names = new ArrayList(currentFileList.length); fiore@0: fiore@0: for (int i = 0; i < currentFileList.length; i ++) { fiore@0: String fileName = currentFileList[i].getName(); fiore@0: if (currentFileList[i].isDirectory()) { fiore@0: fileName = "/" + fileName; fiore@0: } fiore@0: names.add(fileName); fiore@0: } fiore@0: fiore@0: list.setAdapter(new ArrayAdapter(this, fiore@0: R.layout.list_item_1, names)); fiore@0: fiore@0: return !names.isEmpty(); fiore@0: } fiore@0: fiore@0: @Override fiore@0: public void onItemClick(AdapterView av, View v, int position, long id) { fiore@0: // the first thing in list is parent directory, so we must offset it fiore@0: File selectedFile = currentFileList[position]; fiore@0: if (selectedFile.isDirectory()) { fiore@0: if(selectedFile.listFiles(fileFilter).length == 0){ fiore@0: accessibilityService.playSound(SoundEvent.V_ERROR); fiore@0: accessibilityService.speak("Directory empty"); fiore@0: } fiore@0: boolean anyItems = update(selectedFile); fiore@0: accessibilityService.playSound(AccessibilityService.SoundEvent.V_EXPAND); fiore@0: if(anyItems) fiore@0: accessibilityService.speak("Displaying " + getHeaderText()); fiore@0: else fiore@0: accessibilityService.speak("Displaying " + getHeaderText()+". Empty"); fiore@0: }else{ fiore@0: accessibilityService.playSound(SoundEvent.V_ERROR); fiore@0: } fiore@0: } fiore@0: fiore@0: /** fiore@0: * To be implemented by subclasses. The long click represent the trigger of an operation on the selected file. fiore@0: */ fiore@0: @Override fiore@0: public abstract boolean onItemLongClick(AdapterView av, View v, int position, long id); fiore@0: fiore@0: /** fiore@0: * On pressing the back button, the parent of the current directory will be displayed. fiore@0: */ fiore@0: @Override fiore@0: public void onBackPressed() { fiore@0: if(currentFile.getAbsolutePath().equals(Environment.getExternalStorageDirectory().getPath())){ fiore@0: setResult(RESULT_CANCELED); fiore@0: super.onBackPressed(); // finish the activity fiore@0: return; fiore@0: } fiore@0: fiore@0: File parent = currentFile.getParentFile(); fiore@0: update(parent); fiore@0: accessibilityService.playSound(SoundEvent.V_COLLAPSE); fiore@0: accessibilityService.speak("Displaying "+getHeaderText()); fiore@0: } fiore@0: fiore@0: /** fiore@0: * A {@code FileSelectorActivity} for opening a file. A long click on an item will open a confirmation dialog fiore@0: * to open the file. If the user confirms, the activity will finish and set a result intent with the Uri fiore@0: * of the opened file. fiore@0: */ fiore@0: public static class Open extends FileSelectorActivity { fiore@0: /** fiore@0: * Displays a confirmation dialog to open the file fiore@0: */ fiore@0: @Override fiore@0: public boolean onItemLongClick(AdapterView av, View v, int position, long id) { fiore@0: final File selectedFile = super.currentFileList[position]; fiore@0: if(selectedFile.isDirectory()){ fiore@0: accessibilityService.playSound(SoundEvent.V_ERROR); fiore@0: return true; fiore@0: } fiore@0: fiore@0: dialogBuilder.displayDialog(R.layout.alert_dialog_confirmation, "Do you want to Open File "+(((TextView)v).getText())+" ?", new AccessibleDialogBuilder.ButtonClickListener(){ fiore@0: @Override fiore@0: public void onClick(View v, DialogFragment dialogFragment, String dialogTag){ fiore@0: if("YES".equals(v.getTag())){ // YES fiore@0: dialogFragment.dismiss(); fiore@0: Intent resultIntent = new Intent(); fiore@0: resultIntent.setData(Uri.fromFile(selectedFile)); fiore@0: setResult(RESULT_OK, resultIntent); fiore@0: finish(); fiore@0: }else{ // NO fiore@0: dialogFragment.dismiss(); fiore@0: accessibilityService.playSound(SoundEvent.V_CANCEL); fiore@0: accessibilityService.speak(getHeaderText().toString()); fiore@0: } fiore@0: } fiore@0: }); fiore@0: return true; fiore@0: } fiore@0: fiore@0: @Override fiore@0: public String getName(){ fiore@0: return "Select File to Open"; fiore@0: } fiore@0: } fiore@0: fiore@0: /** fiore@0: * A {@code FileSelectorActivity} for saving a file. A long click on an item will open a text dialog fiore@0: * to enter the name of the file to save. If the user clicks on a file list item, the text field will fiore@0: * be filled with the name thereof. fiore@0: */ fiore@0: public static class Save extends FileSelectorActivity { fiore@0: fiore@0: /** fiore@0: * When the user long-clicks on an item a text dialog for the user to enter the file name appears. fiore@0: */ fiore@0: @Override fiore@0: public boolean onItemLongClick(AdapterView av, View v, int position, long id) { fiore@0: final File selectedFile = super.currentFileList[position]; fiore@0: fiore@0: final String text = selectedFile.isDirectory() ? "" : selectedFile.getName(); fiore@0: dialogBuilder.displayTextDialog("Enter File name ", text, new AccessibleDialogBuilder.ButtonClickListener(){ fiore@0: @Override fiore@0: public void onClick(View v, DialogFragment dialogFragment, String dialogTag) { fiore@0: Object buttonTag = v.getTag(); fiore@0: if("OK".equals(buttonTag)){ fiore@0: EditText editText = (EditText)dialogFragment.getDialog().findViewById(R.id.text_edit); fiore@0: String text = editText.getText().toString().trim(); fiore@0: fiore@0: if(text.length() == 0){ fiore@0: accessibilityService.playSound(SoundEvent.V_ERROR); fiore@0: accessibilityService.speak("File name cannot be empty"); fiore@0: return; fiore@0: } fiore@0: fiore@0: /* if the selected file was a directory, then concat it with the text entered by the user * fiore@0: * if it was a file, then concat the directory where the file was with the text entered by the user * fiore@0: * In the latter the user can overwrite a file just by clicking OK without entering any text */ fiore@0: Uri dir = selectedFile.isDirectory() ? Uri.fromFile(selectedFile) : Uri.fromFile(selectedFile.getParentFile()); fiore@0: fiore@0: Intent resultIntent = new Intent(); fiore@0: resultIntent.setData(Uri.withAppendedPath(dir, text)); fiore@0: setResult(RESULT_OK, resultIntent); fiore@0: finish(); fiore@0: }else{ fiore@0: dialogFragment.dismiss(); fiore@0: accessibilityService.playSound(SoundEvent.V_CANCEL); fiore@0: accessibilityService.speak(getHeaderText().toString()); fiore@0: } fiore@0: } fiore@0: fiore@0: }); fiore@0: fiore@0: return false; fiore@0: } fiore@0: fiore@0: public String getName(){ fiore@0: return "Select File to Save"; fiore@0: } fiore@0: fiore@0: } fiore@0: fiore@0: }