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.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().showShareFile(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_rename_file: {
570 RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(mTargetFile);
571 dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE);
572 return true;
573 }
574 case R.id.action_remove_file: {
575 RemoveFileDialogFragment dialog = RemoveFileDialogFragment.newInstance(mTargetFile);
576 dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
577 return true;
578 }
579 case R.id.action_download_file:
580 case R.id.action_sync_file: {
581 mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile);
582 return true;
583 }
584 case R.id.action_cancel_sync: {
585 ((FileDisplayActivity) mContainerActivity).cancelTransference(mTargetFile);
586 return true;
587 }
588 case R.id.action_see_details: {
589 mContainerActivity.showDetails(mTargetFile);
590 return true;
591 }
592 case R.id.action_send_file: {
593 // Obtain the file
594 if (!mTargetFile.isDown()) { // Download the file
595 Log_OC.d(TAG, mTargetFile.getRemotePath() + " : File must be downloaded");
596 ((FileDisplayActivity) mContainerActivity).startDownloadForSending(mTargetFile);
597 return true;
598 } else {
599 mContainerActivity.getFileOperationsHelper().sendDownloadedFile(mTargetFile);
600 }
601 }
602 case R.id.action_stream_file: {
603 Account account = ((FileActivity)mContainerActivity).getAccount();
604 Context context = MainApp.getAppContext();
605 String uri = PreviewMediaFragment.generateUrlWithCredentials(account, context, mTargetFile);
606 MediaService.streamWithExternalApp(uri, getActivity()).show();
607 return true;
608 }
609 case R.id.action_move: {
610 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
611 ArrayList files = new ArrayList();
612 files.add(mTargetFile);
613 action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, files);
614 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
615 return true;
616 }
617 case R.id.action_favorite_file: {
618 mContainerActivity.getFileOperationsHelper().toggleFavorite(mTargetFile, true);
619 return true;
620 }
621 case R.id.action_unfavorite_file: {
622 mContainerActivity.getFileOperationsHelper().toggleFavorite(mTargetFile, false);
623 return true;
624 }
625 case R.id.action_copy:
626 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
627 ArrayList files = new ArrayList();
628 files.add(mTargetFile);
629 action.putExtra(FolderPickerActivity.EXTRA_FILES, files);
630 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_COPY_FILES);
631 return true;
632 default:
633 return false;
634 }
635 } else {
636 ArrayList<OCFile> mTargetFiles = mAdapter.getCheckedItems();
637
638 switch (menuId) {
639 case R.id.action_remove_file: {
640 RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(mTargetFiles);
641 dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
642 return true;
643 }
644 case R.id.action_download_file:
645 case R.id.action_sync_file: {
646 mContainerActivity.getFileOperationsHelper().syncFiles(mTargetFiles);
647 return true;
648 }
649 case R.id.action_move: {
650 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
651 action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, mTargetFiles);
652 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
653 return true;
654 }
655 case R.id.action_favorite_file: {
656 mContainerActivity.getFileOperationsHelper().toggleFavorites(mTargetFiles, true);
657 return true;
658 }
659 case R.id.action_unfavorite_file: {
660 mContainerActivity.getFileOperationsHelper().toggleFavorites(mTargetFiles, false);
661 return true;
662 }
663 case R.id.action_copy:
664 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
665 action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, mTargetFiles);
666 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_COPY_FILES);
667 return true;
668 default:
669 return false;
670 }
671 }
672 }
673
674 /**
675 * {@inhericDoc}
676 */
677 @Override
678 public boolean onContextItemSelected (MenuItem item) {
679 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
680 boolean matched = onFileActionChosen(item.getItemId()) ;
681 if(!matched) {
682 return super.onContextItemSelected(item);
683 } else {
684 return matched;
685 }
686 }
687
688
689 /**
690 * Use this to query the {@link OCFile} that is currently
691 * being displayed by this fragment
692 *
693 * @return The currently viewed OCFile
694 */
695 public OCFile getCurrentFile() {
696 return mFile;
697 }
698
699 /**
700 * Calls {@link OCFileListFragment#listDirectory(OCFile, boolean)} with a null parameter
701 */
702 public void listDirectory(boolean onlyOnDevice){
703 listDirectory(null, onlyOnDevice);
704 }
705
706 public void refreshDirectory(){
707 listDirectory(getCurrentFile(), MainApp.getOnlyOnDevice());
708 }
709
710 /**
711 * Lists the given directory on the view. When the input parameter is null,
712 * it will either refresh the last known directory. list the root
713 * if there never was a directory.
714 *
715 * @param directory File to be listed
716 */
717 public void listDirectory(OCFile directory, boolean onlyOnDevice) {
718 FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
719 if (storageManager != null) {
720
721 // Check input parameters for null
722 if (directory == null) {
723 if (mFile != null) {
724 directory = mFile;
725 } else {
726 directory = storageManager.getFileByPath("/");
727 if (directory == null) return; // no files, wait for sync
728 }
729 }
730
731
732 // If that's not a directory -> List its parent
733 if (!directory.isFolder()) {
734 Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
735 directory = storageManager.getFileById(directory.getParentId());
736 }
737
738 mAdapter.swapDirectory(directory, storageManager, onlyOnDevice);
739 if (mFile == null || !mFile.equals(directory)) {
740 mCurrentListView.setSelection(0);
741 }
742 mFile = directory;
743
744 updateLayout();
745
746 }
747 }
748
749 private void updateLayout() {
750 if (!mJustFolders) {
751 int filesCount = 0, foldersCount = 0, imagesCount = 0;
752 int count = mAdapter.getCount();
753 OCFile file;
754 for (int i=0; i < count ; i++) {
755 file = (OCFile) mAdapter.getItem(i);
756 if (file.isFolder()) {
757 foldersCount++;
758 } else {
759 if (!file.isHidden()) {
760 filesCount++;
761
762 if (file.isImage() || file.isVideo()) {
763 imagesCount++;
764 }
765 }
766 }
767 }
768 // set footer text
769 setFooterText(generateFooterText(filesCount, foldersCount));
770
771 // decide grid vs list view
772 OwnCloudVersion version = AccountUtils.getServerVersion(
773 ((FileActivity)mContainerActivity).getAccount());
774 if (version != null && version.supportsRemoteThumbnails() &&
775 DisplayUtils.isGridView(mFile, mContainerActivity.getStorageManager())) {
776 switchToGridView();
777 registerLongClickListener();
778 } else {
779 switchToListView();
780 // switchToGridView();
781 }
782 }
783 }
784
785 private String generateFooterText(int filesCount, int foldersCount) {
786 String output;
787 if (filesCount <= 0) {
788 if (foldersCount <= 0) {
789 output = "";
790
791 } else if (foldersCount == 1) {
792 output = getResources().getString(R.string.file_list__footer__folder);
793
794 } else { // foldersCount > 1
795 output = getResources().getString(R.string.file_list__footer__folders, foldersCount);
796 }
797
798 } else if (filesCount == 1) {
799 if (foldersCount <= 0) {
800 output = getResources().getString(R.string.file_list__footer__file);
801
802 } else if (foldersCount == 1) {
803 output = getResources().getString(R.string.file_list__footer__file_and_folder);
804
805 } else { // foldersCount > 1
806 output = getResources().getString(R.string.file_list__footer__file_and_folders, foldersCount);
807 }
808 } else { // filesCount > 1
809 if (foldersCount <= 0) {
810 output = getResources().getString(R.string.file_list__footer__files, filesCount);
811
812 } else if (foldersCount == 1) {
813 output = getResources().getString(R.string.file_list__footer__files_and_folder, filesCount);
814
815 } else { // foldersCount > 1
816 output = getResources().getString(
817 R.string.file_list__footer__files_and_folders, filesCount, foldersCount
818 );
819
820 }
821 }
822 return output;
823 }
824
825 public void sortByName(boolean descending) {
826 mAdapter.setSortOrder(FileStorageUtils.SORT_NAME, descending);
827 }
828
829 public void sortByDate(boolean descending) {
830 mAdapter.setSortOrder(FileStorageUtils.SORT_DATE, descending);
831 }
832
833 public void sortBySize(boolean descending) {
834 mAdapter.setSortOrder(FileStorageUtils.SORT_SIZE, descending);
835 }
836 }