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