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