19829fa4fa3e42cd45ffcd6d62b1fa83263e6631
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / fragment / OCFileListFragment.java
1 /* ownCloud Android client application
2 * Copyright (C) 2011 Bartek Przybylski
3 * Copyright (C) 2012-2014 ownCloud Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2,
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18 package com.owncloud.android.ui.fragment;
19
20 import java.io.File;
21
22 import android.app.Activity;
23 import android.content.Intent;
24 import android.os.Bundle;
25 import android.support.v4.widget.SwipeRefreshLayout;
26 import android.view.ContextMenu;
27 import android.view.MenuInflater;
28 import android.view.MenuItem;
29 import android.view.View;
30 import android.widget.AdapterView;
31 import android.widget.AdapterView.AdapterContextMenuInfo;
32
33 import com.owncloud.android.R;
34 import com.owncloud.android.datamodel.FileDataStorageManager;
35 import com.owncloud.android.datamodel.OCFile;
36 import com.owncloud.android.files.FileMenuFilter;
37 import com.owncloud.android.ui.activity.FileDisplayActivity;
38 import com.owncloud.android.ui.activity.MoveActivity;
39 import com.owncloud.android.ui.adapter.FileListListAdapter;
40 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
41 import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
42 import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
43 import com.owncloud.android.ui.preview.PreviewImageFragment;
44 import com.owncloud.android.ui.preview.PreviewMediaFragment;
45 import com.owncloud.android.utils.Log_OC;
46
47 /**
48 * A Fragment that lists all files and folders in a given path.
49 *
50 * TODO refactorize to get rid of direct dependency on FileDisplayActivity
51 *
52 * @author Bartek Przybylski
53 * @author masensio
54 * @author David A. Velasco
55 */
56 public class OCFileListFragment extends ExtendedListFragment {
57
58 private static final String TAG = OCFileListFragment.class.getSimpleName();
59
60 private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ? OCFileListFragment.class
61 .getPackage().getName() : "com.owncloud.android.ui.fragment";
62
63 public final static String ARG_JUST_FOLDERS = MY_PACKAGE + ".JUST_FOLDERS";
64 public final static String ARG_ALLOW_CONTEXTUAL_ACTIONS = MY_PACKAGE + ".ALLOW_CONTEXTUAL";
65
66 private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
67
68 private FileFragment.ContainerActivity mContainerActivity;
69
70 private OCFile mFile = null;
71 private FileListListAdapter mAdapter;
72
73 private OCFile mTargetFile;
74
75 /**
76 * {@inheritDoc}
77 */
78 @Override
79 public void onAttach(Activity activity) {
80 super.onAttach(activity);
81 Log_OC.e(TAG, "onAttach");
82 try {
83 mContainerActivity = (FileFragment.ContainerActivity) activity;
84
85 } catch (ClassCastException e) {
86 throw new ClassCastException(activity.toString() + " must implement "
87 + FileFragment.ContainerActivity.class.getSimpleName());
88 }
89 try {
90 setOnRefreshListener((SwipeRefreshLayout.OnRefreshListener) activity);
91
92 } catch (ClassCastException e) {
93 throw new ClassCastException(activity.toString() + " must implement "
94 + SwipeRefreshLayout.OnRefreshListener.class.getSimpleName());
95 }
96 }
97
98 @Override
99 public void onDetach() {
100 setOnRefreshListener(null);
101 mContainerActivity = null;
102 super.onDetach();
103 }
104
105 /**
106 * {@inheritDoc}
107 */
108 @Override
109 public void onActivityCreated(Bundle savedInstanceState) {
110 super.onActivityCreated(savedInstanceState);
111 Log_OC.e(TAG, "onActivityCreated() start");
112
113 if (savedInstanceState != null) {
114 mFile = savedInstanceState.getParcelable(KEY_FILE);
115 }
116
117 Bundle args = getArguments();
118 boolean justFolders = (args == null) ? false : args.getBoolean(ARG_JUST_FOLDERS, false);
119 mAdapter = new FileListListAdapter(justFolders, getSherlockActivity(), mContainerActivity);
120 setListAdapter(mAdapter);
121
122 registerForContextMenu(getListView());
123 getListView().setOnCreateContextMenuListener(this);
124 }
125
126 /**
127 * Saves the current listed folder.
128 */
129 @Override
130 public void onSaveInstanceState(Bundle outState) {
131 super.onSaveInstanceState(outState);
132 outState.putParcelable(KEY_FILE, mFile);
133 }
134
135 /**
136 * Call this, when the user presses the up button.
137 *
138 * Tries to move up the current folder one level. If the parent folder was
139 * removed from the database, it continues browsing up until finding an
140 * existing folders.
141 *
142 * return Count of folder levels browsed up.
143 */
144 public int onBrowseUp() {
145 OCFile parentDir = null;
146 int moveCount = 0;
147
148 if (mFile != null) {
149 FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
150
151 String parentPath = null;
152 if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
153 parentPath = new File(mFile.getRemotePath()).getParent();
154 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath
155 + OCFile.PATH_SEPARATOR;
156 parentDir = storageManager.getFileByPath(parentPath);
157 moveCount++;
158 } else {
159 parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);
160 }
161 while (parentDir == null) {
162 parentPath = new File(parentPath).getParent();
163 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath
164 + OCFile.PATH_SEPARATOR;
165 parentDir = storageManager.getFileByPath(parentPath);
166 moveCount++;
167 } // exit is granted because storageManager.getFileByPath("/") never
168 // returns null
169 mFile = parentDir;
170
171 listDirectory(mFile);
172
173 onRefresh();
174
175 // restore index and top position
176 restoreIndexAndTopPosition();
177
178 } // else - should never happen now
179
180 return moveCount;
181 }
182
183 @Override
184 public void onItemClick(AdapterView<?> l, View v, int position, long id) {
185 OCFile file = (OCFile) mAdapter.getItem(position);
186 if (file != null) {
187 if (file.isFolder()) {
188 // update state and view of this fragment
189 listDirectory(file);
190 // then, notify parent activity to let it update its state and
191 // view
192 mContainerActivity.onBrowsedDownTo(file);
193 // save index and top position
194 saveIndexAndTopPosition(position);
195
196 } else { // / Click on a file
197 if (PreviewImageFragment.canBePreviewed(file)) {
198 // preview image - it handles the download, if needed
199 ((FileDisplayActivity) mContainerActivity).startImagePreview(file);
200
201 } else if (file.isDown()) {
202 if (PreviewMediaFragment.canBePreviewed(file)) {
203 // media preview
204 ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true);
205 } else {
206 mContainerActivity.getFileOperationsHelper().openFile(file);
207 }
208
209 } else {
210 // automatic download, preview on finish
211 ((FileDisplayActivity) mContainerActivity).startDownloadForPreview(file);
212 }
213
214 }
215
216 } else {
217 Log_OC.d(TAG, "Null object in ListAdapter!!");
218 }
219
220 }
221
222 /**
223 * {@inheritDoc}
224 */
225 @Override
226 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
227 super.onCreateContextMenu(menu, v, menuInfo);
228 Bundle args = getArguments();
229 boolean allowContextualActions = (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
230 if (allowContextualActions) {
231 MenuInflater inflater = getSherlockActivity().getMenuInflater();
232 inflater.inflate(R.menu.file_actions_menu, menu);
233 AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
234 OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
235
236 if (mContainerActivity.getStorageManager() != null) {
237 FileMenuFilter mf = new FileMenuFilter(targetFile, mContainerActivity.getStorageManager()
238 .getAccount(), mContainerActivity, getSherlockActivity());
239 mf.filter(menu);
240 }
241
242 // / additional restrictions for this fragment
243 // TODO allow in the future 'open with' for previewable files
244 MenuItem item = menu.findItem(R.id.action_open_file_with);
245 if (item != null) {
246 item.setVisible(false);
247 item.setEnabled(false);
248 }
249 // / TODO break this direct dependency on FileDisplayActivity... if
250 // possible
251 FileFragment frag = ((FileDisplayActivity) getSherlockActivity()).getSecondFragment();
252 if (frag != null && frag instanceof FileDetailFragment
253 && frag.getFile().getFileId() == targetFile.getFileId()) {
254 item = menu.findItem(R.id.action_see_details);
255 if (item != null) {
256 item.setVisible(false);
257 item.setEnabled(false);
258 }
259 }
260 }
261 }
262
263 /**
264 * {@inhericDoc}
265 */
266 @Override
267 public boolean onContextItemSelected(MenuItem item) {
268 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
269 mTargetFile = (OCFile) mAdapter.getItem(info.position);
270 switch (item.getItemId()) {
271 case R.id.action_share_file: {
272 mContainerActivity.getFileOperationsHelper().shareFileWithLink(mTargetFile);
273 return true;
274 }
275 case R.id.action_unshare_file: {
276 mContainerActivity.getFileOperationsHelper().unshareFileWithLink(mTargetFile);
277 return true;
278 }
279 case R.id.action_rename_file: {
280 RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(mTargetFile);
281 dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE);
282 return true;
283 }
284 case R.id.action_remove_file: {
285 RemoveFileDialogFragment dialog = RemoveFileDialogFragment.newInstance(mTargetFile);
286 dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
287 return true;
288 }
289 case R.id.action_download_file:
290 case R.id.action_sync_file: {
291 mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile);
292 return true;
293 }
294 case R.id.action_cancel_download:
295 case R.id.action_cancel_upload: {
296 ((FileDisplayActivity) mContainerActivity).cancelTransference(mTargetFile);
297 return true;
298 }
299 case R.id.action_see_details: {
300 mContainerActivity.showDetails(mTargetFile);
301 return true;
302 }
303 case R.id.action_send_file: {
304 // Obtain the file
305 if (!mTargetFile.isDown()) { // Download the file
306 Log_OC.d(TAG, mTargetFile.getRemotePath() + " : File must be downloaded");
307 ((FileDisplayActivity) mContainerActivity).startDownloadForSending(mTargetFile);
308
309 } else {
310 mContainerActivity.getFileOperationsHelper().sendDownloadedFile(mTargetFile);
311 }
312 return true;
313 }
314 case R.id.action_move: {
315 Intent action = new Intent(getActivity(), MoveActivity.class);
316
317 // Pass mTargetFile that contains info of selected file/folder
318 action.putExtra(MoveActivity.EXTRA_TARGET_FILE, mTargetFile);
319 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
320 return true;
321 }
322 default:
323 return super.onContextItemSelected(item);
324 }
325 }
326
327 /**
328 * Use this to query the {@link OCFile} that is currently being displayed by
329 * this fragment
330 *
331 * @return The currently viewed OCFile
332 */
333 public OCFile getCurrentFile() {
334 return mFile;
335 }
336
337 /**
338 * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null
339 * parameter
340 */
341 public void listDirectory() {
342 listDirectory(null);
343 }
344
345 /**
346 * Lists the given directory on the view. When the input parameter is null,
347 * it will either refresh the last known directory. list the root if there
348 * never was a directory.
349 *
350 * @param directory
351 * File to be listed
352 */
353 public void listDirectory(OCFile directory) {
354 FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
355 if (storageManager != null) {
356
357 // Check input parameters for null
358 if (directory == null) {
359 if (mFile != null) {
360 directory = mFile;
361 } else {
362 directory = storageManager.getFileByPath("/");
363 if (directory == null)
364 return; // no files, wait for sync
365 }
366 }
367
368 // If that's not a directory -> List its parent
369 if (!directory.isFolder()) {
370 Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
371 directory = storageManager.getFileById(directory.getParentId());
372 }
373
374 mAdapter.swapDirectory(directory, storageManager);
375 if (mFile == null || !mFile.equals(directory)) {
376 mList.setSelectionFromTop(0, 0);
377 }
378 mFile = directory;
379 }
380 }
381
382 public void sortByName(boolean descending) {
383 mAdapter.setSortOrder(FileListListAdapter.mSortName, descending);
384 }
385
386 public void sortByDate(boolean descending) {
387 mAdapter.setSortOrder(FileListListAdapter.mSortDate, descending);
388 }
389
390 public void sortBySize(boolean descending) {
391 mAdapter.setSortOrder(FileListListAdapter.mSortSize, descending);
392 }
393 }