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