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