resized image is loaded instead of full sized image.
[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 java.io.File;
26
27 import android.app.Activity;
28 import android.content.Intent;
29 import android.os.Bundle;
30 import android.support.v4.widget.SwipeRefreshLayout;
31 import android.view.ContextMenu;
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
38 import com.owncloud.android.R;
39 import com.owncloud.android.datamodel.FileDataStorageManager;
40 import com.owncloud.android.datamodel.OCFile;
41 import com.owncloud.android.files.FileMenuFilter;
42 import com.owncloud.android.lib.common.utils.Log_OC;
43 import com.owncloud.android.ui.activity.FileDisplayActivity;
44 import com.owncloud.android.ui.activity.FolderPickerActivity;
45 import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
46 import com.owncloud.android.ui.adapter.FileListListAdapter;
47 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
48 import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
49 import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
50 import com.owncloud.android.ui.preview.PreviewImageFragment;
51 import com.owncloud.android.ui.preview.PreviewMediaFragment;
52 import com.owncloud.android.utils.FileStorageUtils;
53
54 /**
55 * A Fragment that lists all files and folders in a given path.
56 *
57 * TODO refactorize to get rid of direct dependency on FileDisplayActivity
58 */
59 public class OCFileListFragment extends ExtendedListFragment {
60
61 private static final String TAG = OCFileListFragment.class.getSimpleName();
62
63 private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ?
64 OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
65
66 public final static String ARG_JUST_FOLDERS = MY_PACKAGE + ".JUST_FOLDERS";
67 public final static String ARG_ALLOW_CONTEXTUAL_ACTIONS = MY_PACKAGE + ".ALLOW_CONTEXTUAL";
68
69 private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
70
71 private final static Double THUMBNAIL_THRESHOLD = 0.5;
72
73 private FileFragment.ContainerActivity mContainerActivity;
74
75 private OCFile mFile = null;
76 private FileListListAdapter mAdapter;
77 private boolean mJustFolders;
78
79 private OCFile mTargetFile;
80
81
82 /**
83 * {@inheritDoc}
84 */
85 @Override
86 public void onAttach(Activity activity) {
87 super.onAttach(activity);
88 Log_OC.e(TAG, "onAttach");
89 try {
90 mContainerActivity = (FileFragment.ContainerActivity) activity;
91
92 } catch (ClassCastException e) {
93 throw new ClassCastException(activity.toString() + " must implement " +
94 FileFragment.ContainerActivity.class.getSimpleName());
95 }
96 try {
97 setOnRefreshListener((OnEnforceableRefreshListener) activity);
98
99 } catch (ClassCastException e) {
100 throw new ClassCastException(activity.toString() + " must implement " +
101 SwipeRefreshLayout.OnRefreshListener.class.getSimpleName());
102 }
103 }
104
105
106 @Override
107 public void onDetach() {
108 setOnRefreshListener(null);
109 mContainerActivity = null;
110 super.onDetach();
111 }
112
113 /**
114 * {@inheritDoc}
115 */
116 @Override
117 public void onActivityCreated(Bundle savedInstanceState) {
118 super.onActivityCreated(savedInstanceState);
119 Log_OC.e(TAG, "onActivityCreated() start");
120
121 if (savedInstanceState != null) {
122 mFile = savedInstanceState.getParcelable(KEY_FILE);
123 }
124
125 if (mJustFolders) {
126 setFooterEnabled(false);
127 } else {
128 setFooterEnabled(true);
129 }
130
131 Bundle args = getArguments();
132 mJustFolders = (args == null) ? false : args.getBoolean(ARG_JUST_FOLDERS, false);
133 mAdapter = new FileListListAdapter(
134 mJustFolders,
135 getSherlockActivity(),
136 mContainerActivity
137 );
138 setListAdapter(mAdapter);
139
140 registerForContextMenu();
141 }
142
143 /**
144 * Saves the current listed folder.
145 */
146 @Override
147 public void onSaveInstanceState (Bundle outState) {
148 super.onSaveInstanceState(outState);
149 outState.putParcelable(KEY_FILE, mFile);
150 }
151
152 /**
153 * Call this, when the user presses the up button.
154 *
155 * Tries to move up the current folder one level. If the parent folder was removed from the
156 * database, it continues browsing up until finding an existing folders.
157 *
158 * return Count of folder levels browsed up.
159 */
160 public int onBrowseUp() {
161 OCFile parentDir = null;
162 int moveCount = 0;
163
164 if(mFile != null){
165 FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
166
167 String parentPath = null;
168 if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
169 parentPath = new File(mFile.getRemotePath()).getParent();
170 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
171 parentPath + OCFile.PATH_SEPARATOR;
172 parentDir = storageManager.getFileByPath(parentPath);
173 moveCount++;
174 } else {
175 parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);
176 }
177 while (parentDir == null) {
178 parentPath = new File(parentPath).getParent();
179 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
180 parentPath + OCFile.PATH_SEPARATOR;
181 parentDir = storageManager.getFileByPath(parentPath);
182 moveCount++;
183 } // exit is granted because storageManager.getFileByPath("/") never returns null
184 mFile = parentDir;
185
186 listDirectory(mFile);
187
188 onRefresh(false);
189
190 // restore index and top position
191 restoreIndexAndTopPosition();
192
193 } // else - should never happen now
194
195 return moveCount;
196 }
197
198 @Override
199 public void onItemClick(AdapterView<?> l, View v, int position, long id) {
200 OCFile file = (OCFile) mAdapter.getItem(position);
201 if (file != null) {
202 if (file.isFolder()) {
203 // update state and view of this fragment
204 listDirectory(file);
205 // then, notify parent activity to let it update its state and view
206 mContainerActivity.onBrowsedDownTo(file);
207 // save index and top position
208 saveIndexAndTopPosition(position);
209
210 } else { /// Click on a file
211 if (PreviewImageFragment.canBePreviewed(file)) {
212 // preview image - it handles the download, if needed
213 ((FileDisplayActivity)mContainerActivity).startImagePreview(file);
214
215 } else if (file.isDown()) {
216 if (PreviewMediaFragment.canBePreviewed(file)) {
217 // media preview
218 ((FileDisplayActivity)mContainerActivity).startMediaPreview(file, 0, true);
219 } else {
220 mContainerActivity.getFileOperationsHelper().openFile(file);
221 }
222
223 }
224 }
225
226 } else {
227 Log_OC.d(TAG, "Null object in ListAdapter!!");
228 }
229
230 }
231
232 /**
233 * {@inheritDoc}
234 */
235 @Override
236 public void onCreateContextMenu (
237 ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
238 super.onCreateContextMenu(menu, v, menuInfo);
239 Bundle args = getArguments();
240 boolean allowContextualActions =
241 (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
242 if (allowContextualActions) {
243 MenuInflater inflater = getSherlockActivity().getMenuInflater();
244 inflater.inflate(R.menu.file_actions_menu, menu);
245 AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
246 OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
247
248 if (mContainerActivity.getStorageManager() != null) {
249 FileMenuFilter mf = new FileMenuFilter(
250 targetFile,
251 mContainerActivity.getStorageManager().getAccount(),
252 mContainerActivity,
253 getSherlockActivity()
254 );
255 mf.filter(menu);
256 }
257
258 /// TODO break this direct dependency on FileDisplayActivity... if possible
259 MenuItem item = menu.findItem(R.id.action_open_file_with);
260 FileFragment frag = ((FileDisplayActivity)getSherlockActivity()).getSecondFragment();
261 if (frag != null && frag instanceof FileDetailFragment &&
262 frag.getFile().getFileId() == targetFile.getFileId()) {
263 item = menu.findItem(R.id.action_see_details);
264 if (item != null) {
265 item.setVisible(false);
266 item.setEnabled(false);
267 }
268 }
269 }
270 }
271
272
273 /**
274 * {@inhericDoc}
275 */
276 @Override
277 public boolean onContextItemSelected (MenuItem item) {
278 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
279 mTargetFile = (OCFile) mAdapter.getItem(info.position);
280 switch (item.getItemId()) {
281 case R.id.action_share_file: {
282 mContainerActivity.getFileOperationsHelper().shareFileWithLink(mTargetFile);
283 return true;
284 }
285 case R.id.action_open_file_with: {
286 mContainerActivity.getFileOperationsHelper().openFile(mTargetFile);
287 return true;
288 }
289 case R.id.action_unshare_file: {
290 mContainerActivity.getFileOperationsHelper().unshareFileWithLink(mTargetFile);
291 return true;
292 }
293 case R.id.action_rename_file: {
294 RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(mTargetFile);
295 dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE);
296 return true;
297 }
298 case R.id.action_remove_file: {
299 RemoveFileDialogFragment dialog = RemoveFileDialogFragment.newInstance(mTargetFile);
300 dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
301 return true;
302 }
303 case R.id.action_download_file:
304 case R.id.action_sync_file: {
305 mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile);
306 return true;
307 }
308 case R.id.action_cancel_download:
309 case R.id.action_cancel_upload: {
310 ((FileDisplayActivity)mContainerActivity).cancelTransference(mTargetFile);
311 return true;
312 }
313 case R.id.action_see_details: {
314 mContainerActivity.showDetails(mTargetFile);
315 return true;
316 }
317 case R.id.action_send_file: {
318 // Obtain the file
319 if (!mTargetFile.isDown()) { // Download the file
320 Log_OC.d(TAG, mTargetFile.getRemotePath() + " : File must be downloaded");
321 ((FileDisplayActivity)mContainerActivity).startDownloadForSending(mTargetFile);
322
323 } else {
324 mContainerActivity.getFileOperationsHelper().sendDownloadedFile(mTargetFile);
325 }
326 return true;
327 }
328 case R.id.action_move: {
329 Intent action = new Intent(getActivity(), FolderPickerActivity.class);
330
331 // Pass mTargetFile that contains info of selected file/folder
332 action.putExtra(FolderPickerActivity.EXTRA_FILE, mTargetFile);
333 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
334 return true;
335 }
336 default:
337 return super.onContextItemSelected(item);
338 }
339 }
340
341
342 /**
343 * Use this to query the {@link OCFile} that is currently
344 * being displayed by this fragment
345 * @return The currently viewed OCFile
346 */
347 public OCFile getCurrentFile(){
348 return mFile;
349 }
350
351 /**
352 * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
353 */
354 public void listDirectory(){
355 listDirectory(null);
356 }
357
358 /**
359 * Lists the given directory on the view. When the input parameter is null,
360 * it will either refresh the last known directory. list the root
361 * if there never was a directory.
362 *
363 * @param directory File to be listed
364 */
365 public void listDirectory(OCFile directory) {
366 FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
367 if (storageManager != null) {
368
369 // Check input parameters for null
370 if(directory == null){
371 if(mFile != null){
372 directory = mFile;
373 } else {
374 directory = storageManager.getFileByPath("/");
375 if (directory == null) return; // no files, wait for sync
376 }
377 }
378
379
380 // If that's not a directory -> List its parent
381 if(!directory.isFolder()){
382 Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
383 directory = storageManager.getFileById(directory.getParentId());
384 }
385
386 mAdapter.swapDirectory(directory, storageManager);
387 if (mFile == null || !mFile.equals(directory)) {
388 mCurrentListView.setSelection(0);
389 }
390 mFile = directory;
391
392 updateLayout();
393
394 }
395 }
396
397 private void updateLayout() {
398 if (!mJustFolders) {
399 int filesCount = 0, foldersCount = 0, imagesCount = 0;
400 int count = mAdapter.getCount();
401 OCFile file;
402 for (int i=0; i < count ; i++) {
403 file = (OCFile) mAdapter.getItem(i);
404 if (file.isFolder()) {
405 foldersCount++;
406 } else {
407 filesCount++;
408 if (file.isImage()){
409 imagesCount++;
410 }
411 }
412 }
413 // set footer text
414 setFooterText(generateFooterText(filesCount, foldersCount));
415
416 // decide grid vs list view
417 if (((double)imagesCount / (double)filesCount) >= THUMBNAIL_THRESHOLD) {
418 switchToGridView();
419 } else {
420 switchToListView();
421 }
422 }
423 }
424
425 private String generateFooterText(int filesCount, int foldersCount) {
426 String output = "";
427 if (filesCount > 0){
428 if (filesCount == 1) {
429 output = output + filesCount + " " + getResources().getString(R.string.file_list_file);
430 } else {
431 output = output + filesCount + " " + getResources().getString(R.string.file_list_files);
432 }
433 }
434 if (foldersCount > 0 && filesCount > 0){
435 output = output + ", ";
436 }
437 if (foldersCount == 1) {
438 output = output + foldersCount + " " + getResources().getString(R.string.file_list_folder);
439 } else if (foldersCount > 1) {
440 output = output + foldersCount + " " + getResources().getString(R.string.file_list_folders);
441 }
442
443 return output;
444 }
445
446
447 public void sortByName(boolean descending) {
448 mAdapter.setSortOrder(FileStorageUtils.SORT_NAME, descending);
449 }
450
451 public void sortByDate(boolean descending) {
452 mAdapter.setSortOrder(FileStorageUtils.SORT_DATE, descending);
453 }
454
455 public void sortBySize(boolean descending) {
456 mAdapter.setSortOrder(FileStorageUtils.SORT_SIZE, descending);
457 }
458
459 }