a5e59d4655a02cdff5ced605f1b4d18e4df5535f
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / fragment / OCFileListFragment.java
1 /* ownCloud Android client application
2 * Copyright (C) 2011 Bartek Przybylski
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
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 com.owncloud.android.AccountUtils;
23 import com.owncloud.android.R;
24 import com.owncloud.android.datamodel.DataStorageManager;
25 import com.owncloud.android.datamodel.OCFile;
26 import com.owncloud.android.network.OwnCloudClientUtils;
27 import com.owncloud.android.operations.OnRemoteOperationListener;
28 import com.owncloud.android.operations.RemoteOperation;
29 import com.owncloud.android.operations.RemoteOperationResult;
30 import com.owncloud.android.operations.RemoveFileOperation;
31 import com.owncloud.android.operations.RenameFileOperation;
32 import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
33 import com.owncloud.android.ui.FragmentListView;
34 import com.owncloud.android.ui.activity.FileDisplayActivity;
35 import com.owncloud.android.ui.activity.TransferServiceGetter;
36 import com.owncloud.android.ui.adapter.FileListListAdapter;
37 import com.owncloud.android.ui.dialog.EditNameDialog;
38 import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
39
40 import eu.alefzero.webdav.WebdavClient;
41
42 import android.app.Activity;
43 import android.os.Bundle;
44 import android.os.Handler;
45 import android.support.v4.app.FragmentTransaction;
46 import android.util.Log;
47 import android.view.ContextMenu;
48 import android.view.MenuInflater;
49 import android.view.MenuItem;
50 import android.view.View;
51 import android.widget.AdapterView;
52 import android.widget.Toast;
53 import android.widget.AdapterView.AdapterContextMenuInfo;
54
55 /**
56 * A Fragment that lists all files and folders in a given path.
57 *
58 * @author Bartek Przybylski
59 *
60 */
61 public class OCFileListFragment extends FragmentListView implements EditNameDialog.EditNameDialogListener, OnRemoteOperationListener, ConfirmationDialogFragmentListener {
62 private static final String TAG = "FileListFragment";
63 private static final String SAVED_LIST_POSITION = "LIST_POSITION";
64
65 private OCFileListFragment.ContainerActivity mContainerActivity;
66
67 private OCFile mFile = null;
68 private FileListListAdapter mAdapter;
69
70 private Handler mHandler;
71 private boolean mIsLargeLayout;
72 private OCFile mTargetFile;
73
74
75 /**
76 * {@inheritDoc}
77 */
78 @Override
79 public void onAttach(Activity activity) {
80 super.onAttach(activity);
81 try {
82 mContainerActivity = (ContainerActivity) activity;
83 } catch (ClassCastException e) {
84 throw new ClassCastException(activity.toString() + " must implement " + OCFileListFragment.ContainerActivity.class.getSimpleName());
85 }
86 }
87
88
89 /**
90 * {@inheritDoc}
91 */
92 @Override
93 public void onActivityCreated(Bundle savedInstanceState) {
94 Log.i(TAG, "onActivityCreated() start");
95
96 super.onActivityCreated(savedInstanceState);
97 mAdapter = new FileListListAdapter(mContainerActivity.getInitialDirectory(), mContainerActivity.getStorageManager(), getActivity(), mContainerActivity);
98 setListAdapter(mAdapter);
99
100 if (savedInstanceState != null) {
101 Log.i(TAG, "savedInstanceState is not null");
102 int position = savedInstanceState.getInt(SAVED_LIST_POSITION);
103 setReferencePosition(position);
104 }
105
106 registerForContextMenu(getListView());
107 getListView().setOnCreateContextMenuListener(this);
108
109 mIsLargeLayout = getResources().getBoolean(R.bool.large_layout);
110 mHandler = new Handler();
111
112 Log.i(TAG, "onActivityCreated() stop");
113 }
114
115
116
117 @Override
118 public void onSaveInstanceState(Bundle savedInstanceState) {
119 Log.i(TAG, "onSaveInstanceState() start");
120
121 savedInstanceState.putInt(SAVED_LIST_POSITION, getReferencePosition());
122
123 Log.i(TAG, "onSaveInstanceState() stop");
124 }
125
126
127 @Override
128 public void onItemClick(AdapterView<?> l, View v, int position, long id) {
129 OCFile file = (OCFile) mAdapter.getItem(position);
130 if (file != null) {
131 /// Click on a directory
132 if (file.getMimetype().equals("DIR")) {
133 // just local updates
134 mFile = file;
135 listDirectory(file);
136 // any other updates are let to the container Activity
137 mContainerActivity.onDirectoryClick(file);
138
139 } else { /// Click on a file
140 mContainerActivity.onFileClick(file);
141 }
142
143 } else {
144 Log.d(TAG, "Null object in ListAdapter!!");
145 }
146
147 }
148
149 /**
150 * {@inheritDoc}
151 */
152 @Override
153 public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
154 super.onCreateContextMenu(menu, v, menuInfo);
155 MenuInflater inflater = getActivity().getMenuInflater();
156 inflater.inflate(R.menu.file_context_menu, menu);
157 AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
158 OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
159 MenuItem item = null;
160 int[] ids = null;
161 if (targetFile.isDirectory()) {
162 int[] theIds = {R.id.open_file_item, R.id.download_file_item, R.id.cancel_download_item, R.id.cancel_upload_item};
163 ids = theIds;
164
165 } else if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
166 int[] theIds = {R.id.open_file_item, R.id.download_file_item, R.id.cancel_upload_item};
167 ids = theIds;
168
169 } else if ( mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
170 int[] theIds = {R.id.open_file_item, R.id.download_file_item, R.id.cancel_download_item};
171 ids = theIds;
172
173 } else if ( targetFile.isDown()) {
174 int[] theIds = {R.id.cancel_download_item, R.id.cancel_upload_item};
175 ids = theIds;
176
177 } else {
178 int[] theIds = {R.id.open_file_item, R.id.cancel_download_item, R.id.cancel_upload_item};
179 ids = theIds;
180 }
181
182 for (int i=0; i < ids.length; i++) {
183 item = menu.findItem(ids[i]);
184 if (item != null) {
185 item.setVisible(false);
186 item.setEnabled(false);
187 }
188 }
189 }
190
191
192 /**
193 * {@inhericDoc}
194 */
195 @Override
196 public boolean onContextItemSelected (MenuItem item) {
197 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
198 mTargetFile = (OCFile) mAdapter.getItem(info.position);
199 switch (item.getItemId()) {
200 case R.id.rename_file_item:
201 EditNameDialog dialog = EditNameDialog.newInstance(mTargetFile.getFileName());
202 dialog.setOnDismissListener(this);
203 dialog.show(getFragmentManager(), "nameeditdialog");
204 Log.d(TAG, "RENAME SELECTED, item " + info.id + " at position " + info.position);
205 return true;
206 case R.id.remove_file_item:
207 int messageStringId = R.string.confirmation_remove_alert;
208 int posBtnStringId = R.string.confirmation_remove_remote;
209 int neuBtnStringId = -1;
210 if (mTargetFile.isDirectory()) {
211 messageStringId = R.string.confirmation_remove_folder_alert;
212 posBtnStringId = R.string.confirmation_remove_remote_and_local;
213 neuBtnStringId = R.string.confirmation_remove_folder_local;
214 } else if (mTargetFile.isDown()) {
215 posBtnStringId = R.string.confirmation_remove_remote_and_local;
216 neuBtnStringId = R.string.confirmation_remove_local;
217 }
218 ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
219 messageStringId,
220 new String[]{mTargetFile.getFileName()},
221 posBtnStringId,
222 neuBtnStringId,
223 R.string.common_cancel);
224 confDialog.setOnConfirmationListener(this);
225 confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION);
226 Log.d(TAG, "REMOVE SELECTED, item " + info.id + " at position " + info.position);
227 return true;
228 default:
229 return super.onContextItemSelected(item);
230 }
231 }
232
233
234 /**
235 * Call this, when the user presses the up button
236 */
237 public void onNavigateUp() {
238 OCFile parentDir = null;
239
240 if(mFile != null){
241 DataStorageManager storageManager = mContainerActivity.getStorageManager();
242 parentDir = storageManager.getFileById(mFile.getParentId());
243 mFile = parentDir;
244 }
245 listDirectory(parentDir);
246 }
247
248 /**
249 * Use this to query the {@link OCFile} that is currently
250 * being displayed by this fragment
251 * @return The currently viewed OCFile
252 */
253 public OCFile getCurrentFile(){
254 return mFile;
255 }
256
257 /**
258 * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
259 */
260 public void listDirectory(){
261 listDirectory(null);
262 }
263
264 /**
265 * Lists the given directory on the view. When the input parameter is null,
266 * it will either refresh the last known directory, or list the root
267 * if there never was a directory.
268 *
269 * @param directory File to be listed
270 */
271 public void listDirectory(OCFile directory) {
272 DataStorageManager storageManager = mContainerActivity.getStorageManager();
273 if (storageManager != null) {
274
275 // Check input parameters for null
276 if(directory == null){
277 if(mFile != null){
278 directory = mFile;
279 } else {
280 directory = storageManager.getFileByPath("/");
281 if (directory == null) return; // no files, wait for sync
282 }
283 }
284
285
286 // If that's not a directory -> List its parent
287 if(!directory.isDirectory()){
288 Log.w(TAG, "You see, that is not a directory -> " + directory.toString());
289 directory = storageManager.getFileById(directory.getParentId());
290 }
291
292 mAdapter.swapDirectory(directory, storageManager);
293 if (mFile == null || !mFile.equals(directory)) {
294 mList.setSelectionFromTop(0, 0);
295 }
296 mFile = directory;
297 }
298 }
299
300
301
302 /**
303 * Interface to implement by any Activity that includes some instance of FileListFragment
304 *
305 * @author David A. Velasco
306 */
307 public interface ContainerActivity extends TransferServiceGetter {
308
309 /**
310 * Callback method invoked when a directory is clicked by the user on the files list
311 *
312 * @param file
313 */
314 public void onDirectoryClick(OCFile file);
315
316 /**
317 * Callback method invoked when a file (non directory) is clicked by the user on the files list
318 *
319 * @param file
320 */
321 public void onFileClick(OCFile file);
322
323 /**
324 * Getter for the current DataStorageManager in the container activity
325 */
326 public DataStorageManager getStorageManager();
327
328
329 /**
330 * Callback method invoked when the parent activity is fully created to get the directory to list firstly.
331 *
332 * @return Directory to list firstly. Can be NULL.
333 */
334 public OCFile getInitialDirectory();
335
336
337 }
338
339
340
341 @Override
342 public void onDismiss(EditNameDialog dialog) {
343 if (dialog.getResult()) {
344 String newFilename = dialog.getNewFilename();
345 Log.d(TAG, "name edit dialog dismissed with new name " + newFilename);
346 RemoteOperation operation = new RenameFileOperation(mTargetFile,
347 newFilename,
348 mContainerActivity.getStorageManager());
349 WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity().getApplicationContext());
350 operation.execute(wc, this, mHandler);
351 getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
352 }
353 }
354
355
356 @Override
357 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
358 if (operation instanceof RemoveFileOperation) {
359 onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
360
361 } else if (operation instanceof RenameFileOperation) {
362 onRenameFileOperationFinish((RenameFileOperation)operation, result);
363 }
364 }
365
366
367 private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
368 getActivity().dismissDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
369 if (result.isSuccess()) {
370 Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
371 msg.show();
372 if (mIsLargeLayout) {
373 // TODO - this should be done only when the current FileDetailFragment shows the deleted file
374 // -> THIS METHOD WOULD BE BETTER PLACED AT THE ACTIVITY LEVEL
375 FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
376 transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment
377 transaction.commit();
378 }
379 listDirectory();
380
381 } else {
382 Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG);
383 msg.show();
384 if (result.isSslRecoverableException()) {
385 // TODO show the SSL warning dialog
386 }
387 }
388 }
389
390
391 private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
392 getActivity().dismissDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
393 if (result.isSuccess()) {
394 listDirectory();
395 // TODO is file
396
397 } else {
398 if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
399 Toast msg = Toast.makeText(getActivity(), R.string.rename_local_fail_msg, Toast.LENGTH_LONG);
400 msg.show();
401 // TODO throw again the new rename dialog
402 } else {
403 Toast msg = Toast.makeText(getActivity(), R.string.rename_server_fail_msg, Toast.LENGTH_LONG);
404 msg.show();
405 if (result.isSslRecoverableException()) {
406 // TODO show the SSL warning dialog
407 }
408 }
409 }
410 }
411
412
413 @Override
414 public void onConfirmation(String callerTag) {
415 if (callerTag.equals(FileDetailFragment.FTAG_CONFIRMATION)) {
416 if (mContainerActivity.getStorageManager().getFileById(mTargetFile.getFileId()) != null) {
417 RemoteOperation operation = new RemoveFileOperation( mTargetFile,
418 true,
419 mContainerActivity.getStorageManager());
420 WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity().getApplicationContext());
421 operation.execute(wc, this, mHandler);
422
423 getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
424 }
425 }
426 }
427
428 @Override
429 public void onNeutral(String callerTag) {
430 File f = null;
431 if (mTargetFile.isDirectory()) {
432 // TODO run in a secondary thread?
433 mContainerActivity.getStorageManager().removeDirectory(mTargetFile, false, true);
434
435 } else if (mTargetFile.isDown() && (f = new File(mTargetFile.getStoragePath())).exists()) {
436 f.delete();
437 mTargetFile.setStoragePath(null);
438 mContainerActivity.getStorageManager().saveFile(mFile);
439 }
440 listDirectory();
441 }
442
443 @Override
444 public void onCancel(String callerTag) {
445 Log.d(TAG, "REMOVAL CANCELED");
446 }
447
448
449 }