initial implementaion for click events + using the import icon for upload from other...
[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.os.Build;
28 import android.os.Bundle;
29 import android.support.v4.widget.SwipeRefreshLayout;
30 import android.view.ContextMenu;
31 import android.view.Menu;
32 import android.view.MenuInflater;
33 import android.view.MenuItem;
34 import android.view.View;
35 import android.widget.AdapterView;
36 import android.widget.AdapterView.AdapterContextMenuInfo;
37 import android.widget.PopupMenu;
38 import android.widget.Toast;
39
40 import com.owncloud.android.R;
41 import com.owncloud.android.authentication.AccountUtils;
42 import com.owncloud.android.datamodel.FileDataStorageManager;
43 import com.owncloud.android.datamodel.OCFile;
44 import com.owncloud.android.files.FileMenuFilter;
45 import com.owncloud.android.lib.common.utils.Log_OC;
46 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
47 import com.owncloud.android.ui.activity.FileActivity;
48 import com.owncloud.android.ui.activity.FileDisplayActivity;
49 import com.owncloud.android.ui.activity.FolderPickerActivity;
50 import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
51 import com.owncloud.android.ui.activity.UploadFilesActivity;
52 import com.owncloud.android.ui.adapter.FileListListAdapter;
53 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
54 import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
55 import com.owncloud.android.ui.dialog.FileActionsDialogFragment;
56 import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
57 import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
58 import com.owncloud.android.ui.dialog.UploadSourceDialogFragment;
59 import com.owncloud.android.ui.preview.PreviewImageFragment;
60 import com.owncloud.android.ui.preview.PreviewMediaFragment;
61 import com.owncloud.android.utils.FileStorageUtils;
62 import com.owncloud.android.ui.preview.PreviewTextFragment;
63
64 import java.io.File;
65
66 /**
67 * A Fragment that lists all files and folders in a given path.
68 *
69 * TODO refactor to get rid of direct dependency on FileDisplayActivity
70 */
71 public class OCFileListFragment extends ExtendedListFragment implements FileActionsDialogFragment.FileActionsDialogFragmentListener {
72
73 private static final String TAG = OCFileListFragment.class.getSimpleName();
74
75 private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ?
76 OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
77
78 public final static String ARG_JUST_FOLDERS = MY_PACKAGE + ".JUST_FOLDERS";
79 public final static String ARG_ALLOW_CONTEXTUAL_ACTIONS = MY_PACKAGE + ".ALLOW_CONTEXTUAL";
80
81 private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
82
83 private static String DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER";
84
85 private FileFragment.ContainerActivity mContainerActivity;
86
87 private OCFile mFile = null;
88 private FileListListAdapter mAdapter;
89 private boolean mJustFolders;
90
91 private OCFile mTargetFile;
92
93
94
95 /**
96 * {@inheritDoc}
97 */
98 @Override
99 public void onAttach(Activity activity) {
100 super.onAttach(activity);
101 Log_OC.e(TAG, "onAttach");
102 try {
103 mContainerActivity = (FileFragment.ContainerActivity) activity;
104
105 } catch (ClassCastException e) {
106 throw new ClassCastException(activity.toString() + " must implement " +
107 FileFragment.ContainerActivity.class.getSimpleName());
108 }
109 try {
110 setOnRefreshListener((OnEnforceableRefreshListener) activity);
111
112 } catch (ClassCastException e) {
113 throw new ClassCastException(activity.toString() + " must implement " +
114 SwipeRefreshLayout.OnRefreshListener.class.getSimpleName());
115 }
116 }
117
118
119 @Override
120 public void onDetach() {
121 setOnRefreshListener(null);
122 mContainerActivity = null;
123 super.onDetach();
124 }
125
126 /**
127 * {@inheritDoc}
128 */
129 @Override
130 public void onActivityCreated(Bundle savedInstanceState) {
131 super.onActivityCreated(savedInstanceState);
132 Log_OC.e(TAG, "onActivityCreated() start");
133
134 if (savedInstanceState != null) {
135 mFile = savedInstanceState.getParcelable(KEY_FILE);
136 }
137
138 if (mJustFolders) {
139 setFooterEnabled(false);
140 } else {
141 setFooterEnabled(true);
142 }
143
144 Bundle args = getArguments();
145 mJustFolders = (args == null) ? false : args.getBoolean(ARG_JUST_FOLDERS, false);
146 mAdapter = new FileListListAdapter(
147 mJustFolders,
148 getActivity(),
149 mContainerActivity
150 );
151 setListAdapter(mAdapter);
152
153 registerLongClickListener();
154 registerFabListeners();
155 }
156
157 private void registerFabListeners() {
158 registerFabUploadListeners();
159 registerFabMkDirListeners();
160 registerFabUploadFromAppListeners();
161 }
162
163 private void registerFabUploadListeners() {
164 getFabUpload().setOnClickListener(new View.OnClickListener() {
165 @Override
166 public void onClick(View v) {
167 Intent action = new Intent(getActivity(), UploadFilesActivity.class);
168 action.putExtra(
169 UploadFilesActivity.EXTRA_ACCOUNT,
170 ((FileActivity) getActivity()).getAccount()
171 );
172 getActivity().startActivityForResult(action, UploadSourceDialogFragment.ACTION_SELECT_MULTIPLE_FILES);
173 getFabMain().collapse();
174 }
175 });
176
177 getFabUpload().setOnLongClickListener(new View.OnLongClickListener() {
178 @Override
179 public boolean onLongClick(View v) {
180 Toast.makeText(getActivity(), R.string.actionbar_upload, Toast.LENGTH_SHORT).show();
181 return true;
182 }
183 });
184 }
185
186 private void registerFabMkDirListeners() {
187 getFabMkdir().setOnClickListener(new View.OnClickListener() {
188 @Override
189 public void onClick(View v) {
190 CreateFolderDialogFragment dialog =
191 CreateFolderDialogFragment.newInstance(mFile);
192 dialog.show(getActivity().getSupportFragmentManager(), FileDisplayActivity.DIALOG_CREATE_FOLDER);
193 getFabMain().collapse();
194 }
195 });
196
197 getFabMkdir().setOnLongClickListener(new View.OnLongClickListener() {
198 @Override
199 public boolean onLongClick(View v) {
200 Toast.makeText(getActivity(), R.string.actionbar_mkdir, Toast.LENGTH_SHORT).show();
201 return true;
202 }
203 });
204 }
205
206 private void registerFabUploadFromAppListeners() {
207 getFabUploadFromApp().setOnClickListener(new View.OnClickListener() {
208 @Override
209 public void onClick(View v) {
210 Intent action = new Intent(Intent.ACTION_GET_CONTENT);
211 action = action.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE);
212
213 //Intent.EXTRA_ALLOW_MULTIPLE is only supported on api level 18+, Jelly Bean
214 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
215 action.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
216 }
217
218 getActivity().startActivityForResult(
219 Intent.createChooser(action, getString(R.string.upload_chooser_title)),
220 UploadSourceDialogFragment.ACTION_SELECT_CONTENT_FROM_APPS
221 );
222 getFabMain().collapse();
223 }
224 });
225
226 getFabUploadFromApp().setOnLongClickListener(new View.OnLongClickListener() {
227 @Override
228 public boolean onLongClick(View v) {
229 Toast.makeText(getActivity(),
230 R.string.actionbar_upload_from_apps,
231 Toast.LENGTH_SHORT).show();
232 return true;
233 }
234 });
235 }
236
237 private void registerLongClickListener() {
238 getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
239 public boolean onItemLongClick(AdapterView<?> arg0, View v,
240 int index, long arg3) {
241 showFileAction(index);
242 return true;
243 }
244 });
245 }
246
247
248 private void showFileAction(int fileIndex) {
249 Bundle args = getArguments();
250 PopupMenu pm = new PopupMenu(getActivity(),null);
251 Menu menu = pm.getMenu();
252
253 boolean allowContextualActions =
254 (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
255
256 if (allowContextualActions) {
257 MenuInflater inflater = getActivity().getMenuInflater();
258
259 inflater.inflate(R.menu.file_actions_menu, menu);
260 OCFile targetFile = (OCFile) mAdapter.getItem(fileIndex);
261
262 if (mContainerActivity.getStorageManager() != null) {
263 FileMenuFilter mf = new FileMenuFilter(
264 targetFile,
265 mContainerActivity.getStorageManager().getAccount(),
266 mContainerActivity,
267 getActivity()
268 );
269 mf.filter(menu);
270 }
271
272 /// TODO break this direct dependency on FileDisplayActivity... if possible
273 MenuItem item = menu.findItem(R.id.action_open_file_with);
274 FileFragment frag = ((FileDisplayActivity)getActivity()).getSecondFragment();
275 if (frag != null && frag instanceof FileDetailFragment &&
276 frag.getFile().getFileId() == targetFile.getFileId()) {
277 item = menu.findItem(R.id.action_see_details);
278 if (item != null) {
279 item.setVisible(false);
280 item.setEnabled(false);
281 }
282 }
283
284 FileActionsDialogFragment dialog = FileActionsDialogFragment.newInstance(menu, fileIndex, targetFile.getFileName());
285 dialog.setTargetFragment(this, 0);
286 dialog.show(getFragmentManager(), FileActionsDialogFragment.FTAG_FILE_ACTIONS);
287 }
288 }
289
290 /**
291 * Saves the current listed folder.
292 */
293 @Override
294 public void onSaveInstanceState(Bundle outState) {
295 super.onSaveInstanceState(outState);
296 outState.putParcelable(KEY_FILE, mFile);
297 }
298
299 /**
300 * Call this, when the user presses the up button.
301 *
302 * Tries to move up the current folder one level. If the parent folder was removed from the
303 * database, it continues browsing up until finding an existing folders.
304 * <p/>
305 * return Count of folder levels browsed up.
306 */
307 public int onBrowseUp() {
308 OCFile parentDir = null;
309 int moveCount = 0;
310
311 if (mFile != null) {
312 FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
313
314 String parentPath = null;
315 if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
316 parentPath = new File(mFile.getRemotePath()).getParent();
317 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
318 parentPath + OCFile.PATH_SEPARATOR;
319 parentDir = storageManager.getFileByPath(parentPath);
320 moveCount++;
321 } else {
322 parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);
323 }
324 while (parentDir == null) {
325 parentPath = new File(parentPath).getParent();
326 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
327 parentPath + OCFile.PATH_SEPARATOR;
328 parentDir = storageManager.getFileByPath(parentPath);
329 moveCount++;
330 } // exit is granted because storageManager.getFileByPath("/") never returns null
331 mFile = parentDir;
332
333 // TODO Enable when "On Device" is recovered ?
334 listDirectory(mFile /*, MainApp.getOnlyOnDevice()*/);
335
336 onRefresh(false);
337
338 // restore index and top position
339 restoreIndexAndTopPosition();
340
341 } // else - should never happen now
342
343 return moveCount;
344 }
345
346 @Override
347 public void onItemClick(AdapterView<?> l, View v, int position, long id) {
348 OCFile file = (OCFile) mAdapter.getItem(position);
349 if (file != null) {
350 if (file.isFolder()) {
351 // update state and view of this fragment
352 // TODO Enable when "On Device" is recovered ?
353 listDirectory(file/*, MainApp.getOnlyOnDevice()*/);
354 // then, notify parent activity to let it update its state and view
355 mContainerActivity.onBrowsedDownTo(file);
356 // save index and top position
357 saveIndexAndTopPosition(position);
358
359 } else { /// Click on a file
360 if (PreviewImageFragment.canBePreviewed(file)) {
361 // preview image - it handles the download, if needed
362 ((FileDisplayActivity)mContainerActivity).startImagePreview(file);
363 } else if (PreviewTextFragment.canBePreviewed(file)){
364 ((FileDisplayActivity)mContainerActivity).startTextPreview(file);
365 } else if (file.isDown()) {
366 if (PreviewMediaFragment.canBePreviewed(file)) {
367 // media preview
368 ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true);
369 } else {
370 mContainerActivity.getFileOperationsHelper().openFile(file);
371 }
372
373 } else {
374 // automatic download, preview on finish
375 ((FileDisplayActivity) mContainerActivity).startDownloadForPreview(file);
376 }
377
378 }
379
380 } else {
381 Log_OC.d(TAG, "Null object in ListAdapter!!");
382 }
383
384 }
385
386 /**
387 * {@inheritDoc}
388 */
389 @Override
390 public void onCreateContextMenu(
391 ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
392 Bundle args = getArguments();
393 boolean allowContextualActions =
394 (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
395 if (allowContextualActions) {
396 MenuInflater inflater = getActivity().getMenuInflater();
397 inflater.inflate(R.menu.file_actions_menu, menu);
398 AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
399 OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
400
401 if (mContainerActivity.getStorageManager() != null) {
402 FileMenuFilter mf = new FileMenuFilter(
403 targetFile,
404 mContainerActivity.getStorageManager().getAccount(),
405 mContainerActivity,
406 getActivity()
407 );
408 mf.filter(menu);
409 }
410
411 /// TODO break this direct dependency on FileDisplayActivity... if possible
412 MenuItem item = menu.findItem(R.id.action_open_file_with);
413 FileFragment frag = ((FileDisplayActivity)getActivity()).getSecondFragment();
414 if (frag != null && frag instanceof FileDetailFragment &&
415 frag.getFile().getFileId() == targetFile.getFileId()) {
416 item = menu.findItem(R.id.action_see_details);
417 if (item != null) {
418 item.setVisible(false);
419 item.setEnabled(false);
420 }
421 }
422 }
423 }
424
425 /**
426 * {@inheritDoc}
427 */
428 @Override
429 public boolean onFileActionChosen(int menuId, int filePosition) {
430 mTargetFile = (OCFile) mAdapter.getItem(filePosition);
431 switch (menuId) {
432 case R.id.action_share_file: {
433 mContainerActivity.getFileOperationsHelper().shareFileWithLink(mTargetFile);
434 return true;
435 }
436 case R.id.action_open_file_with: {
437 mContainerActivity.getFileOperationsHelper().openFile(mTargetFile);
438 return true;
439 }
440 case R.id.action_unshare_file: {
441 mContainerActivity.getFileOperationsHelper().unshareFileWithLink(mTargetFile);
442 return true;
443 }
444 case R.id.action_rename_file: {
445 RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(mTargetFile);
446 dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE);
447 return true;
448 }
449 case R.id.action_remove_file: {
450 RemoveFileDialogFragment dialog = RemoveFileDialogFragment.newInstance(mTargetFile);
451 dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
452 return true;
453 }
454 case R.id.action_download_file:
455 case R.id.action_sync_file: {
456 mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile);
457 return true;
458 }
459 case R.id.action_cancel_download:
460 case R.id.action_cancel_upload: {
461 ((FileDisplayActivity) mContainerActivity).cancelTransference(mTargetFile);
462 return true;
463 }
464 case R.id.action_see_details: {
465 mContainerActivity.showDetails(mTargetFile);
466 return true;
467 }
468 case R.id.action_send_file: {
469 // Obtain the file
470 if (!mTargetFile.isDown()) { // Download the file
471 Log_OC.d(TAG, mTargetFile.getRemotePath() + " : File must be downloaded");
472 ((FileDisplayActivity) mContainerActivity).startDownloadForSending(mTargetFile);
473
474 } else {
475 mContainerActivity.getFileOperationsHelper().sendDownloadedFile(mTargetFile);
476 }
477 return true;
478 }
479 case R.id.action_move: {
480 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
481
482 // Pass mTargetFile that contains info of selected file/folder
483 action.putExtra(FolderPickerActivity.EXTRA_FILE, mTargetFile);
484 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
485 return true;
486 }
487 case R.id.action_favorite_file: {
488 mContainerActivity.getFileOperationsHelper().toggleFavorite(mTargetFile, true);
489 return true;
490 }
491 case R.id.action_unfavorite_file: {
492 mContainerActivity.getFileOperationsHelper().toggleFavorite(mTargetFile, false);
493 return true;
494 }
495 case R.id.action_copy:
496 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
497
498 // Pass mTargetFile that contains info of selected file/folder
499 action.putExtra(FolderPickerActivity.EXTRA_FILE, mTargetFile);
500 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_COPY_FILES);
501 return true;
502 default:
503 return false;
504 }
505 }
506
507 /**
508 * {@inhericDoc}
509 */
510 @Override
511 public boolean onContextItemSelected (MenuItem item) {
512 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
513 boolean matched = onFileActionChosen(item.getItemId(), ((AdapterContextMenuInfo) item.getMenuInfo()).position);
514 if(!matched) {
515 return super.onContextItemSelected(item);
516 } else {
517 return matched;
518 }
519 }
520
521
522 /**
523 * Use this to query the {@link OCFile} that is currently
524 * being displayed by this fragment
525 *
526 * @return The currently viewed OCFile
527 */
528 public OCFile getCurrentFile() {
529 return mFile;
530 }
531
532 /**
533 * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
534 */
535 public void listDirectory(/*boolean onlyOnDevice*/){
536 listDirectory(null);
537 // TODO Enable when "On Device" is recovered ?
538 // listDirectory(null, onlyOnDevice);
539 }
540
541 public void refreshDirectory(){
542 // TODO Enable when "On Device" is recovered ?
543 listDirectory(getCurrentFile()/*, MainApp.getOnlyOnDevice()*/);
544 }
545
546 /**
547 * Lists the given directory on the view. When the input parameter is null,
548 * it will either refresh the last known directory. list the root
549 * if there never was a directory.
550 *
551 * @param directory File to be listed
552 */
553 public void listDirectory(OCFile directory/*, boolean onlyOnDevice*/) {
554 FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
555 if (storageManager != null) {
556
557 // Check input parameters for null
558 if (directory == null) {
559 if (mFile != null) {
560 directory = mFile;
561 } else {
562 directory = storageManager.getFileByPath("/");
563 if (directory == null) return; // no files, wait for sync
564 }
565 }
566
567
568 // If that's not a directory -> List its parent
569 if (!directory.isFolder()) {
570 Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
571 directory = storageManager.getFileById(directory.getParentId());
572 }
573
574 // TODO Enable when "On Device" is recovered ?
575 mAdapter.swapDirectory(directory, storageManager/*, onlyOnDevice*/);
576 if (mFile == null || !mFile.equals(directory)) {
577 mCurrentListView.setSelection(0);
578 }
579 mFile = directory;
580
581 updateLayout();
582
583 }
584 }
585
586 private void updateLayout() {
587 if (!mJustFolders) {
588 int filesCount = 0, foldersCount = 0, imagesCount = 0;
589 int count = mAdapter.getCount();
590 OCFile file;
591 for (int i=0; i < count ; i++) {
592 file = (OCFile) mAdapter.getItem(i);
593 if (file.isFolder()) {
594 foldersCount++;
595 } else {
596 if (!file.isHidden()) {
597 filesCount++;
598
599 if (file.isImage()) {
600 imagesCount++;
601 }
602 }
603 }
604 }
605 // set footer text
606 setFooterText(generateFooterText(filesCount, foldersCount));
607
608 // decide grid vs list view
609 OwnCloudVersion version = AccountUtils.getServerVersion(
610 ((FileActivity)mContainerActivity).getAccount());
611 if (version != null && version.supportsRemoteThumbnails() &&
612 imagesCount > 0 && imagesCount == filesCount) {
613 switchToGridView();
614 registerLongClickListener();
615 } else {
616 switchToListView();
617 }
618 }
619 }
620
621 private String generateFooterText(int filesCount, int foldersCount) {
622 String output;
623 if (filesCount <= 0) {
624 if (foldersCount <= 0) {
625 output = "";
626
627 } else if (foldersCount == 1) {
628 output = getResources().getString(R.string.file_list__footer__folder);
629
630 } else { // foldersCount > 1
631 output = getResources().getString(R.string.file_list__footer__folders, foldersCount);
632 }
633
634 } else if (filesCount == 1) {
635 if (foldersCount <= 0) {
636 output = getResources().getString(R.string.file_list__footer__file);
637
638 } else if (foldersCount == 1) {
639 output = getResources().getString(R.string.file_list__footer__file_and_folder);
640
641 } else { // foldersCount > 1
642 output = getResources().getString(R.string.file_list__footer__file_and_folders, foldersCount);
643 }
644 } else { // filesCount > 1
645 if (foldersCount <= 0) {
646 output = getResources().getString(R.string.file_list__footer__files, filesCount);
647
648 } else if (foldersCount == 1) {
649 output = getResources().getString(R.string.file_list__footer__files_and_folder, filesCount);
650
651 } else { // foldersCount > 1
652 output = getResources().getString(
653 R.string.file_list__footer__files_and_folders, filesCount, foldersCount
654 );
655
656 }
657 }
658 return output;
659 }
660
661 public void sortByName(boolean descending) {
662 mAdapter.setSortOrder(FileStorageUtils.SORT_NAME, descending);
663 }
664
665 public void sortByDate(boolean descending) {
666 mAdapter.setSortOrder(FileStorageUtils.SORT_DATE, descending);
667 }
668
669 public void sortBySize(boolean descending) {
670 mAdapter.setSortOrder(FileStorageUtils.SORT_SIZE, descending);
671 }
672 }