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