Versionstring shows YYYYMMDD
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / fragment / OCFileListFragment.java
1 /**
2 * ownCloud Android client application
3 *
4 * @author Bartek Przybylski
5 * @author masensio
6 * @author David A. Velasco
7 * Copyright (C) 2011 Bartek Przybylski
8 * Copyright (C) 2015 ownCloud Inc.
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23 package com.owncloud.android.ui.fragment;
24
25 import android.accounts.Account;
26 import android.app.Activity;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.Intent;
30 import android.content.SharedPreferences;
31 import android.os.Build;
32 import android.net.Uri;
33 import android.os.Bundle;
34 import android.preference.PreferenceManager;
35 import android.support.v4.widget.SwipeRefreshLayout;
36 import android.view.ActionMode;
37 import android.support.v7.app.AlertDialog;
38 import android.view.ContextMenu;
39 import android.view.Menu;
40 import android.view.MenuInflater;
41 import android.view.MenuItem;
42 import android.view.View;
43 import android.widget.AbsListView;
44 import android.widget.AdapterView;
45 import android.widget.AdapterView.AdapterContextMenuInfo;
46 import android.widget.PopupMenu;
47 import android.widget.TextView;
48 import android.widget.Toast;
49
50 import com.owncloud.android.MainApp;
51 import com.owncloud.android.R;
52 import com.owncloud.android.authentication.AccountUtils;
53 import com.owncloud.android.datamodel.FileDataStorageManager;
54 import com.owncloud.android.datamodel.OCFile;
55 import com.owncloud.android.files.FileMenuFilter;
56 import com.owncloud.android.lib.common.utils.Log_OC;
57 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
58 import com.owncloud.android.media.MediaService;
59 import com.owncloud.android.ui.activity.FileActivity;
60 import com.owncloud.android.ui.activity.FileDisplayActivity;
61 import com.owncloud.android.ui.activity.FolderPickerActivity;
62 import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
63 import com.owncloud.android.ui.activity.UploadFilesActivity;
64 import com.owncloud.android.ui.adapter.FileListListAdapter;
65 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
66 import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
67 import com.owncloud.android.ui.dialog.FileActionsDialogFragment;
68 import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
69 import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
70 import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
71 import com.owncloud.android.ui.dialog.UploadSourceDialogFragment;
72 import com.owncloud.android.ui.preview.PreviewImageFragment;
73 import com.owncloud.android.ui.preview.PreviewMediaFragment;
74 import com.owncloud.android.utils.DisplayUtils;
75 import com.owncloud.android.utils.ExceptionHandler;
76 import com.owncloud.android.utils.FileStorageUtils;
77 import com.owncloud.android.ui.preview.PreviewTextFragment;
78 import com.owncloud.android.utils.FileStorageUtils;
79
80 import java.io.File;
81 import java.util.ArrayList;
82
83 /**
84 * A Fragment that lists all files and folders in a given path.
85 *
86 * TODO refactor to get rid of direct dependency on FileDisplayActivity
87 */
88 public class OCFileListFragment extends ExtendedListFragment {
89
90 private static final String TAG = OCFileListFragment.class.getSimpleName();
91
92 private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ?
93 OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
94
95 public final static String ARG_JUST_FOLDERS = MY_PACKAGE + ".JUST_FOLDERS";
96 public final static String ARG_ALLOW_CONTEXTUAL_ACTIONS = MY_PACKAGE + ".ALLOW_CONTEXTUAL";
97 public final static String ARG_HIDE_FAB = MY_PACKAGE + ".HIDE_FAB";
98
99 private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
100 private static final String KEY_FAB_EVER_CLICKED = "FAB_EVER_CLICKED";
101
102 private static String DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER";
103
104 private FileFragment.ContainerActivity mContainerActivity;
105
106 private OCFile mFile = null;
107 private FileListListAdapter mAdapter;
108 private boolean mJustFolders;
109
110 private OCFile mTargetFile;
111
112 private boolean miniFabClicked = false;
113
114 /**
115 * {@inheritDoc}
116 */
117 @Override
118 public void onAttach(Activity activity) {
119 super.onAttach(activity);
120 Log_OC.e(TAG, "onAttach");
121 try {
122 mContainerActivity = (FileFragment.ContainerActivity) activity;
123
124 } catch (ClassCastException e) {
125 throw new ClassCastException(activity.toString() + " must implement " +
126 FileFragment.ContainerActivity.class.getSimpleName());
127 }
128 try {
129 setOnRefreshListener((OnEnforceableRefreshListener) activity);
130
131 } catch (ClassCastException e) {
132 throw new ClassCastException(activity.toString() + " must implement " +
133 SwipeRefreshLayout.OnRefreshListener.class.getSimpleName());
134 }
135 }
136
137
138 @Override
139 public void onDetach() {
140 setOnRefreshListener(null);
141 mContainerActivity = null;
142 super.onDetach();
143 }
144
145 /**
146 * {@inheritDoc}
147 */
148 @Override
149 public void onActivityCreated(Bundle savedInstanceState) {
150 super.onActivityCreated(savedInstanceState);
151 Log_OC.e(TAG, "onActivityCreated() start");
152
153 if (savedInstanceState != null) {
154 mFile = savedInstanceState.getParcelable(KEY_FILE);
155 }
156
157 if (mJustFolders) {
158 setFooterEnabled(false);
159 } else {
160 setFooterEnabled(true);
161 }
162
163 Bundle args = getArguments();
164 mJustFolders = (args == null) ? false : args.getBoolean(ARG_JUST_FOLDERS, false);
165 mAdapter = new FileListListAdapter(
166 mJustFolders,
167 getActivity(),
168 mContainerActivity
169 );
170 setListAdapter(mAdapter);
171
172 registerLongClickListener();
173
174 boolean hideFab = (args != null) && args.getBoolean(ARG_HIDE_FAB, false);
175 if (hideFab) {
176 setFabEnabled(false);
177 } else {
178 setFabEnabled(true);
179 registerFabListeners();
180
181 // detect if a mini FAB has ever been clicked
182 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
183 if(prefs.getLong(KEY_FAB_EVER_CLICKED, 0) > 0) {
184 miniFabClicked = true;
185 }
186
187 // add labels to the min FABs when none of them has ever been clicked on
188 if(!miniFabClicked) {
189 setFabLabels();
190 } else {
191 removeFabLabels();
192 }
193 }
194 }
195
196 /**
197 * adds labels to all mini FABs.
198 */
199 private void setFabLabels() {
200 getFabUpload().setTitle(getResources().getString(R.string.actionbar_upload));
201 getFabMkdir().setTitle(getResources().getString(R.string.actionbar_mkdir));
202 getFabUploadFromApp().setTitle(getResources().getString(R.string.actionbar_upload_from_apps));
203 }
204
205 /**
206 * registers all listeners on all mini FABs.
207 */
208 private void registerFabListeners() {
209 registerFabUploadListeners();
210 registerFabMkDirListeners();
211 registerFabUploadFromAppListeners();
212 }
213
214 /**
215 * registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
216 * on the Upload mini FAB for the linked action and {@link Toast} showing the underlying action.
217 */
218 private void registerFabUploadListeners() {
219 getFabUpload().setOnClickListener(new View.OnClickListener() {
220 @Override
221 public void onClick(View v) {
222 Intent action = new Intent(getActivity(), UploadFilesActivity.class);
223 action.putExtra(
224 UploadFilesActivity.EXTRA_ACCOUNT,
225 ((FileActivity) getActivity()).getAccount()
226 );
227 getActivity().startActivityForResult(action, UploadSourceDialogFragment.ACTION_SELECT_MULTIPLE_FILES);
228 getFabMain().collapse();
229 recordMiniFabClick();
230 }
231 });
232
233 getFabUpload().setOnLongClickListener(new View.OnLongClickListener() {
234 @Override
235 public boolean onLongClick(View v) {
236 Toast.makeText(getActivity(), R.string.actionbar_upload, Toast.LENGTH_SHORT).show();
237 return true;
238 }
239 });
240 }
241
242 /**
243 * registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
244 * on the 'Create Dir' mini FAB for the linked action and {@link Toast} showing the underlying action.
245 */
246 private void registerFabMkDirListeners() {
247 getFabMkdir().setOnClickListener(new View.OnClickListener() {
248 @Override
249 public void onClick(View v) {
250 CreateFolderDialogFragment dialog =
251 CreateFolderDialogFragment.newInstance(mFile);
252 dialog.show(getActivity().getSupportFragmentManager(), FileDisplayActivity.DIALOG_CREATE_FOLDER);
253 getFabMain().collapse();
254 recordMiniFabClick();
255 }
256 });
257
258 getFabMkdir().setOnLongClickListener(new View.OnLongClickListener() {
259 @Override
260 public boolean onLongClick(View v) {
261 Toast.makeText(getActivity(), R.string.actionbar_mkdir, Toast.LENGTH_SHORT).show();
262 return true;
263 }
264 });
265 }
266
267 /**
268 * registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
269 * on the Upload from App mini FAB for the linked action and {@link Toast} showing the underlying action.
270 */
271 private void registerFabUploadFromAppListeners() {
272 getFabUploadFromApp().setOnClickListener(new View.OnClickListener() {
273 @Override
274 public void onClick(View v) {
275 Intent action = new Intent(Intent.ACTION_GET_CONTENT);
276 action = action.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE);
277
278 //Intent.EXTRA_ALLOW_MULTIPLE is only supported on api level 18+, Jelly Bean
279 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
280 action.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
281 }
282
283 getActivity().startActivityForResult(
284 Intent.createChooser(action, getString(R.string.upload_chooser_title)),
285 UploadSourceDialogFragment.ACTION_SELECT_CONTENT_FROM_APPS
286 );
287 getFabMain().collapse();
288 recordMiniFabClick();
289 }
290 });
291
292 getFabUploadFromApp().setOnLongClickListener(new View.OnLongClickListener() {
293 @Override
294 public boolean onLongClick(View v) {
295 Toast.makeText(getActivity(),
296 R.string.actionbar_upload_from_apps,
297 Toast.LENGTH_SHORT).show();
298 return true;
299 }
300 });
301 }
302
303 /**
304 * records a click on a mini FAB and thus:
305 * <ol>
306 * <li>persists the click fact</li>
307 * <li>removes the mini FAB labels</li>
308 * </ol>
309 */
310 private void recordMiniFabClick() {
311 // only record if it hasn't been done already at some other time
312 if(!miniFabClicked) {
313 final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
314 sp.edit().putLong(KEY_FAB_EVER_CLICKED, 1).commit();
315 miniFabClicked = true;
316 }
317 }
318
319 /**
320 * removes the labels on all known min FABs.
321 */
322 private void removeFabLabels() {
323 getFabUpload().setTitle(null);
324 getFabMkdir().setTitle(null);
325 getFabUploadFromApp().setTitle(null);
326 ((TextView) getFabUpload().getTag(com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
327 ((TextView) getFabMkdir().getTag(com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
328 ((TextView) getFabUploadFromApp().getTag(com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
329 }
330
331 private void registerLongClickListener() {
332 getListView().setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
333 private Menu menu;
334
335 @Override
336 public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
337 final int checkedCount = getListView().getCheckedItemCount();
338 // TODO Tobi extract to values
339 mode.setTitle(checkedCount + " selected");
340
341 if (checked) {
342 mAdapter.setNewSelection(position, checked);
343 } else {
344 mAdapter.removeSelection(position);
345 }
346
347 // TODO maybe change: only recreate menu if count changes
348 menu.clear();
349 if (checkedCount == 1) {
350 createContextMenu(menu);
351 } else {
352 // download, move, copy, delete
353 getActivity().getMenuInflater().inflate(R.menu.multiple_file_actions_menu, menu);
354 }
355
356 }
357
358 @Override
359 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
360 this.menu = menu;
361 return true;
362 }
363
364 @Override
365 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
366 return false;
367 }
368
369 @Override
370 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
371 return onFileActionChosen(item.getItemId());
372 }
373
374 @Override
375 public void onDestroyActionMode(ActionMode mode) {
376 mAdapter.removeSelection();
377 }
378 });
379 }
380
381
382 private void showFileAction(int fileIndex) {
383 Bundle args = getArguments();
384 PopupMenu pm = new PopupMenu(getActivity(),null);
385 Menu menu = pm.getMenu();
386
387 boolean allowContextualActions =
388 (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
389
390 if (allowContextualActions) {
391 MenuInflater inflater = getActivity().getMenuInflater();
392
393 inflater.inflate(R.menu.file_actions_menu, menu);
394 OCFile targetFile = (OCFile) mAdapter.getItem(fileIndex);
395
396 if (mContainerActivity.getStorageManager() != null) {
397 FileMenuFilter mf = new FileMenuFilter(
398 targetFile,
399 mContainerActivity.getStorageManager().getAccount(),
400 mContainerActivity,
401 getActivity()
402 );
403 mf.filter(menu);
404 }
405
406 /// TODO break this direct dependency on FileDisplayActivity... if possible
407 MenuItem item = menu.findItem(R.id.action_open_file_with);
408 FileFragment frag = ((FileDisplayActivity)getActivity()).getSecondFragment();
409 if (frag != null && frag instanceof FileDetailFragment &&
410 frag.getFile().getFileId() == targetFile.getFileId()) {
411 item = menu.findItem(R.id.action_see_details);
412 if (item != null) {
413 item.setVisible(false);
414 item.setEnabled(false);
415 }
416 }
417
418 FileActionsDialogFragment dialog = FileActionsDialogFragment.newInstance(menu,
419 fileIndex, targetFile.getFileName());
420 dialog.setTargetFragment(this, 0);
421 dialog.show(getFragmentManager(), FileActionsDialogFragment.FTAG_FILE_ACTIONS);
422 }
423 }
424
425 /**
426 * Saves the current listed folder.
427 */
428 @Override
429 public void onSaveInstanceState(Bundle outState) {
430 super.onSaveInstanceState(outState);
431 outState.putParcelable(KEY_FILE, mFile);
432 }
433
434 /**
435 * Call this, when the user presses the up button.
436 * <p>
437 * Tries to move up the current folder one level. If the parent folder was removed from the
438 * database, it continues browsing up until finding an existing folders.
439 * </p>
440 * @return Count of folder levels browsed up.
441 */
442 public int onBrowseUp() {
443 OCFile parentDir = null;
444 int moveCount = 0;
445
446 if (mFile != null) {
447 FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
448
449 String parentPath = null;
450 if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
451 parentPath = new File(mFile.getRemotePath()).getParent();
452 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
453 parentPath + OCFile.PATH_SEPARATOR;
454 parentDir = storageManager.getFileByPath(parentPath);
455 moveCount++;
456 } else {
457 parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);
458 }
459 while (parentDir == null) {
460 parentPath = new File(parentPath).getParent();
461 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
462 parentPath + OCFile.PATH_SEPARATOR;
463 parentDir = storageManager.getFileByPath(parentPath);
464 moveCount++;
465 } // exit is granted because storageManager.getFileByPath("/") never returns null
466 mFile = parentDir;
467
468 listDirectory(mFile, MainApp.getOnlyOnDevice());
469
470 onRefresh(false);
471
472 // restore index and top position
473 restoreIndexAndTopPosition();
474
475 } // else - should never happen now
476
477 return moveCount;
478 }
479
480 @Override
481 public void onItemClick(AdapterView<?> l, View v, int position, long id) {
482 OCFile file = (OCFile) mAdapter.getItem(position);
483 if (file != null) {
484 if (file.isFolder()) {
485 // update state and view of this fragment
486 listDirectory(file, MainApp.getOnlyOnDevice());
487 // then, notify parent activity to let it update its state and view
488 mContainerActivity.onBrowsedDownTo(file);
489 // save index and top position
490 saveIndexAndTopPosition(position);
491
492 } else { /// Click on a file
493 if (PreviewImageFragment.canBePreviewed(file)) {
494 // preview image - it handles the download, if needed
495 ((FileDisplayActivity)mContainerActivity).startImagePreview(file);
496 } else if (PreviewTextFragment.canBePreviewed(file)){
497 ((FileDisplayActivity)mContainerActivity).startTextPreview(file);
498 } else if (PreviewMediaFragment.canBePreviewed(file)) {
499 // media preview
500 ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true);
501 } else if (file.isDown()) {
502 mContainerActivity.getFileOperationsHelper().openFile(file);
503 } else {
504 // automatic download, preview on finish
505 ((FileDisplayActivity) mContainerActivity).startDownloadForPreview(file);
506 }
507 }
508 } else {
509 Log_OC.d(TAG, "Null object in ListAdapter!!");
510 }
511 }
512
513 /**
514 * {@inheritDoc}
515 */
516 // TODO Tobi needed?
517 public void createContextMenu(Menu menu) {
518 Bundle args = getArguments();
519 boolean allowContextualActions =
520 (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
521 if (allowContextualActions) {
522 MenuInflater inflater = getActivity().getMenuInflater();
523 inflater.inflate(R.menu.file_actions_menu, menu);
524 OCFile targetFile = null;
525 if (mAdapter.getCheckedItems().size() == 1){
526 targetFile = mAdapter.getCheckedItems().get(0);
527 }
528
529 if (mContainerActivity.getStorageManager() != null) {
530 FileMenuFilter mf = new FileMenuFilter(
531 targetFile,
532 mContainerActivity.getStorageManager().getAccount(),
533 mContainerActivity,
534 getActivity()
535 );
536 mf.filter(menu);
537 }
538
539 /// TODO break this direct dependency on FileDisplayActivity... if possible
540 MenuItem item = menu.findItem(R.id.action_open_file_with);
541 FileFragment frag = ((FileDisplayActivity)getActivity()).getSecondFragment();
542 if (frag != null && frag instanceof FileDetailFragment &&
543 frag.getFile().getFileId() == targetFile.getFileId()) {
544 item = menu.findItem(R.id.action_see_details);
545 if (item != null) {
546 item.setVisible(false);
547 item.setEnabled(false);
548 }
549 }
550
551 // String.format(mContext.getString(R.string.subject_token),
552 // getClient().getCredentials().getUsername(), file.getFileName()));
553 }
554 }
555
556 public boolean onFileActionChosen(int menuId) {
557 if (mAdapter.getCheckedItems().size() == 1){
558 OCFile mTargetFile = mAdapter.getCheckedItems().get(0);
559
560 switch (menuId) {
561 case R.id.action_share_file: {
562 mContainerActivity.getFileOperationsHelper().shareFileWithLink(mTargetFile);
563 return true;
564 }
565 case R.id.action_open_file_with: {
566 mContainerActivity.getFileOperationsHelper().openFile(mTargetFile);
567 return true;
568 }
569 case R.id.action_unshare_file: {
570 mContainerActivity.getFileOperationsHelper().unshareFileWithLink(mTargetFile);
571 return true;
572 }
573 case R.id.action_rename_file: {
574 RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(mTargetFile);
575 dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE);
576 return true;
577 }
578 case R.id.action_remove_file: {
579 RemoveFileDialogFragment dialog = RemoveFileDialogFragment.newInstance(mTargetFile);
580 dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
581 return true;
582 }
583 case R.id.action_download_file:
584 case R.id.action_sync_file: {
585 mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile);
586 return true;
587 }
588 case R.id.action_cancel_sync: {
589 ((FileDisplayActivity) mContainerActivity).cancelTransference(mTargetFile);
590 return true;
591 }
592 case R.id.action_see_details: {
593 mContainerActivity.showDetails(mTargetFile);
594 return true;
595 }
596 case R.id.action_send_file: {
597 // Obtain the file
598 if (!mTargetFile.isDown()) { // Download the file
599 Log_OC.d(TAG, mTargetFile.getRemotePath() + " : File must be downloaded");
600 ((FileDisplayActivity) mContainerActivity).startDownloadForSending(mTargetFile);
601 return true;
602 } else {
603 mContainerActivity.getFileOperationsHelper().sendDownloadedFile(mTargetFile);
604 }
605 }
606 case R.id.action_stream_file: {
607 Account account = ((FileActivity)mContainerActivity).getAccount();
608 Context context = MainApp.getAppContext();
609 String uri = PreviewMediaFragment.generateUrlWithCredentials(account, context, mTargetFile);
610 MediaService.streamWithExternalApp(uri, getActivity()).show();
611 return true;
612 }
613 case R.id.action_move: {
614 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
615 ArrayList files = new ArrayList();
616 files.add(mTargetFile);
617 action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, files);
618 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
619 return true;
620 }
621 case R.id.action_favorite_file: {
622 mContainerActivity.getFileOperationsHelper().toggleFavorite(mTargetFile, true);
623 return true;
624 }
625 case R.id.action_unfavorite_file: {
626 mContainerActivity.getFileOperationsHelper().toggleFavorite(mTargetFile, false);
627 return true;
628 }
629 case R.id.action_copy:
630 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
631
632 // Pass mTargetFile that contains info of selected file/folder
633 action.putExtra(FolderPickerActivity.EXTRA_FILE, mTargetFile);
634 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_COPY_FILES);
635 return true;
636 default:
637 return false;
638 }
639 } else {
640 ArrayList<OCFile> mTargetFiles = mAdapter.getCheckedItems();
641
642 switch (menuId) {
643 case R.id.action_remove_file: {
644 RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(mTargetFiles);
645 dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
646 return true;
647 }
648 case R.id.action_download_file:
649 case R.id.action_sync_file: {
650 mContainerActivity.getFileOperationsHelper().syncFiles(mTargetFiles);
651 return true;
652 }
653 case R.id.action_move: {
654 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
655 action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, mTargetFiles);
656 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
657 return true;
658 }
659 case R.id.action_favorite_file: {
660 mContainerActivity.getFileOperationsHelper().toggleFavorites(mTargetFiles, true);
661 return true;
662 }
663 case R.id.action_unfavorite_file: {
664 mContainerActivity.getFileOperationsHelper().toggleFavorites(mTargetFiles, false);
665 return true;
666 }
667 case R.id.action_copy:
668 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
669 action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, mTargetFiles);
670 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_COPY_FILES);
671 return true;
672 default:
673 return false;
674 }
675 }
676 }
677
678 /**
679 * {@inhericDoc}
680 */
681 @Override
682 public boolean onContextItemSelected (MenuItem item) {
683 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
684 boolean matched = onFileActionChosen(item.getItemId()) ;
685 if(!matched) {
686 return super.onContextItemSelected(item);
687 } else {
688 return matched;
689 }
690 }
691
692
693 /**
694 * Use this to query the {@link OCFile} that is currently
695 * being displayed by this fragment
696 *
697 * @return The currently viewed OCFile
698 */
699 public OCFile getCurrentFile() {
700 return mFile;
701 }
702
703 /**
704 * Calls {@link OCFileListFragment#listDirectory(OCFile, boolean)} with a null parameter
705 */
706 public void listDirectory(boolean onlyOnDevice){
707 listDirectory(null, onlyOnDevice);
708 }
709
710 public void refreshDirectory(){
711 listDirectory(getCurrentFile(), MainApp.getOnlyOnDevice());
712 }
713
714 /**
715 * Lists the given directory on the view. When the input parameter is null,
716 * it will either refresh the last known directory. list the root
717 * if there never was a directory.
718 *
719 * @param directory File to be listed
720 */
721 public void listDirectory(OCFile directory, boolean onlyOnDevice) {
722 FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
723 if (storageManager != null) {
724
725 // Check input parameters for null
726 if (directory == null) {
727 if (mFile != null) {
728 directory = mFile;
729 } else {
730 directory = storageManager.getFileByPath("/");
731 if (directory == null) return; // no files, wait for sync
732 }
733 }
734
735
736 // If that's not a directory -> List its parent
737 if (!directory.isFolder()) {
738 Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
739 directory = storageManager.getFileById(directory.getParentId());
740 }
741
742 mAdapter.swapDirectory(directory, storageManager, onlyOnDevice);
743 if (mFile == null || !mFile.equals(directory)) {
744 mCurrentListView.setSelection(0);
745 }
746 mFile = directory;
747
748 updateLayout();
749
750 }
751 }
752
753 private void updateLayout() {
754 if (!mJustFolders) {
755 int filesCount = 0, foldersCount = 0, imagesCount = 0;
756 int count = mAdapter.getCount();
757 OCFile file;
758 for (int i=0; i < count ; i++) {
759 file = (OCFile) mAdapter.getItem(i);
760 if (file.isFolder()) {
761 foldersCount++;
762 } else {
763 if (!file.isHidden()) {
764 filesCount++;
765
766 if (file.isImage() || file.isVideo()) {
767 imagesCount++;
768 }
769 }
770 }
771 }
772 // set footer text
773 setFooterText(generateFooterText(filesCount, foldersCount));
774
775 // decide grid vs list view
776 OwnCloudVersion version = AccountUtils.getServerVersion(
777 ((FileActivity)mContainerActivity).getAccount());
778 if (version != null && version.supportsRemoteThumbnails() &&
779 DisplayUtils.isGridView(mFile, mContainerActivity.getStorageManager())) {
780 switchToGridView();
781 registerLongClickListener();
782 } else {
783 switchToListView();
784 // switchToGridView();
785 }
786 }
787 }
788
789 private String generateFooterText(int filesCount, int foldersCount) {
790 String output;
791 if (filesCount <= 0) {
792 if (foldersCount <= 0) {
793 output = "";
794
795 } else if (foldersCount == 1) {
796 output = getResources().getString(R.string.file_list__footer__folder);
797
798 } else { // foldersCount > 1
799 output = getResources().getString(R.string.file_list__footer__folders, foldersCount);
800 }
801
802 } else if (filesCount == 1) {
803 if (foldersCount <= 0) {
804 output = getResources().getString(R.string.file_list__footer__file);
805
806 } else if (foldersCount == 1) {
807 output = getResources().getString(R.string.file_list__footer__file_and_folder);
808
809 } else { // foldersCount > 1
810 output = getResources().getString(R.string.file_list__footer__file_and_folders, foldersCount);
811 }
812 } else { // filesCount > 1
813 if (foldersCount <= 0) {
814 output = getResources().getString(R.string.file_list__footer__files, filesCount);
815
816 } else if (foldersCount == 1) {
817 output = getResources().getString(R.string.file_list__footer__files_and_folder, filesCount);
818
819 } else { // foldersCount > 1
820 output = getResources().getString(
821 R.string.file_list__footer__files_and_folders, filesCount, foldersCount
822 );
823
824 }
825 }
826 return output;
827 }
828
829 public void sortByName(boolean descending) {
830 mAdapter.setSortOrder(FileStorageUtils.SORT_NAME, descending);
831 }
832
833 public void sortByDate(boolean descending) {
834 mAdapter.setSortOrder(FileStorageUtils.SORT_DATE, descending);
835 }
836
837 public void sortBySize(boolean descending) {
838 mAdapter.setSortOrder(FileStorageUtils.SORT_SIZE, descending);
839 }
840 }