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