Trigger MoveFileOperation when 'Choose' button is clicked
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / activity / MoveActivity.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012-2014 ownCloud Inc.
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 version 2,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 package com.owncloud.android.ui.activity;
19
20 import java.io.IOException;
21
22 import android.accounts.Account;
23 import android.accounts.AccountManager;
24 import android.accounts.AuthenticatorException;
25 import android.accounts.OperationCanceledException;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.res.Resources.NotFoundException;
31 import android.os.Bundle;
32 import android.support.v4.app.Fragment;
33 import android.support.v4.app.FragmentTransaction;
34 import android.support.v4.widget.SwipeRefreshLayout;
35 import android.util.Log;
36 import android.view.View;
37 import android.view.View.OnClickListener;
38 import android.widget.Button;
39 import android.widget.Toast;
40
41 import com.actionbarsherlock.app.ActionBar;
42 import com.actionbarsherlock.view.Menu;
43 import com.actionbarsherlock.view.MenuInflater;
44 import com.actionbarsherlock.view.MenuItem;
45 import com.actionbarsherlock.view.Window;
46 import com.owncloud.android.R;
47 import com.owncloud.android.datamodel.OCFile;
48 import com.owncloud.android.lib.common.OwnCloudAccount;
49 import com.owncloud.android.lib.common.OwnCloudClient;
50 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
51 import com.owncloud.android.lib.common.OwnCloudCredentials;
52 import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
53 import com.owncloud.android.lib.common.operations.RemoteOperation;
54 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
55 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
56 import com.owncloud.android.operations.CreateFolderOperation;
57 import com.owncloud.android.operations.SynchronizeFolderOperation;
58 import com.owncloud.android.syncadapter.FileSyncAdapter;
59 import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
60 import com.owncloud.android.ui.fragment.FileFragment;
61 import com.owncloud.android.ui.fragment.OCFileListFragment;
62 import com.owncloud.android.utils.DisplayUtils;
63 import com.owncloud.android.utils.ErrorMessageAdapter;
64 import com.owncloud.android.utils.Log_OC;
65
66 public class MoveActivity extends HookActivity implements FileFragment.ContainerActivity,
67 OnClickListener, SwipeRefreshLayout.OnRefreshListener {
68
69 public static final String EXTRA_CURRENT_FOLDER = UploadFilesActivity.class.getCanonicalName() + ".EXTRA_CURRENT_FOLDER";
70
71 public static final int RESULT_OK_AND_MOVE = 1;
72
73 private SyncBroadcastReceiver mSyncBroadcastReceiver;
74
75 private static final String TAG = MoveActivity.class.getSimpleName();
76
77 private static final String TAG_LIST_OF_FOLDERS = "LIST_OF_FOLDERS";
78
79 private boolean mSyncInProgress = false;
80
81 private Button mCancelBtn;
82 private Button mChooseBtn;
83
84
85 @Override
86 protected void onCreate(Bundle savedInstanceState) {
87 Log_OC.d(TAG, "onCreate() start");
88 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
89
90 super.onCreate(savedInstanceState);
91
92 setContentView(R.layout.files_move);
93
94 if (savedInstanceState == null) {
95 createFragments();
96 }
97
98 // sets callback listeners for UI elements
99 initControls();
100
101 // Action bar setup
102 ActionBar actionBar = getSupportActionBar();
103 actionBar.setDisplayShowTitleEnabled(true);
104 actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
105 setSupportProgressBarIndeterminateVisibility(mSyncInProgress);
106 // always AFTER setContentView(...) ; to work around bug in its implementation
107
108 // sets message for empty list of folders
109 setBackgroundText();
110
111 Log_OC.d(TAG, "onCreate() end");
112
113 }
114
115 @Override
116 protected void onStart() {
117 super.onStart();
118 getSupportActionBar().setIcon(DisplayUtils.getSeasonalIconId());
119 }
120
121 @Override
122 protected void onDestroy() {
123 super.onDestroy();
124 }
125
126 /**
127 * Called when the ownCloud {@link Account} associated to the Activity was just updated.
128 */
129 @Override
130 protected void onAccountSet(boolean stateWasRecovered) {
131 super.onAccountSet(stateWasRecovered);
132 if (getAccount() != null) {
133
134 updateFileFromDB();
135
136 OCFile folder = getFile();
137 if (folder == null || !folder.isFolder()) {
138 // fall back to root folder
139 setFile(getStorageManager().getFileByPath(OCFile.ROOT_PATH));
140 folder = getFile();
141 }
142
143 if (!stateWasRecovered) {
144 OCFileListFragment listOfFolders = getListOfFilesFragment();
145 listOfFolders.listDirectory(folder);
146
147 startSyncFolderOperation(folder);
148 }
149
150 updateNavigationElementsInActionBar();
151 }
152 }
153
154 private void createFragments() {
155 OCFileListFragment listOfFiles = new OCFileListFragment();
156 Bundle args = new Bundle();
157 args.putBoolean(OCFileListFragment.ARG_JUST_FOLDERS, true);
158 args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, false);
159 listOfFiles.setArguments(args);
160 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
161 transaction.add(R.id.fragment_container, listOfFiles, TAG_LIST_OF_FOLDERS);
162 transaction.commit();
163 }
164
165 /**
166 * Show a text message on screen view for notifying user if content is
167 * loading or folder is empty
168 */
169 private void setBackgroundText() {
170 OCFileListFragment listFragment = getListOfFilesFragment();
171 if (listFragment != null) {
172 int message = R.string.file_list_loading;
173 if (!mSyncInProgress) {
174 // In case folder list is empty
175 message = R.string.file_list_empty_moving;
176 }
177 listFragment.setMessageForEmptyList(getString(message));
178 } else {
179 Log.e(TAG, "OCFileListFragment is null");
180 }
181 }
182
183 private OCFileListFragment getListOfFilesFragment() {
184 Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(MoveActivity.TAG_LIST_OF_FOLDERS);
185 if (listOfFiles != null) {
186 return (OCFileListFragment)listOfFiles;
187 }
188 Log_OC.wtf(TAG, "Access to unexisting list of files fragment!!");
189 return null;
190 }
191
192
193 /**
194 * {@inheritDoc}
195 *
196 * Updates action bar and second fragment, if in dual pane mode.
197 */
198 @Override
199 public void onBrowsedDownTo(OCFile directory) {
200 setFile(directory);
201 updateNavigationElementsInActionBar();
202 // Sync Folder
203 startSyncFolderOperation(directory);
204
205 }
206
207
208 public void startSyncFolderOperation(OCFile folder) {
209 long currentSyncTime = System.currentTimeMillis();
210
211 mSyncInProgress = true;
212
213 // perform folder synchronization
214 RemoteOperation synchFolderOp = new SynchronizeFolderOperation( folder,
215 currentSyncTime,
216 false,
217 getFileOperationsHelper().isSharedSupported(),
218 getStorageManager(),
219 getAccount(),
220 getApplicationContext()
221 );
222 synchFolderOp.execute(getAccount(), this, null, null);
223
224 setSupportProgressBarIndeterminateVisibility(true);
225
226 setBackgroundText();
227 }
228
229 @Override
230 protected void onResume() {
231 super.onResume();
232 Log_OC.e(TAG, "onResume() start");
233
234 // refresh list of files
235 refreshListOfFilesFragment();
236
237 // Listen for sync messages
238 IntentFilter syncIntentFilter = new IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START);
239 syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_END);
240 syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED);
241 syncIntentFilter.addAction(SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED);
242 syncIntentFilter.addAction(SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED);
243 mSyncBroadcastReceiver = new SyncBroadcastReceiver();
244 registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
245
246 Log_OC.d(TAG, "onResume() end");
247 }
248
249 @Override
250 protected void onPause() {
251 Log_OC.e(TAG, "onPause() start");
252 if (mSyncBroadcastReceiver != null) {
253 unregisterReceiver(mSyncBroadcastReceiver);
254 //LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadcastReceiver);
255 mSyncBroadcastReceiver = null;
256 }
257
258 Log_OC.d(TAG, "onPause() end");
259 super.onPause();
260 }
261
262 @Override
263 public boolean onCreateOptionsMenu(Menu menu) {
264 MenuInflater inflater = getSherlock().getMenuInflater();
265 inflater.inflate(R.menu.main_menu, menu);
266 menu.findItem(R.id.action_upload).setVisible(false);
267 menu.findItem(R.id.action_settings).setVisible(false);
268 menu.findItem(R.id.action_sync_account).setVisible(false);
269 return true;
270 }
271
272 @Override
273 public boolean onOptionsItemSelected(MenuItem item) {
274 boolean retval = true;
275 switch (item.getItemId()) {
276 case R.id.action_create_dir: {
277 CreateFolderDialogFragment dialog =
278 CreateFolderDialogFragment.newInstance(getCurrentFolder());
279 dialog.show(
280 getSupportFragmentManager(),
281 CreateFolderDialogFragment.CREATE_FOLDER_FRAGMENT
282 );
283 break;
284 }
285 case android.R.id.home: {
286 OCFile currentDir = getCurrentFolder();
287 if(currentDir != null && currentDir.getParentId() != 0) {
288 onBackPressed();
289 }
290 break;
291 }
292 default:
293 retval = super.onOptionsItemSelected(item);
294 }
295 return retval;
296 }
297
298 private OCFile getCurrentFolder() {
299 OCFile file = getFile();
300 if (file != null) {
301 if (file.isFolder()) {
302 return file;
303 } else if (getStorageManager() != null) {
304 String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
305 return getStorageManager().getFileByPath(parentPath);
306 }
307 }
308 return null;
309 }
310
311 protected void refreshListOfFilesFragment() {
312 OCFileListFragment fileListFragment = getListOfFilesFragment();
313 if (fileListFragment != null) {
314 fileListFragment.listDirectory();
315 }
316 }
317
318 public void browseToRoot() {
319 OCFileListFragment listOfFiles = getListOfFilesFragment();
320 if (listOfFiles != null) { // should never be null, indeed
321 OCFile root = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
322 listOfFiles.listDirectory(root);
323 setFile(listOfFiles.getCurrentFile());
324 updateNavigationElementsInActionBar();
325 startSyncFolderOperation(root);
326 }
327 }
328
329 @Override
330 public void onBackPressed() {
331 OCFileListFragment listOfFiles = getListOfFilesFragment();
332 if (listOfFiles != null) { // should never be null, indeed
333 int levelsUp = listOfFiles.onBrowseUp();
334 if (levelsUp == 0) {
335 finish();
336 return;
337 }
338 setFile(listOfFiles.getCurrentFile());
339 updateNavigationElementsInActionBar();
340 }
341 }
342
343 private void updateNavigationElementsInActionBar() {
344 ActionBar actionBar = getSupportActionBar();
345 OCFile currentDir = getCurrentFolder();
346 boolean atRoot = (currentDir == null || currentDir.getParentId() == 0);
347 actionBar.setDisplayHomeAsUpEnabled(!atRoot);
348 actionBar.setHomeButtonEnabled(!atRoot);
349 actionBar.setTitle(
350 atRoot
351 ? getString(R.string.default_display_name_for_root_folder)
352 : currentDir.getFileName()
353 );
354 }
355
356 /**
357 * Set per-view controllers
358 */
359 private void initControls(){
360 mCancelBtn = (Button) findViewById(R.id.move_files_btn_cancel);
361 mCancelBtn.setOnClickListener(this);
362 mChooseBtn = (Button) findViewById(R.id.move_files_btn_choose);
363 mChooseBtn.setOnClickListener(this);
364 }
365
366 @Override
367 public void onClick(View v) {
368 if (v == mCancelBtn) {
369 finish();
370 } else if (v == mChooseBtn) {
371 Intent data = new Intent();
372 data.putExtra(EXTRA_CURRENT_FOLDER, getCurrentFolder());
373 setResult(RESULT_OK_AND_MOVE, data);
374 finish();
375 }
376 }
377
378
379 @Override
380 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
381 super.onRemoteOperationFinish(operation, result);
382
383 if (operation instanceof CreateFolderOperation) {
384 onCreateFolderOperationFinish((CreateFolderOperation)operation, result);
385
386 }
387 }
388
389
390 /**
391 * Updates the view associated to the activity after the finish of an operation trying
392 * to create a new folder.
393 *
394 * @param operation Creation operation performed.
395 * @param result Result of the creation.
396 */
397 private void onCreateFolderOperationFinish(
398 CreateFolderOperation operation, RemoteOperationResult result
399 ) {
400
401 if (result.isSuccess()) {
402 dismissLoadingDialog();
403 refreshListOfFilesFragment();
404 } else {
405 dismissLoadingDialog();
406 try {
407 Toast msg = Toast.makeText(MoveActivity.this,
408 ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
409 Toast.LENGTH_LONG);
410 msg.show();
411
412 } catch (NotFoundException e) {
413 Log_OC.e(TAG, "Error while trying to show fail message " , e);
414 }
415 }
416 }
417
418
419
420 private class SyncBroadcastReceiver extends BroadcastReceiver {
421
422 /**
423 * {@link BroadcastReceiver} to enable syncing feedback in UI
424 */
425 @Override
426 public void onReceive(Context context, Intent intent) {
427 try {
428 String event = intent.getAction();
429 Log_OC.d(TAG, "Received broadcast " + event);
430 String accountName = intent.getStringExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME);
431 String synchFolderRemotePath = intent.getStringExtra(FileSyncAdapter.EXTRA_FOLDER_PATH);
432 RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncAdapter.EXTRA_RESULT);
433 boolean sameAccount = (getAccount() != null && accountName.equals(getAccount().name) && getStorageManager() != null);
434
435 if (sameAccount) {
436
437 if (FileSyncAdapter.EVENT_FULL_SYNC_START.equals(event)) {
438 mSyncInProgress = true;
439
440 } else {
441 OCFile currentFile = (getFile() == null) ? null : getStorageManager().getFileByPath(getFile().getRemotePath());
442 OCFile currentDir = (getCurrentFolder() == null) ? null : getStorageManager().getFileByPath(getCurrentFolder().getRemotePath());
443
444 if (currentDir == null) {
445 // current folder was removed from the server
446 Toast.makeText( MoveActivity.this,
447 String.format(getString(R.string.sync_current_folder_was_removed), "PLACEHOLDER"),
448 Toast.LENGTH_LONG)
449 .show();
450 browseToRoot();
451
452 } else {
453 if (currentFile == null && !getFile().isFolder()) {
454 // currently selected file was removed in the server, and now we know it
455 currentFile = currentDir;
456 }
457
458 if (synchFolderRemotePath != null && currentDir.getRemotePath().equals(synchFolderRemotePath)) {
459 OCFileListFragment fileListFragment = getListOfFilesFragment();
460 if (fileListFragment != null) {
461 fileListFragment.listDirectory(currentDir);
462 }
463 }
464 setFile(currentFile);
465 }
466
467 mSyncInProgress = (!FileSyncAdapter.EVENT_FULL_SYNC_END.equals(event) && !SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED.equals(event));
468
469 if (SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED.
470 equals(event) &&
471 /// TODO refactor and make common
472 synchResult != null && !synchResult.isSuccess() &&
473 (synchResult.getCode() == ResultCode.UNAUTHORIZED ||
474 synchResult.isIdPRedirection() ||
475 (synchResult.isException() && synchResult.getException()
476 instanceof AuthenticatorException))) {
477
478 OwnCloudClient client = null;
479 try {
480 OwnCloudAccount ocAccount =
481 new OwnCloudAccount(getAccount(), context);
482 client = (OwnCloudClientManagerFactory.getDefaultSingleton().
483 removeClientFor(ocAccount));
484 // TODO get rid of these exceptions
485 } catch (AccountNotFoundException e) {
486 e.printStackTrace();
487 } catch (AuthenticatorException e) {
488 e.printStackTrace();
489 } catch (OperationCanceledException e) {
490 e.printStackTrace();
491 } catch (IOException e) {
492 e.printStackTrace();
493 }
494
495 if (client != null) {
496 OwnCloudCredentials cred = client.getCredentials();
497 if (cred != null) {
498 AccountManager am = AccountManager.get(context);
499 if (cred.authTokenExpires()) {
500 am.invalidateAuthToken(
501 getAccount().type,
502 cred.getAuthToken()
503 );
504 } else {
505 am.clearPassword(getAccount());
506 }
507 }
508 }
509
510 requestCredentialsUpdate();
511
512 }
513 }
514 removeStickyBroadcast(intent);
515 Log_OC.d(TAG, "Setting progress visibility to " + mSyncInProgress);
516 setSupportProgressBarIndeterminateVisibility(mSyncInProgress /*|| mRefreshSharesInProgress*/);
517
518 setBackgroundText();
519
520 }
521
522 } catch (RuntimeException e) {
523 // avoid app crashes after changing the serial id of RemoteOperationResult
524 // in owncloud library with broadcast notifications pending to process
525 removeStickyBroadcast(intent);
526 }
527 }
528 }
529
530
531
532 /**
533 * Shows the information of the {@link OCFile} received as a
534 * parameter in the second fragment.
535 *
536 * @param file {@link OCFile} whose details will be shown
537 */
538 @Override
539 public void showDetails(OCFile file) {
540
541 }
542
543 /**
544 * {@inheritDoc}
545 */
546 @Override
547 public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
548
549 }
550
551
552 @Override
553 public void onRefresh() {
554 OCFileListFragment listOfFiles = getListOfFilesFragment();
555 if (listOfFiles != null) {
556 OCFile folder = listOfFiles.getCurrentFile();
557 if (folder != null) {
558 startSyncFolderOperation(folder);
559 }
560 }
561 }
562
563 }