1 /* ownCloud Android client application
2 * Copyright (C) 2012-2014 ownCloud Inc.
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.
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.
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/>.
18 package com
.owncloud
.android
.ui
.activity
;
20 import java
.io
.IOException
;
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
;
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
.FileDataStorageManager
;
48 import com
.owncloud
.android
.datamodel
.OCFile
;
49 import com
.owncloud
.android
.lib
.common
.OwnCloudAccount
;
50 import com
.owncloud
.android
.lib
.common
.OwnCloudClient
;
51 import com
.owncloud
.android
.lib
.common
.OwnCloudClientManagerFactory
;
52 import com
.owncloud
.android
.lib
.common
.OwnCloudCredentials
;
53 import com
.owncloud
.android
.lib
.common
.accounts
.AccountUtils
.AccountNotFoundException
;
54 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperation
;
55 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
;
56 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
.ResultCode
;
57 import com
.owncloud
.android
.operations
.CreateFolderOperation
;
58 import com
.owncloud
.android
.operations
.SynchronizeFolderOperation
;
59 import com
.owncloud
.android
.syncadapter
.FileSyncAdapter
;
60 import com
.owncloud
.android
.ui
.dialog
.CreateFolderDialogFragment
;
61 import com
.owncloud
.android
.ui
.fragment
.FileFragment
;
62 import com
.owncloud
.android
.ui
.fragment
.OCFileListFragment
;
63 import com
.owncloud
.android
.utils
.DisplayUtils
;
64 import com
.owncloud
.android
.utils
.ErrorMessageAdapter
;
65 import com
.owncloud
.android
.utils
.Log_OC
;
67 public class MoveActivity
extends HookActivity
implements FileFragment
.ContainerActivity
,
68 OnClickListener
, SwipeRefreshLayout
.OnRefreshListener
{
70 private SyncBroadcastReceiver mSyncBroadcastReceiver
;
72 private static final String TAG
= MoveActivity
.class.getSimpleName();
74 private static final String TAG_LIST_OF_FOLDERS
= "LIST_OF_FOLDERS";
76 private boolean mSyncInProgress
= false
;
78 private Button mCancelBtn
;
79 private Button mChooseBtn
;
83 protected void onCreate(Bundle savedInstanceState
) {
84 Log_OC
.d(TAG
, "onCreate() start");
85 requestWindowFeature(Window
.FEATURE_INDETERMINATE_PROGRESS
);
87 super.onCreate(savedInstanceState
);
89 setContentView(R
.layout
.files_move
);
91 if (savedInstanceState
== null
) {
95 // sets callback listeners for UI elements
99 ActionBar actionBar
= getSupportActionBar();
100 actionBar
.setDisplayShowTitleEnabled(true
);
101 actionBar
.setNavigationMode(ActionBar
.NAVIGATION_MODE_STANDARD
);
102 setSupportProgressBarIndeterminateVisibility(mSyncInProgress
);
103 // always AFTER setContentView(...) ; to work around bug in its implementation
105 // sets message for empty list of folders
108 Log_OC
.d(TAG
, "onCreate() end");
113 protected void onStart() {
115 getSupportActionBar().setIcon(DisplayUtils
.getSeasonalIconId());
119 protected void onDestroy() {
124 * Called when the ownCloud {@link Account} associated to the Activity was just updated.
127 protected void onAccountSet(boolean stateWasRecovered
) {
128 super.onAccountSet(stateWasRecovered
);
129 if (getAccount() != null
) {
133 OCFile folder
= getFile();
134 if (folder
== null
|| !folder
.isFolder()) {
135 // fall back to root folder
136 setFile(getStorageManager().getFileByPath(OCFile
.ROOT_PATH
));
140 if (!stateWasRecovered
) {
141 OCFileListFragment listOfFolders
= getListOfFilesFragment();
142 listOfFolders
.listDirectory(folder
);
144 startSyncFolderOperation(folder
);
147 updateNavigationElementsInActionBar();
151 private void createFragments() {
152 OCFileListFragment listOfFiles
= new OCFileListFragment();
153 Bundle args
= new Bundle();
154 args
.putBoolean(OCFileListFragment
.ARG_JUST_FOLDERS
, true
);
155 args
.putBoolean(OCFileListFragment
.ARG_ALLOW_CONTEXTUAL_ACTIONS
, false
);
156 listOfFiles
.setArguments(args
);
157 FragmentTransaction transaction
= getSupportFragmentManager().beginTransaction();
158 transaction
.add(R
.id
.fragment_container
, listOfFiles
, TAG_LIST_OF_FOLDERS
);
159 transaction
.commit();
163 * Show a text message on screen view for notifying user if content is
164 * loading or folder is empty
166 private void setBackgroundText() {
167 OCFileListFragment listFragment
= getListOfFilesFragment();
168 if (listFragment
!= null
) {
169 int message
= R
.string
.file_list_loading
;
170 if (!mSyncInProgress
) {
171 // In case folder list is empty
172 message
= R
.string
.file_list_empty_moving
;
174 listFragment
.setMessageForEmptyList(getString(message
));
176 Log
.e(TAG
, "OCFileListFragment is null");
180 private OCFileListFragment
getListOfFilesFragment() {
181 Fragment listOfFiles
= getSupportFragmentManager().findFragmentByTag(MoveActivity
.TAG_LIST_OF_FOLDERS
);
182 if (listOfFiles
!= null
) {
183 return (OCFileListFragment
)listOfFiles
;
185 Log_OC
.wtf(TAG
, "Access to unexisting list of files fragment!!");
193 * Updates action bar and second fragment, if in dual pane mode.
196 public void onBrowsedDownTo(OCFile directory
) {
198 updateNavigationElementsInActionBar();
200 startSyncFolderOperation(directory
);
205 public void startSyncFolderOperation(OCFile folder
) {
206 long currentSyncTime
= System
.currentTimeMillis();
208 mSyncInProgress
= true
;
210 // perform folder synchronization
211 RemoteOperation synchFolderOp
= new SynchronizeFolderOperation( folder
,
214 getFileOperationsHelper().isSharedSupported(),
217 getApplicationContext()
219 synchFolderOp
.execute(getAccount(), this, null
, null
);
221 setSupportProgressBarIndeterminateVisibility(true
);
227 protected void onResume() {
229 Log_OC
.e(TAG
, "onResume() start");
231 // refresh list of files
232 refreshListOfFilesFragment();
234 // Listen for sync messages
235 IntentFilter syncIntentFilter
= new IntentFilter(FileSyncAdapter
.EVENT_FULL_SYNC_START
);
236 syncIntentFilter
.addAction(FileSyncAdapter
.EVENT_FULL_SYNC_END
);
237 syncIntentFilter
.addAction(FileSyncAdapter
.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED
);
238 syncIntentFilter
.addAction(SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED
);
239 syncIntentFilter
.addAction(SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_SHARES_SYNCED
);
240 mSyncBroadcastReceiver
= new SyncBroadcastReceiver();
241 registerReceiver(mSyncBroadcastReceiver
, syncIntentFilter
);
243 Log_OC
.d(TAG
, "onResume() end");
247 protected void onPause() {
248 Log_OC
.e(TAG
, "onPause() start");
249 if (mSyncBroadcastReceiver
!= null
) {
250 unregisterReceiver(mSyncBroadcastReceiver
);
251 //LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadcastReceiver);
252 mSyncBroadcastReceiver
= null
;
255 Log_OC
.d(TAG
, "onPause() end");
260 public boolean onCreateOptionsMenu(Menu menu
) {
261 MenuInflater inflater
= getSherlock().getMenuInflater();
262 inflater
.inflate(R
.menu
.main_menu
, menu
);
263 menu
.findItem(R
.id
.action_upload
).setVisible(false
);
264 menu
.findItem(R
.id
.action_settings
).setVisible(false
);
265 menu
.findItem(R
.id
.action_sync_account
).setVisible(false
);
270 public boolean onOptionsItemSelected(MenuItem item
) {
271 boolean retval
= true
;
272 switch (item
.getItemId()) {
273 case R
.id
.action_create_dir
: {
274 CreateFolderDialogFragment dialog
=
275 CreateFolderDialogFragment
.newInstance(getCurrentFolder());
277 getSupportFragmentManager(),
278 CreateFolderDialogFragment
.CREATE_FOLDER_FRAGMENT
282 case android
.R
.id
.home
: {
283 OCFile currentDir
= getCurrentFolder();
284 if(currentDir
!= null
&& currentDir
.getParentId() != 0) {
290 retval
= super.onOptionsItemSelected(item
);
295 private OCFile
getCurrentFolder() {
296 OCFile file
= getFile();
298 if (file
.isFolder()) {
300 } else if (getStorageManager() != null
) {
301 String parentPath
= file
.getRemotePath().substring(0, file
.getRemotePath().lastIndexOf(file
.getFileName()));
302 return getStorageManager().getFileByPath(parentPath
);
308 protected void refreshListOfFilesFragment() {
309 OCFileListFragment fileListFragment
= getListOfFilesFragment();
310 if (fileListFragment
!= null
) {
311 fileListFragment
.listDirectory();
315 public void browseToRoot() {
316 OCFileListFragment listOfFiles
= getListOfFilesFragment();
317 if (listOfFiles
!= null
) { // should never be null, indeed
318 OCFile root
= getStorageManager().getFileByPath(OCFile
.ROOT_PATH
);
319 listOfFiles
.listDirectory(root
);
320 setFile(listOfFiles
.getCurrentFile());
321 updateNavigationElementsInActionBar();
322 startSyncFolderOperation(root
);
327 public void onBackPressed() {
328 OCFileListFragment listOfFiles
= getListOfFilesFragment();
329 if (listOfFiles
!= null
) { // should never be null, indeed
330 int levelsUp
= listOfFiles
.onBrowseUp();
335 setFile(listOfFiles
.getCurrentFile());
336 updateNavigationElementsInActionBar();
340 private void updateNavigationElementsInActionBar() {
341 ActionBar actionBar
= getSupportActionBar();
342 OCFile currentDir
= getCurrentFolder();
343 boolean atRoot
= (currentDir
== null
|| currentDir
.getParentId() == 0);
344 actionBar
.setDisplayHomeAsUpEnabled(!atRoot
);
345 actionBar
.setHomeButtonEnabled(!atRoot
);
348 ?
getString(R
.string
.default_display_name_for_root_folder
)
349 : currentDir
.getFileName()
354 * Set per-view controllers
356 private void initControls(){
357 mCancelBtn
= (Button
) findViewById(R
.id
.move_files_btn_cancel
);
358 mCancelBtn
.setOnClickListener(this);
359 mChooseBtn
= (Button
) findViewById(R
.id
.move_files_btn_choose
);
360 mChooseBtn
.setOnClickListener(this);
364 public void onClick(View v
) {
365 if (v
== mCancelBtn
) {
367 } else if (v
== mChooseBtn
) {
368 ComponentsGetter cg
= (ComponentsGetter
)getSherlockActivity();
369 FileDataStorageManager storageManager
= cg
.getStorageManager();
370 if (storageManager
.getFileById(mTargetFile
.getFileId()) != null
) {
371 cg
.getFileOperationsHelper().removeFile(mTargetFile
, false
);
373 cg
.getFileOperationsHelper
.moveFile(m
)
379 public void onRemoteOperationFinish(RemoteOperation operation
, RemoteOperationResult result
) {
380 super.onRemoteOperationFinish(operation
, result
);
382 if (operation
instanceof CreateFolderOperation
) {
383 onCreateFolderOperationFinish((CreateFolderOperation
)operation
, result
);
390 * Updates the view associated to the activity after the finish of an operation trying
391 * to create a new folder.
393 * @param operation Creation operation performed.
394 * @param result Result of the creation.
396 private void onCreateFolderOperationFinish(
397 CreateFolderOperation operation
, RemoteOperationResult result
400 if (result
.isSuccess()) {
401 dismissLoadingDialog();
402 refreshListOfFilesFragment();
404 dismissLoadingDialog();
406 Toast msg
= Toast
.makeText(MoveActivity
.this,
407 ErrorMessageAdapter
.getErrorCauseMessage(result
, operation
, getResources()),
411 } catch (NotFoundException e
) {
412 Log_OC
.e(TAG
, "Error while trying to show fail message " , e
);
419 private class SyncBroadcastReceiver
extends BroadcastReceiver
{
422 * {@link BroadcastReceiver} to enable syncing feedback in UI
425 public void onReceive(Context context
, Intent intent
) {
427 String event
= intent
.getAction();
428 Log_OC
.d(TAG
, "Received broadcast " + event
);
429 String accountName
= intent
.getStringExtra(FileSyncAdapter
.EXTRA_ACCOUNT_NAME
);
430 String synchFolderRemotePath
= intent
.getStringExtra(FileSyncAdapter
.EXTRA_FOLDER_PATH
);
431 RemoteOperationResult synchResult
= (RemoteOperationResult
)intent
.getSerializableExtra(FileSyncAdapter
.EXTRA_RESULT
);
432 boolean sameAccount
= (getAccount() != null
&& accountName
.equals(getAccount().name
) && getStorageManager() != null
);
436 if (FileSyncAdapter
.EVENT_FULL_SYNC_START
.equals(event
)) {
437 mSyncInProgress
= true
;
440 OCFile currentFile
= (getFile() == null
) ? null
: getStorageManager().getFileByPath(getFile().getRemotePath());
441 OCFile currentDir
= (getCurrentFolder() == null
) ? null
: getStorageManager().getFileByPath(getCurrentFolder().getRemotePath());
443 if (currentDir
== null
) {
444 // current folder was removed from the server
445 Toast
.makeText( MoveActivity
.this,
446 String
.format(getString(R
.string
.sync_current_folder_was_removed
), "PLACEHOLDER"),
452 if (currentFile
== null
&& !getFile().isFolder()) {
453 // currently selected file was removed in the server, and now we know it
454 currentFile
= currentDir
;
457 if (synchFolderRemotePath
!= null
&& currentDir
.getRemotePath().equals(synchFolderRemotePath
)) {
458 OCFileListFragment fileListFragment
= getListOfFilesFragment();
459 if (fileListFragment
!= null
) {
460 fileListFragment
.listDirectory(currentDir
);
463 setFile(currentFile
);
466 mSyncInProgress
= (!FileSyncAdapter
.EVENT_FULL_SYNC_END
.equals(event
) && !SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_SHARES_SYNCED
.equals(event
));
468 if (SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED
.
470 /// TODO refactor and make common
471 synchResult
!= null
&& !synchResult
.isSuccess() &&
472 (synchResult
.getCode() == ResultCode
.UNAUTHORIZED
||
473 synchResult
.isIdPRedirection() ||
474 (synchResult
.isException() && synchResult
.getException()
475 instanceof AuthenticatorException
))) {
477 OwnCloudClient client
= null
;
479 OwnCloudAccount ocAccount
=
480 new OwnCloudAccount(getAccount(), context
);
481 client
= (OwnCloudClientManagerFactory
.getDefaultSingleton().
482 removeClientFor(ocAccount
));
483 // TODO get rid of these exceptions
484 } catch (AccountNotFoundException e
) {
486 } catch (AuthenticatorException e
) {
488 } catch (OperationCanceledException e
) {
490 } catch (IOException e
) {
494 if (client
!= null
) {
495 OwnCloudCredentials cred
= client
.getCredentials();
497 AccountManager am
= AccountManager
.get(context
);
498 if (cred
.authTokenExpires()) {
499 am
.invalidateAuthToken(
504 am
.clearPassword(getAccount());
509 requestCredentialsUpdate();
513 removeStickyBroadcast(intent
);
514 Log_OC
.d(TAG
, "Setting progress visibility to " + mSyncInProgress
);
515 setSupportProgressBarIndeterminateVisibility(mSyncInProgress
/*|| mRefreshSharesInProgress*/);
521 } catch (RuntimeException e
) {
522 // avoid app crashes after changing the serial id of RemoteOperationResult
523 // in owncloud library with broadcast notifications pending to process
524 removeStickyBroadcast(intent
);
532 * Shows the information of the {@link OCFile} received as a
533 * parameter in the second fragment.
535 * @param file {@link OCFile} whose details will be shown
538 public void showDetails(OCFile file
) {
546 public void onTransferStateChanged(OCFile file
, boolean downloading
, boolean uploading
) {
552 public void onRefresh() {
553 OCFileListFragment listOfFiles
= getListOfFilesFragment();
554 if (listOfFiles
!= null
) {
555 OCFile folder
= listOfFiles
.getCurrentFile();
556 if (folder
!= null
) {
557 startSyncFolderOperation(folder
);