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