npe fix for file upload on instance saving since actionbar in list mode doesn't habe...
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / activity / FolderPickerActivity.java
1 /**
2 * ownCloud Android client application
3 *
4 * Copyright (C) 2015 ownCloud Inc.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2,
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 package com.owncloud.android.ui.activity;
21
22 import android.accounts.Account;
23 import android.accounts.AccountManager;
24 import android.accounts.AuthenticatorException;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.res.Resources.NotFoundException;
30 import android.os.Bundle;
31 import android.os.Parcelable;
32 import android.support.v4.app.Fragment;
33 import android.support.v4.app.FragmentTransaction;
34 import android.support.v7.app.ActionBar;
35 import android.util.Log;
36 import android.view.Menu;
37 import android.view.MenuInflater;
38 import android.view.MenuItem;
39 import android.view.View;
40 import android.view.View.OnClickListener;
41 import android.view.Window;
42 import android.widget.Button;
43 import android.widget.Toast;
44
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.lib.common.utils.Log_OC;
56 import com.owncloud.android.operations.CreateFolderOperation;
57 import com.owncloud.android.operations.RefreshFolderOperation;
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.ErrorMessageAdapter;
63
64 public class FolderPickerActivity extends FileActivity implements FileFragment.ContainerActivity,
65 OnClickListener, OnEnforceableRefreshListener {
66
67 public static final String EXTRA_FOLDER = UploadFilesActivity.class.getCanonicalName()
68 + ".EXTRA_FOLDER";
69 public static final String EXTRA_FILE = UploadFilesActivity.class.getCanonicalName()
70 + ".EXTRA_FILE";
71 //TODO: Think something better
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 }
119
120 /**
121 * Called when the ownCloud {@link Account} associated to the Activity was just updated.
122 */
123 @Override
124 protected void onAccountSet(boolean stateWasRecovered) {
125 super.onAccountSet(stateWasRecovered);
126 if (getAccount() != null) {
127
128 updateFileFromDB();
129
130 OCFile folder = getFile();
131 if (folder == null || !folder.isFolder()) {
132 // fall back to root folder
133 setFile(getStorageManager().getFileByPath(OCFile.ROOT_PATH));
134 folder = getFile();
135 }
136
137 if (!stateWasRecovered) {
138 OCFileListFragment listOfFolders = getListOfFilesFragment();
139 listOfFolders.listDirectory(folder/*, false*/);
140
141 startSyncFolderOperation(folder, false);
142 }
143
144 updateNavigationElementsInActionBar();
145 }
146 }
147
148 private void createFragments() {
149 OCFileListFragment listOfFiles = new OCFileListFragment();
150 Bundle args = new Bundle();
151 args.putBoolean(OCFileListFragment.ARG_JUST_FOLDERS, true);
152 args.putBoolean(OCFileListFragment.ARG_ALLOW_CONTEXTUAL_ACTIONS, false);
153 listOfFiles.setArguments(args);
154 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
155 transaction.add(R.id.fragment_container, listOfFiles, TAG_LIST_OF_FOLDERS);
156 transaction.commit();
157 }
158
159 /**
160 * Show a text message on screen view for notifying user if content is
161 * loading or folder is empty
162 */
163 private void setBackgroundText() {
164 OCFileListFragment listFragment = getListOfFilesFragment();
165 if (listFragment != null) {
166 int message = R.string.file_list_loading;
167 if (!mSyncInProgress) {
168 // In case folder list is empty
169 message = R.string.file_list_empty_moving;
170 }
171 listFragment.setMessageForEmptyList(getString(message));
172 } else {
173 Log.e(TAG, "OCFileListFragment is null");
174 }
175 }
176
177 protected OCFileListFragment getListOfFilesFragment() {
178 Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(FolderPickerActivity.TAG_LIST_OF_FOLDERS);
179 if (listOfFiles != null) {
180 return (OCFileListFragment)listOfFiles;
181 }
182 Log_OC.wtf(TAG, "Access to unexisting list of files fragment!!");
183 return null;
184 }
185
186
187 /**
188 * {@inheritDoc}
189 *
190 * Updates action bar and second fragment, if in dual pane mode.
191 */
192 @Override
193 public void onBrowsedDownTo(OCFile directory) {
194 setFile(directory);
195 updateNavigationElementsInActionBar();
196 // Sync Folder
197 startSyncFolderOperation(directory, false);
198
199 }
200
201
202 public void startSyncFolderOperation(OCFile folder, boolean ignoreETag) {
203 long currentSyncTime = System.currentTimeMillis();
204
205 mSyncInProgress = true;
206
207 // perform folder synchronization
208 RemoteOperation synchFolderOp = new RefreshFolderOperation( folder,
209 currentSyncTime,
210 false,
211 getFileOperationsHelper().isSharedSupported(),
212 ignoreETag,
213 getStorageManager(),
214 getAccount(),
215 getApplicationContext()
216 );
217 synchFolderOp.execute(getAccount(), this, null, null);
218
219 setSupportProgressBarIndeterminateVisibility(true);
220
221 setBackgroundText();
222 }
223
224 @Override
225 protected void onResume() {
226 super.onResume();
227 Log_OC.e(TAG, "onResume() start");
228
229 // refresh list of files
230 refreshListOfFilesFragment();
231
232 // Listen for sync messages
233 IntentFilter syncIntentFilter = new IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START);
234 syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_END);
235 syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED);
236 syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED);
237 syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED);
238 mSyncBroadcastReceiver = new SyncBroadcastReceiver();
239 registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
240
241 Log_OC.d(TAG, "onResume() end");
242 }
243
244 @Override
245 protected void onPause() {
246 Log_OC.e(TAG, "onPause() start");
247 if (mSyncBroadcastReceiver != null) {
248 unregisterReceiver(mSyncBroadcastReceiver);
249 //LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadcastReceiver);
250 mSyncBroadcastReceiver = null;
251 }
252
253 Log_OC.d(TAG, "onPause() end");
254 super.onPause();
255 }
256
257 @Override
258 public boolean onCreateOptionsMenu(Menu menu) {
259 MenuInflater inflater = getMenuInflater();
260 inflater.inflate(R.menu.main_menu, menu);
261 menu.findItem(R.id.action_upload).setVisible(false);
262 menu.findItem(R.id.action_sort).setVisible(false);
263 return true;
264 }
265
266 @Override
267 public boolean onOptionsItemSelected(MenuItem item) {
268 boolean retval = true;
269 switch (item.getItemId()) {
270 case R.id.action_create_dir: {
271 CreateFolderDialogFragment dialog =
272 CreateFolderDialogFragment.newInstance(getCurrentFolder());
273 dialog.show(
274 getSupportFragmentManager(),
275 CreateFolderDialogFragment.CREATE_FOLDER_FRAGMENT
276 );
277 break;
278 }
279 case android.R.id.home: {
280 OCFile currentDir = getCurrentFolder();
281 if(currentDir != null && currentDir.getParentId() != 0) {
282 onBackPressed();
283 }
284 break;
285 }
286 default:
287 retval = super.onOptionsItemSelected(item);
288 }
289 return retval;
290 }
291
292 protected OCFile getCurrentFolder() {
293 OCFile file = getFile();
294 if (file != null) {
295 if (file.isFolder()) {
296 return file;
297 } else if (getStorageManager() != null) {
298 String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
299 return getStorageManager().getFileByPath(parentPath);
300 }
301 }
302 return null;
303 }
304
305 protected void refreshListOfFilesFragment() {
306 OCFileListFragment fileListFragment = getListOfFilesFragment();
307 if (fileListFragment != null) {
308 fileListFragment.listDirectory();
309 // TODO Enable when "On Device" is recovered ?
310 // fileListFragment.listDirectory(false);
311 }
312 }
313
314 public void browseToRoot() {
315 OCFileListFragment listOfFiles = getListOfFilesFragment();
316 if (listOfFiles != null) { // should never be null, indeed
317 OCFile root = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
318 listOfFiles.listDirectory(root);
319 // TODO Enable when "On Device" is recovered ?
320 // listOfFiles.listDirectory(root, false);
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 Parcelable targetFile = i.getParcelableExtra(FolderPickerActivity.EXTRA_FILE);
371
372 Intent data = new Intent();
373 data.putExtra(EXTRA_FOLDER, getCurrentFolder());
374 if (targetFile != null) {
375 data.putExtra(EXTRA_FILE, targetFile);
376 }
377 setResult(RESULT_OK, data);
378 finish();
379 }
380 }
381
382
383 @Override
384 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
385 super.onRemoteOperationFinish(operation, result);
386
387 if (operation instanceof CreateFolderOperation) {
388 onCreateFolderOperationFinish((CreateFolderOperation)operation, result);
389
390 }
391 }
392
393
394 /**
395 * Updates the view associated to the activity after the finish of an operation trying
396 * to create a new folder.
397 *
398 * @param operation Creation operation performed.
399 * @param result Result of the creation.
400 */
401 private void onCreateFolderOperationFinish(
402 CreateFolderOperation operation, RemoteOperationResult result
403 ) {
404
405 if (result.isSuccess()) {
406 dismissLoadingDialog();
407 refreshListOfFilesFragment();
408 } else {
409 dismissLoadingDialog();
410 try {
411 Toast msg = Toast.makeText(FolderPickerActivity.this,
412 ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
413 Toast.LENGTH_LONG);
414 msg.show();
415
416 } catch (NotFoundException e) {
417 Log_OC.e(TAG, "Error while trying to show fail message " , e);
418 }
419 }
420 }
421
422
423
424 private class SyncBroadcastReceiver extends BroadcastReceiver {
425
426 /**
427 * {@link BroadcastReceiver} to enable syncing feedback in UI
428 */
429 @Override
430 public void onReceive(Context context, Intent intent) {
431 try {
432 String event = intent.getAction();
433 Log_OC.d(TAG, "Received broadcast " + event);
434 String accountName = intent.getStringExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME);
435 String synchFolderRemotePath = intent.getStringExtra(FileSyncAdapter.EXTRA_FOLDER_PATH);
436 RemoteOperationResult synchResult = (RemoteOperationResult)intent.
437 getSerializableExtra(FileSyncAdapter.EXTRA_RESULT);
438 boolean sameAccount = (getAccount() != null &&
439 accountName.equals(getAccount().name) && getStorageManager() != null);
440
441 if (sameAccount) {
442
443 if (FileSyncAdapter.EVENT_FULL_SYNC_START.equals(event)) {
444 mSyncInProgress = true;
445
446 } else {
447 OCFile currentFile = (getFile() == null) ? null :
448 getStorageManager().getFileByPath(getFile().getRemotePath());
449 OCFile currentDir = (getCurrentFolder() == null) ? null :
450 getStorageManager().getFileByPath(getCurrentFolder().getRemotePath());
451
452 if (currentDir == null) {
453 // current folder was removed from the server
454 Toast.makeText( FolderPickerActivity.this,
455 String.format(
456 getString(R.string.sync_current_folder_was_removed),
457 getCurrentFolder().getFileName()),
458 Toast.LENGTH_LONG)
459 .show();
460 browseToRoot();
461
462 } else {
463 if (currentFile == null && !getFile().isFolder()) {
464 // currently selected file was removed in the server, and now we know it
465 currentFile = currentDir;
466 }
467
468 if (synchFolderRemotePath != null && currentDir.getRemotePath().
469 equals(synchFolderRemotePath)) {
470 OCFileListFragment fileListFragment = getListOfFilesFragment();
471 if (fileListFragment != null) {
472 fileListFragment.listDirectory(currentDir);
473 // TODO Enable when "On Device" is recovered ?
474 // fileListFragment.listDirectory(currentDir, false);
475 }
476 }
477 setFile(currentFile);
478 }
479
480 mSyncInProgress = (!FileSyncAdapter.EVENT_FULL_SYNC_END.equals(event) &&
481 !RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED.equals(event));
482
483 if (RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED.
484 equals(event) &&
485 /// TODO refactor and make common
486 synchResult != null && !synchResult.isSuccess() &&
487 (synchResult.getCode() == ResultCode.UNAUTHORIZED ||
488 synchResult.isIdPRedirection() ||
489 (synchResult.isException() && synchResult.getException()
490 instanceof AuthenticatorException))) {
491
492 try {
493 OwnCloudClient client;
494 OwnCloudAccount ocAccount =
495 new OwnCloudAccount(getAccount(), context);
496 client = (OwnCloudClientManagerFactory.getDefaultSingleton().
497 removeClientFor(ocAccount));
498
499 if (client != null) {
500 OwnCloudCredentials cred = client.getCredentials();
501 if (cred != null) {
502 AccountManager am = AccountManager.get(context);
503 if (cred.authTokenExpires()) {
504 am.invalidateAuthToken(
505 getAccount().type,
506 cred.getAuthToken()
507 );
508 } else {
509 am.clearPassword(getAccount());
510 }
511 }
512 }
513 requestCredentialsUpdate();
514
515 } catch (AccountNotFoundException e) {
516 Log_OC.e(TAG, "Account " + getAccount() + " was removed!", e);
517 }
518
519 }
520 }
521 removeStickyBroadcast(intent);
522 Log_OC.d(TAG, "Setting progress visibility to " + mSyncInProgress);
523 setSupportProgressBarIndeterminateVisibility(mSyncInProgress /*|| mRefreshSharesInProgress*/);
524
525 setBackgroundText();
526
527 }
528
529 } catch (RuntimeException e) {
530 // avoid app crashes after changing the serial id of RemoteOperationResult
531 // in owncloud library with broadcast notifications pending to process
532 removeStickyBroadcast(intent);
533 }
534 }
535 }
536
537
538
539 /**
540 * Shows the information of the {@link OCFile} received as a
541 * parameter in the second fragment.
542 *
543 * @param file {@link OCFile} whose details will be shown
544 */
545 @Override
546 public void showDetails(OCFile file) {
547
548 }
549
550 /**
551 * {@inheritDoc}
552 */
553 @Override
554 public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
555
556 }
557
558 @Override
559 public void onRefresh() {
560 refreshList(true);
561 }
562
563 @Override
564 public void onRefresh(boolean enforced) {
565 refreshList(enforced);
566 }
567
568 private void refreshList(boolean ignoreETag) {
569 OCFileListFragment listOfFiles = getListOfFilesFragment();
570 if (listOfFiles != null) {
571 OCFile folder = listOfFiles.getCurrentFile();
572 if (folder != null) {
573 startSyncFolderOperation(folder, ignoreETag);
574 }
575 }
576 }
577 }