Rename and remove operations connected to the contextual menu over items at the list...
[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 }
159
160
161 /**
162 * {@inhericDoc}
163 */
164 @Override
165 public boolean onContextItemSelected (MenuItem item) {
166 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
167 mTargetFile = (OCFile) mAdapter.getItem(info.position);
168 switch (item.getItemId()) {
169 case R.id.rename_file_item:
170 EditNameDialog dialog = EditNameDialog.newInstance(mTargetFile.getFileName());
171 dialog.setOnDismissListener(this);
172 dialog.show(getFragmentManager(), "nameeditdialog");
173 Log.d(TAG, "RENAME SELECTED, item " + info.id + " at position " + info.position);
174 return true;
175 case R.id.remove_file_item:
176 ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
177 R.string.confirmation_remove_alert,
178 new String[]{mTargetFile.getFileName()},
179 mTargetFile.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote,
180 mTargetFile.isDown() ? R.string.confirmation_remove_local : -1,
181 R.string.common_cancel);
182 confDialog.setOnConfirmationListener(this);
183 confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION);
184 Log.d(TAG, "REMOVE SELECTED, item " + info.id + " at position " + info.position);
185 return true;
186 default:
187 return super.onContextItemSelected(item);
188 }
189 }
190
191
192 /**
193 * Call this, when the user presses the up button
194 */
195 public void onNavigateUp() {
196 OCFile parentDir = null;
197
198 if(mFile != null){
199 DataStorageManager storageManager = mContainerActivity.getStorageManager();
200 parentDir = storageManager.getFileById(mFile.getParentId());
201 mFile = parentDir;
202 }
203 listDirectory(parentDir);
204 }
205
206 /**
207 * Use this to query the {@link OCFile} that is currently
208 * being displayed by this fragment
209 * @return The currently viewed OCFile
210 */
211 public OCFile getCurrentFile(){
212 return mFile;
213 }
214
215 /**
216 * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
217 */
218 public void listDirectory(){
219 listDirectory(null);
220 }
221
222 /**
223 * Lists the given directory on the view. When the input parameter is null,
224 * it will either refresh the last known directory, or list the root
225 * if there never was a directory.
226 *
227 * @param directory File to be listed
228 */
229 public void listDirectory(OCFile directory) {
230 DataStorageManager storageManager = mContainerActivity.getStorageManager();
231 if (storageManager != null) {
232
233 // Check input parameters for null
234 if(directory == null){
235 if(mFile != null){
236 directory = mFile;
237 } else {
238 directory = storageManager.getFileByPath("/");
239 if (directory == null) return; // no files, wait for sync
240 }
241 }
242
243
244 // If that's not a directory -> List its parent
245 if(!directory.isDirectory()){
246 Log.w(TAG, "You see, that is not a directory -> " + directory.toString());
247 directory = storageManager.getFileById(directory.getParentId());
248 }
249
250 mAdapter.swapDirectory(directory, storageManager);
251 if (mFile == null || !mFile.equals(directory)) {
252 mList.setSelectionFromTop(0, 0);
253 }
254 mFile = directory;
255 }
256 }
257
258
259
260 /**
261 * Interface to implement by any Activity that includes some instance of FileListFragment
262 *
263 * @author David A. Velasco
264 */
265 public interface ContainerActivity extends TransferServiceGetter {
266
267 /**
268 * Callback method invoked when a directory is clicked by the user on the files list
269 *
270 * @param file
271 */
272 public void onDirectoryClick(OCFile file);
273
274 /**
275 * Callback method invoked when a file (non directory) is clicked by the user on the files list
276 *
277 * @param file
278 */
279 public void onFileClick(OCFile file);
280
281 /**
282 * Getter for the current DataStorageManager in the container activity
283 */
284 public DataStorageManager getStorageManager();
285
286
287 /**
288 * Callback method invoked when the parent activity is fully created to get the directory to list firstly.
289 *
290 * @return Directory to list firstly. Can be NULL.
291 */
292 public OCFile getInitialDirectory();
293
294
295 }
296
297
298
299 @Override
300 public void onDismiss(EditNameDialog dialog) {
301 if (dialog.getResult()) {
302 String newFilename = dialog.getNewFilename();
303 Log.d(TAG, "name edit dialog dismissed with new name " + newFilename);
304 RemoteOperation operation = new RenameFileOperation(mTargetFile,
305 newFilename,
306 mContainerActivity.getStorageManager());
307 WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity().getApplicationContext());
308 operation.execute(wc, this, mHandler);
309 getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
310 }
311 }
312
313
314 @Override
315 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
316 if (operation instanceof RemoveFileOperation) {
317 onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
318
319 } else if (operation instanceof RenameFileOperation) {
320 onRenameFileOperationFinish((RenameFileOperation)operation, result);
321 }
322 }
323
324
325 private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
326 getActivity().dismissDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
327 if (result.isSuccess()) {
328 Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
329 msg.show();
330 if (mIsLargeLayout) {
331 // TODO - this should be done only when the current FileDetailFragment shows the deleted file
332 // -> THIS METHOD WOULD BE BETTER PLACED AT THE ACTIVITY LEVEL
333 FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
334 transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment
335 transaction.commit();
336 }
337 listDirectory();
338
339 } else {
340 Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG);
341 msg.show();
342 if (result.isSslRecoverableException()) {
343 // TODO show the SSL warning dialog
344 }
345 }
346 }
347
348
349 private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
350 getActivity().dismissDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
351 if (result.isSuccess()) {
352 listDirectory();
353 // TODO is file
354
355 } else {
356 if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
357 Toast msg = Toast.makeText(getActivity(), R.string.rename_local_fail_msg, Toast.LENGTH_LONG);
358 msg.show();
359 // TODO throw again the new rename dialog
360 } else {
361 Toast msg = Toast.makeText(getActivity(), R.string.rename_server_fail_msg, Toast.LENGTH_LONG);
362 msg.show();
363 if (result.isSslRecoverableException()) {
364 // TODO show the SSL warning dialog
365 }
366 }
367 }
368 }
369
370
371 @Override
372 public void onConfirmation(String callerTag) {
373 if (callerTag.equals(FileDetailFragment.FTAG_CONFIRMATION)) {
374 if (mContainerActivity.getStorageManager().getFileById(mTargetFile.getFileId()) != null) {
375 RemoteOperation operation = new RemoveFileOperation( mTargetFile,
376 true,
377 mContainerActivity.getStorageManager());
378 WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity().getApplicationContext());
379 operation.execute(wc, this, mHandler);
380
381 getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
382 }
383 }
384 }
385
386 @Override
387 public void onNeutral(String callerTag) {
388 File f = null;
389 if (mTargetFile.isDown() && (f = new File(mTargetFile.getStoragePath())).exists()) {
390 f.delete();
391 mTargetFile.setStoragePath(null);
392 mContainerActivity.getStorageManager().saveFile(mFile);
393 }
394 listDirectory();
395 }
396
397 @Override
398 public void onCancel(String callerTag) {
399 Log.d(TAG, "REMOVAL CANCELED");
400 }
401
402
403 }