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