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