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: }