UploadFileActivity protected agains access to non-existing account
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / fragment / OCFileListFragment.java
1 /* ownCloud Android client application
2 * Copyright (C) 2011 Bartek Przybylski
3 * Copyright (C) 2012-2013 ownCloud Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2,
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18 package com.owncloud.android.ui.fragment;
19
20 import java.io.File;
21 import java.util.ArrayList;
22 import java.util.List;
23
24 import com.owncloud.android.AccountUtils;
25 import com.owncloud.android.Log_OC;
26 import com.owncloud.android.R;
27 import com.owncloud.android.datamodel.DataStorageManager;
28 import com.owncloud.android.datamodel.OCFile;
29 import com.owncloud.android.files.FileHandler;
30 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
31 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
32 import com.owncloud.android.operations.OnRemoteOperationListener;
33 import com.owncloud.android.operations.RemoteOperation;
34 import com.owncloud.android.operations.RemoveFileOperation;
35 import com.owncloud.android.operations.RenameFileOperation;
36 import com.owncloud.android.operations.SynchronizeFileOperation;
37 import com.owncloud.android.ui.activity.FileDisplayActivity;
38 import com.owncloud.android.ui.activity.TransferServiceGetter;
39 import com.owncloud.android.ui.adapter.FileListListAdapter;
40 import com.owncloud.android.ui.dialog.EditNameDialog;
41 import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
42 import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
43 import com.owncloud.android.ui.preview.PreviewImageFragment;
44 import com.owncloud.android.ui.preview.PreviewMediaFragment;
45
46 import android.accounts.Account;
47 import android.app.Activity;
48 import android.os.Bundle;
49 import android.os.Handler;
50 import android.view.ContextMenu;
51 import android.view.MenuInflater;
52 import android.view.MenuItem;
53 import android.view.View;
54 import android.widget.AdapterView;
55 import android.widget.AdapterView.AdapterContextMenuInfo;
56
57 /**
58 * A Fragment that lists all files and folders in a given path.
59 *
60 * @author Bartek Przybylski
61 *
62 */
63 public class OCFileListFragment extends ExtendedListFragment implements EditNameDialogListener, ConfirmationDialogFragmentListener {
64
65 private static final String TAG = OCFileListFragment.class.getSimpleName();
66
67 private OCFileListFragment.ContainerActivity mContainerActivity;
68
69 private OCFile mFile = null;
70 private FileListListAdapter mAdapter;
71
72 private Handler mHandler;
73 private OCFile mTargetFile;
74
75 /**
76 * {@inheritDoc}
77 */
78 @Override
79 public void onAttach(Activity activity) {
80 super.onAttach(activity);
81 Log_OC.e(TAG, "onAttach");
82 try {
83 mContainerActivity = (ContainerActivity) activity;
84 } catch (ClassCastException e) {
85 throw new ClassCastException(activity.toString() + " must implement " + OCFileListFragment.ContainerActivity.class.getSimpleName());
86 }
87 }
88
89
90 /**
91 * {@inheritDoc}
92 */
93 @Override
94 public void onActivityCreated(Bundle savedInstanceState) {
95 super.onActivityCreated(savedInstanceState);
96 Log_OC.e(TAG, "onActivityCreated() start");
97 mAdapter = new FileListListAdapter(getActivity(), mContainerActivity);
98 setListAdapter(mAdapter);
99
100 registerForContextMenu(getListView());
101 getListView().setOnCreateContextMenuListener(this);
102
103 mHandler = new Handler();
104 }
105
106
107 /**
108 * Call this, when the user presses the up button
109 */
110 public void onBrowseUp() {
111 OCFile parentDir = null;
112
113 if(mFile != null){
114 DataStorageManager storageManager = mContainerActivity.getStorageManager();
115 parentDir = storageManager.getFileById(mFile.getParentId());
116 mFile = parentDir;
117 }
118 listDirectory(parentDir);
119 }
120
121 @Override
122 public void onItemClick(AdapterView<?> l, View v, int position, long id) {
123 OCFile file = (OCFile) mAdapter.getItem(position);
124 if (file != null) {
125 if (file.isDirectory()) {
126 // update state and view of this fragment
127 listDirectory(file);
128 // then, notify parent activity to let it update its state and view, and other fragments
129 mContainerActivity.onBrowsedDownTo(file);
130
131 } else { /// Click on a file
132 if (PreviewImageFragment.canBePreviewed(file)) {
133 // preview image - it handles the download, if needed
134 mContainerActivity.startImagePreview(file);
135
136 } else if (file.isDown()) {
137 if (PreviewMediaFragment.canBePreviewed(file)) {
138 // media preview
139 mContainerActivity.startMediaPreview(file, 0, true);
140 } else {
141 // open with
142 mContainerActivity.openFile(file);
143 }
144
145 } else {
146 // automatic download, preview on finish
147 mContainerActivity.startDownloadForPreview(file);
148 }
149
150 }
151
152 } else {
153 Log_OC.d(TAG, "Null object in ListAdapter!!");
154 }
155
156 }
157
158 /**
159 * {@inheritDoc}
160 */
161 @Override
162 public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
163 super.onCreateContextMenu(menu, v, menuInfo);
164 MenuInflater inflater = getActivity().getMenuInflater();
165 inflater.inflate(R.menu.file_actions_menu, menu);
166 AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
167 OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
168 List<Integer> toHide = new ArrayList<Integer>();
169 List<Integer> toDisable = new ArrayList<Integer>();
170
171 MenuItem item = null;
172 if (targetFile.isDirectory()) {
173 // contextual menu for folders
174 toHide.add(R.id.action_open_file_with);
175 toHide.add(R.id.action_download_file);
176 toHide.add(R.id.action_cancel_download);
177 toHide.add(R.id.action_cancel_upload);
178 toHide.add(R.id.action_sync_file);
179 toHide.add(R.id.action_see_details);
180 if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ||
181 mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ) {
182 toDisable.add(R.id.action_rename_file);
183 toDisable.add(R.id.action_remove_file);
184
185 }
186
187 } else {
188 // contextual menu for regular files
189
190 // new design: 'download' and 'open with' won't be available anymore in context menu
191 toHide.add(R.id.action_download_file);
192 toHide.add(R.id.action_open_file_with);
193
194 if (targetFile.isDown()) {
195 toHide.add(R.id.action_cancel_download);
196 toHide.add(R.id.action_cancel_upload);
197
198 } else {
199 toHide.add(R.id.action_sync_file);
200 }
201 if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
202 toHide.add(R.id.action_cancel_upload);
203 toDisable.add(R.id.action_rename_file);
204 toDisable.add(R.id.action_remove_file);
205
206 } else if ( mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
207 toHide.add(R.id.action_cancel_download);
208 toDisable.add(R.id.action_rename_file);
209 toDisable.add(R.id.action_remove_file);
210
211 } else {
212 toHide.add(R.id.action_cancel_download);
213 toHide.add(R.id.action_cancel_upload);
214 }
215 }
216
217 for (int i : toHide) {
218 item = menu.findItem(i);
219 if (item != null) {
220 item.setVisible(false);
221 item.setEnabled(false);
222 }
223 }
224
225 for (int i : toDisable) {
226 item = menu.findItem(i);
227 if (item != null) {
228 item.setEnabled(false);
229 }
230 }
231 }
232
233
234 /**
235 * {@inhericDoc}
236 */
237 @Override
238 public boolean onContextItemSelected (MenuItem item) {
239 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
240 mTargetFile = (OCFile) mAdapter.getItem(info.position);
241 switch (item.getItemId()) {
242 case R.id.action_rename_file: {
243 String fileName = mTargetFile.getFileName();
244 int extensionStart = mTargetFile.isDirectory() ? -1 : fileName.lastIndexOf(".");
245 int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
246 EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
247 dialog.show(getFragmentManager(), EditNameDialog.TAG);
248 return true;
249 }
250 case R.id.action_remove_file: {
251 int messageStringId = R.string.confirmation_remove_alert;
252 int posBtnStringId = R.string.confirmation_remove_remote;
253 int neuBtnStringId = -1;
254 if (mTargetFile.isDirectory()) {
255 messageStringId = R.string.confirmation_remove_folder_alert;
256 posBtnStringId = R.string.confirmation_remove_remote_and_local;
257 neuBtnStringId = R.string.confirmation_remove_folder_local;
258 } else if (mTargetFile.isDown()) {
259 posBtnStringId = R.string.confirmation_remove_remote_and_local;
260 neuBtnStringId = R.string.confirmation_remove_local;
261 }
262 ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
263 messageStringId,
264 new String[]{mTargetFile.getFileName()},
265 posBtnStringId,
266 neuBtnStringId,
267 R.string.common_cancel);
268 confDialog.setOnConfirmationListener(this);
269 confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION);
270 return true;
271 }
272 case R.id.action_sync_file: {
273 Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity());
274 RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, false, getSherlockActivity());
275 operation.execute(account, getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
276 getSherlockActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
277 return true;
278 }
279 case R.id.action_cancel_download: {
280 FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
281 Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
282 if (downloaderBinder != null && downloaderBinder.isDownloading(account, mTargetFile)) {
283 downloaderBinder.cancel(account, mTargetFile);
284 listDirectory();
285 mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
286 }
287 return true;
288 }
289 case R.id.action_cancel_upload: {
290 FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
291 Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
292 if (uploaderBinder != null && uploaderBinder.isUploading(account, mTargetFile)) {
293 uploaderBinder.cancel(account, mTargetFile);
294 listDirectory();
295 mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
296 }
297 return true;
298 }
299 case R.id.action_see_details: {
300 ((FileFragment.ContainerActivity)getActivity()).showDetails(mTargetFile);
301 return true;
302 }
303 default:
304 return super.onContextItemSelected(item);
305 }
306 }
307
308
309 /**
310 * Use this to query the {@link OCFile} that is currently
311 * being displayed by this fragment
312 * @return The currently viewed OCFile
313 */
314 public OCFile getCurrentFile(){
315 return mFile;
316 }
317
318 /**
319 * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
320 */
321 public void listDirectory(){
322 listDirectory(null);
323 }
324
325 /**
326 * Lists the given directory on the view. When the input parameter is null,
327 * it will either refresh the last known directory. list the root
328 * if there never was a directory.
329 *
330 * @param directory File to be listed
331 */
332 public void listDirectory(OCFile directory) {
333 DataStorageManager storageManager = mContainerActivity.getStorageManager();
334 if (storageManager != null) {
335
336 // Check input parameters for null
337 if(directory == null){
338 if(mFile != null){
339 directory = mFile;
340 } else {
341 directory = storageManager.getFileByPath("/");
342 if (directory == null) return; // no files, wait for sync
343 }
344 }
345
346
347 // If that's not a directory -> List its parent
348 if(!directory.isDirectory()){
349 Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
350 directory = storageManager.getFileById(directory.getParentId());
351 }
352
353 mAdapter.swapDirectory(directory, storageManager);
354 if (mFile == null || !mFile.equals(directory)) {
355 mList.setSelectionFromTop(0, 0);
356 }
357 mFile = directory;
358 }
359 }
360
361
362
363 /**
364 * Interface to implement by any Activity that includes some instance of FileListFragment
365 *
366 * @author David A. Velasco
367 */
368 public interface ContainerActivity extends TransferServiceGetter, OnRemoteOperationListener, FileHandler {
369
370 /**
371 * Callback method invoked when a the user browsed into a different folder through the list of files
372 *
373 * @param file
374 */
375 public void onBrowsedDownTo(OCFile folder);
376
377 public void startDownloadForPreview(OCFile file);
378
379 public void startMediaPreview(OCFile file, int i, boolean b);
380
381 public void startImagePreview(OCFile file);
382
383 /**
384 * Getter for the current DataStorageManager in the container activity
385 */
386 public DataStorageManager getStorageManager();
387
388
389 /**
390 * Callback method invoked when a the 'transfer state' of a file changes.
391 *
392 * This happens when a download or upload is started or ended for a file.
393 *
394 * This method is necessary by now to update the user interface of the double-pane layout in tablets
395 * because methods {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and {@link FileUploaderBinder#isUploading(Account, OCFile)}
396 * won't provide the needed response before the method where this is called finishes.
397 *
398 * TODO Remove this when the transfer state of a file is kept in the database (other thing TODO)
399 *
400 * @param file OCFile which state changed.
401 * @param downloading Flag signaling if the file is now downloading.
402 * @param uploading Flag signaling if the file is now uploading.
403 */
404 public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading);
405
406 }
407
408
409 @Override
410 public void onDismiss(EditNameDialog dialog) {
411 if (dialog.getResult()) {
412 String newFilename = dialog.getNewFilename();
413 Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
414 RemoteOperation operation = new RenameFileOperation(mTargetFile,
415 AccountUtils.getCurrentOwnCloudAccount(getActivity()),
416 newFilename,
417 mContainerActivity.getStorageManager());
418 operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
419 getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
420 }
421 }
422
423
424 @Override
425 public void onConfirmation(String callerTag) {
426 if (callerTag.equals(FileDetailFragment.FTAG_CONFIRMATION)) {
427 if (mContainerActivity.getStorageManager().getFileById(mTargetFile.getFileId()) != null) {
428 RemoteOperation operation = new RemoveFileOperation( mTargetFile,
429 true,
430 mContainerActivity.getStorageManager());
431 operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
432
433 getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
434 }
435 }
436 }
437
438 @Override
439 public void onNeutral(String callerTag) {
440 File f = null;
441 if (mTargetFile.isDirectory()) {
442 // TODO run in a secondary thread?
443 mContainerActivity.getStorageManager().removeDirectory(mTargetFile, false, true);
444
445 } else if (mTargetFile.isDown() && (f = new File(mTargetFile.getStoragePath())).exists()) {
446 f.delete();
447 mTargetFile.setStoragePath(null);
448 mContainerActivity.getStorageManager().saveFile(mTargetFile);
449 }
450 listDirectory();
451 mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
452 }
453
454 @Override
455 public void onCancel(String callerTag) {
456 Log_OC.d(TAG, "REMOVAL CANCELED");
457 }
458
459
460 }