annotate src/uk/ac/qmul/eecs/ccmi/activities/FileSelectorActivity.java @ 1:66b3a838feca logging tip

Added logging of user interaction
author Fiore Martin <fiore@eecs.qmul.ac.uk>
date Tue, 12 Feb 2013 15:31:48 +0000
parents e0ee6ac3a45f
children
rev   line source
fiore@0 1 /*
fiore@0 2 CCmI Diagram Editor for Android
fiore@0 3
fiore@0 4 Copyright (C) 2012 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
fiore@0 5
fiore@0 6 This program is free software: you can redistribute it and/or modify
fiore@0 7 it under the terms of the GNU General Public License as published by
fiore@0 8 the Free Software Foundation, either version 3 of the License, or
fiore@0 9 (at your option) any later version.
fiore@0 10
fiore@0 11 This program is distributed in the hope that it will be useful,
fiore@0 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
fiore@0 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
fiore@0 14 GNU General Public License for more details.
fiore@0 15
fiore@0 16 You should have received a copy of the GNU General Public License
fiore@0 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
fiore@0 18 */
fiore@0 19 package uk.ac.qmul.eecs.ccmi.activities;
fiore@0 20
fiore@0 21 import java.io.File;
fiore@0 22 import java.io.FileFilter;
fiore@0 23 import java.util.ArrayList;
fiore@0 24 import java.util.List;
fiore@0 25
fiore@0 26 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService.SoundEvent;
fiore@0 27 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService;
fiore@0 28 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibleDialogBuilder;
fiore@0 29 import android.content.Intent;
fiore@0 30 import android.net.Uri;
fiore@0 31 import android.os.Bundle;
fiore@0 32 import android.os.Environment;
fiore@0 33 import android.support.v4.app.DialogFragment;
fiore@0 34 import android.view.View;
fiore@0 35 import android.widget.AdapterView;
fiore@0 36 import android.widget.AdapterView.OnItemClickListener;
fiore@0 37 import android.widget.AdapterView.OnItemLongClickListener;
fiore@0 38 import android.widget.ArrayAdapter;
fiore@0 39 import android.widget.EditText;
fiore@0 40 import android.widget.TextView;
fiore@0 41
fiore@0 42 /**
fiore@0 43 * An abstract activity for file selection.
fiore@0 44 *
fiore@0 45 * The file system is shown as follows: the current directory is displayed
fiore@0 46 * 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 47 * will only take effect if the item is a directory. The selected directory will become the current directory.
fiore@0 48 * In order to select the parent directory a click on the back button is needed.
fiore@0 49 *
fiore@0 50 * Being an {@code AccessibleActivity} the view can be explored though sound and haptics by hovering on it.
fiore@0 51 * in order to scroll the list a double-finger scroll is required or, if the device does not support multitouch, a
fiore@0 52 * scroll button will appear on the bottom for scrolling.
fiore@0 53 *
fiore@0 54 */
fiore@0 55 public abstract class FileSelectorActivity extends AccessibleActivity implements
fiore@0 56 OnItemClickListener, OnItemLongClickListener {
fiore@0 57
fiore@0 58 private File[] currentFileList;
fiore@0 59 private File currentFile;
fiore@0 60 private FileFilter fileFilter;
fiore@0 61
fiore@0 62 @Override
fiore@0 63 public void onCreate(Bundle savedInstanceState) {
fiore@0 64 super.onCreate(savedInstanceState);
fiore@0 65
fiore@0 66 list.setOnItemClickListener(this);
fiore@0 67 list.setOnItemLongClickListener(this);
fiore@0 68
fiore@0 69 final CharSequence extension = getIntent().getCharSequenceExtra("extension");
fiore@0 70 fileFilter = new FileFilter() {
fiore@0 71 /* only accepts directories and .ccmi files */
fiore@0 72 public boolean accept(File file) {
fiore@0 73 if (file.isDirectory()) {
fiore@0 74 return true;
fiore@0 75 }
fiore@0 76
fiore@0 77 if(extension == null)
fiore@0 78 return true;
fiore@0 79 else
fiore@0 80 return file.getName().endsWith(extension.toString());
fiore@0 81 }
fiore@0 82 };
fiore@0 83
fiore@0 84 update(new File(Environment.getExternalStorageDirectory().getPath()));
fiore@0 85 }
fiore@0 86
fiore@0 87 /* false if new list is empty, true otherwise */
fiore@0 88 private boolean update(File file) {
fiore@0 89
fiore@0 90 currentFile = file;
fiore@0 91 currentFileList = currentFile.listFiles(fileFilter);
fiore@0 92
fiore@0 93 setHeaderText(currentFile.getName());
fiore@0 94 List<String> names = new ArrayList<String>(currentFileList.length);
fiore@0 95
fiore@0 96 for (int i = 0; i < currentFileList.length; i ++) {
fiore@0 97 String fileName = currentFileList[i].getName();
fiore@0 98 if (currentFileList[i].isDirectory()) {
fiore@0 99 fileName = "/" + fileName;
fiore@0 100 }
fiore@0 101 names.add(fileName);
fiore@0 102 }
fiore@0 103
fiore@0 104 list.setAdapter(new ArrayAdapter<String>(this,
fiore@0 105 R.layout.list_item_1, names));
fiore@0 106
fiore@0 107 return !names.isEmpty();
fiore@0 108 }
fiore@0 109
fiore@0 110 @Override
fiore@0 111 public void onItemClick(AdapterView<?> av, View v, int position, long id) {
fiore@0 112 // the first thing in list is parent directory, so we must offset it
fiore@0 113 File selectedFile = currentFileList[position];
fiore@0 114 if (selectedFile.isDirectory()) {
fiore@0 115 if(selectedFile.listFiles(fileFilter).length == 0){
fiore@0 116 accessibilityService.playSound(SoundEvent.V_ERROR);
fiore@0 117 accessibilityService.speak("Directory empty");
fiore@0 118 }
fiore@0 119 boolean anyItems = update(selectedFile);
fiore@0 120 accessibilityService.playSound(AccessibilityService.SoundEvent.V_EXPAND);
fiore@0 121 if(anyItems)
fiore@0 122 accessibilityService.speak("Displaying " + getHeaderText());
fiore@0 123 else
fiore@0 124 accessibilityService.speak("Displaying " + getHeaderText()+". Empty");
fiore@0 125 }else{
fiore@0 126 accessibilityService.playSound(SoundEvent.V_ERROR);
fiore@0 127 }
fiore@0 128 }
fiore@0 129
fiore@0 130 /**
fiore@0 131 * To be implemented by subclasses. The long click represent the trigger of an operation on the selected file.
fiore@0 132 */
fiore@0 133 @Override
fiore@0 134 public abstract boolean onItemLongClick(AdapterView<?> av, View v, int position, long id);
fiore@0 135
fiore@0 136 /**
fiore@0 137 * On pressing the back button, the parent of the current directory will be displayed.
fiore@0 138 */
fiore@0 139 @Override
fiore@0 140 public void onBackPressed() {
fiore@0 141 if(currentFile.getAbsolutePath().equals(Environment.getExternalStorageDirectory().getPath())){
fiore@0 142 setResult(RESULT_CANCELED);
fiore@0 143 super.onBackPressed(); // finish the activity
fiore@0 144 return;
fiore@0 145 }
fiore@0 146
fiore@0 147 File parent = currentFile.getParentFile();
fiore@0 148 update(parent);
fiore@0 149 accessibilityService.playSound(SoundEvent.V_COLLAPSE);
fiore@0 150 accessibilityService.speak("Displaying "+getHeaderText());
fiore@0 151 }
fiore@0 152
fiore@0 153 /**
fiore@0 154 * A {@code FileSelectorActivity} for opening a file. A long click on an item will open a confirmation dialog
fiore@0 155 * to open the file. If the user confirms, the activity will finish and set a result intent with the Uri
fiore@0 156 * of the opened file.
fiore@0 157 */
fiore@0 158 public static class Open extends FileSelectorActivity {
fiore@0 159 /**
fiore@0 160 * Displays a confirmation dialog to open the file
fiore@0 161 */
fiore@0 162 @Override
fiore@0 163 public boolean onItemLongClick(AdapterView<?> av, View v, int position, long id) {
fiore@0 164 final File selectedFile = super.currentFileList[position];
fiore@0 165 if(selectedFile.isDirectory()){
fiore@0 166 accessibilityService.playSound(SoundEvent.V_ERROR);
fiore@0 167 return true;
fiore@0 168 }
fiore@0 169
fiore@0 170 dialogBuilder.displayDialog(R.layout.alert_dialog_confirmation, "Do you want to Open File "+(((TextView)v).getText())+" ?", new AccessibleDialogBuilder.ButtonClickListener(){
fiore@0 171 @Override
fiore@0 172 public void onClick(View v, DialogFragment dialogFragment, String dialogTag){
fiore@0 173 if("YES".equals(v.getTag())){ // YES
fiore@0 174 dialogFragment.dismiss();
fiore@0 175 Intent resultIntent = new Intent();
fiore@0 176 resultIntent.setData(Uri.fromFile(selectedFile));
fiore@0 177 setResult(RESULT_OK, resultIntent);
fiore@0 178 finish();
fiore@0 179 }else{ // NO
fiore@0 180 dialogFragment.dismiss();
fiore@0 181 accessibilityService.playSound(SoundEvent.V_CANCEL);
fiore@0 182 accessibilityService.speak(getHeaderText().toString());
fiore@0 183 }
fiore@0 184 }
fiore@0 185 });
fiore@0 186 return true;
fiore@0 187 }
fiore@0 188
fiore@0 189 @Override
fiore@0 190 public String getName(){
fiore@0 191 return "Select File to Open";
fiore@0 192 }
fiore@0 193 }
fiore@0 194
fiore@0 195 /**
fiore@0 196 * A {@code FileSelectorActivity} for saving a file. A long click on an item will open a text dialog
fiore@0 197 * to enter the name of the file to save. If the user clicks on a file list item, the text field will
fiore@0 198 * be filled with the name thereof.
fiore@0 199 */
fiore@0 200 public static class Save extends FileSelectorActivity {
fiore@0 201
fiore@0 202 /**
fiore@0 203 * When the user long-clicks on an item a text dialog for the user to enter the file name appears.
fiore@0 204 */
fiore@0 205 @Override
fiore@0 206 public boolean onItemLongClick(AdapterView<?> av, View v, int position, long id) {
fiore@0 207 final File selectedFile = super.currentFileList[position];
fiore@0 208
fiore@0 209 final String text = selectedFile.isDirectory() ? "" : selectedFile.getName();
fiore@0 210 dialogBuilder.displayTextDialog("Enter File name ", text, new AccessibleDialogBuilder.ButtonClickListener(){
fiore@0 211 @Override
fiore@0 212 public void onClick(View v, DialogFragment dialogFragment, String dialogTag) {
fiore@0 213 Object buttonTag = v.getTag();
fiore@0 214 if("OK".equals(buttonTag)){
fiore@0 215 EditText editText = (EditText)dialogFragment.getDialog().findViewById(R.id.text_edit);
fiore@0 216 String text = editText.getText().toString().trim();
fiore@0 217
fiore@0 218 if(text.length() == 0){
fiore@0 219 accessibilityService.playSound(SoundEvent.V_ERROR);
fiore@0 220 accessibilityService.speak("File name cannot be empty");
fiore@0 221 return;
fiore@0 222 }
fiore@0 223
fiore@0 224 /* if the selected file was a directory, then concat it with the text entered by the user *
fiore@0 225 * if it was a file, then concat the directory where the file was with the text entered by the user *
fiore@0 226 * In the latter the user can overwrite a file just by clicking OK without entering any text */
fiore@0 227 Uri dir = selectedFile.isDirectory() ? Uri.fromFile(selectedFile) : Uri.fromFile(selectedFile.getParentFile());
fiore@0 228
fiore@0 229 Intent resultIntent = new Intent();
fiore@0 230 resultIntent.setData(Uri.withAppendedPath(dir, text));
fiore@0 231 setResult(RESULT_OK, resultIntent);
fiore@0 232 finish();
fiore@0 233 }else{
fiore@0 234 dialogFragment.dismiss();
fiore@0 235 accessibilityService.playSound(SoundEvent.V_CANCEL);
fiore@0 236 accessibilityService.speak(getHeaderText().toString());
fiore@0 237 }
fiore@0 238 }
fiore@0 239
fiore@0 240 });
fiore@0 241
fiore@0 242 return false;
fiore@0 243 }
fiore@0 244
fiore@0 245 public String getName(){
fiore@0 246 return "Select File to Save";
fiore@0 247 }
fiore@0 248
fiore@0 249 }
fiore@0 250
fiore@0 251 }