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