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