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