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
.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
.lib
.common
.utils
.Log_OC
; 
  66 public class MoveActivity 
extends HookActivity 
implements FileFragment
.ContainerActivity
,  
  67     OnClickListener
, SwipeRefreshLayout
.OnRefreshListener 
{ 
  69     public static final String EXTRA_CURRENT_FOLDER 
= UploadFilesActivity
.class.getCanonicalName() + ".EXTRA_CURRENT_FOLDER"; 
  70     public static final String EXTRA_TARGET_FILE 
= UploadFilesActivity
.class.getCanonicalName() + "EXTRA_TARGET_FILE"; 
  72     public static final int RESULT_OK_AND_MOVE 
= 1; 
  74     private SyncBroadcastReceiver mSyncBroadcastReceiver
; 
  76     private static final String TAG 
= MoveActivity
.class.getSimpleName(); 
  78     private static final String TAG_LIST_OF_FOLDERS 
= "LIST_OF_FOLDERS"; 
  80     private boolean mSyncInProgress 
= false
; 
  82     private Button mCancelBtn
; 
  83     private Button mChooseBtn
; 
  87     protected void onCreate(Bundle savedInstanceState
) { 
  88         Log_OC
.d(TAG
, "onCreate() start"); 
  89         requestWindowFeature(Window
.FEATURE_INDETERMINATE_PROGRESS
); 
  91         super.onCreate(savedInstanceState
);  
  93         setContentView(R
.layout
.files_move
); 
  95         if (savedInstanceState 
== null
) { 
  99         // sets callback listeners for UI elements 
 103         ActionBar actionBar 
= getSupportActionBar(); 
 104         actionBar
.setDisplayShowTitleEnabled(true
); 
 105         actionBar
.setNavigationMode(ActionBar
.NAVIGATION_MODE_STANDARD
); 
 106         setSupportProgressBarIndeterminateVisibility(mSyncInProgress
); 
 107             // always AFTER setContentView(...) ; to work around bug in its implementation 
 109         // sets message for empty list of folders 
 112         Log_OC
.d(TAG
, "onCreate() end"); 
 117     protected void onStart() { 
 119         getSupportActionBar().setIcon(DisplayUtils
.getSeasonalIconId()); 
 123     protected void onDestroy() { 
 128      *  Called when the ownCloud {@link Account} associated to the Activity was just updated. 
 131     protected void onAccountSet(boolean stateWasRecovered
) { 
 132         super.onAccountSet(stateWasRecovered
); 
 133         if (getAccount() != null
) { 
 137             OCFile folder 
= getFile(); 
 138             if (folder 
== null 
|| !folder
.isFolder()) { 
 139                 // fall back to root folder 
 140                 setFile(getStorageManager().getFileByPath(OCFile
.ROOT_PATH
)); 
 144             if (!stateWasRecovered
) { 
 145                 OCFileListFragment listOfFolders 
= getListOfFilesFragment();  
 146                 listOfFolders
.listDirectory(folder
);    
 148                 startSyncFolderOperation(folder
); 
 151             updateNavigationElementsInActionBar(); 
 155     private void createFragments() { 
 156         OCFileListFragment listOfFiles 
= new OCFileListFragment(); 
 157         Bundle args 
= new Bundle(); 
 158         args
.putBoolean(OCFileListFragment
.ARG_JUST_FOLDERS
, true
); 
 159         args
.putBoolean(OCFileListFragment
.ARG_ALLOW_CONTEXTUAL_ACTIONS
, false
); 
 160         listOfFiles
.setArguments(args
); 
 161         FragmentTransaction transaction 
= getSupportFragmentManager().beginTransaction(); 
 162         transaction
.add(R
.id
.fragment_container
, listOfFiles
, TAG_LIST_OF_FOLDERS
); 
 163         transaction
.commit(); 
 167      * Show a text message on screen view for notifying user if content is 
 168      * loading or folder is empty 
 170     private void setBackgroundText() { 
 171         OCFileListFragment listFragment 
= getListOfFilesFragment(); 
 172         if (listFragment 
!= null
) { 
 173             int message 
= R
.string
.file_list_loading
; 
 174             if (!mSyncInProgress
) { 
 175                 // In case folder list is empty 
 176                 message 
= R
.string
.file_list_empty_moving
; 
 178             listFragment
.setMessageForEmptyList(getString(message
)); 
 180             Log
.e(TAG
, "OCFileListFragment is null"); 
 184     private OCFileListFragment 
getListOfFilesFragment() { 
 185         Fragment listOfFiles 
= getSupportFragmentManager().findFragmentByTag(MoveActivity
.TAG_LIST_OF_FOLDERS
); 
 186         if (listOfFiles 
!= null
) { 
 187             return (OCFileListFragment
)listOfFiles
; 
 189         Log_OC
.wtf(TAG
, "Access to unexisting list of files fragment!!"); 
 197      * Updates action bar and second fragment, if in dual pane mode. 
 200     public void onBrowsedDownTo(OCFile directory
) { 
 202         updateNavigationElementsInActionBar(); 
 204         startSyncFolderOperation(directory
); 
 209     public void startSyncFolderOperation(OCFile folder
) { 
 210         long currentSyncTime 
= System
.currentTimeMillis();  
 212         mSyncInProgress 
= true
; 
 214         // perform folder synchronization 
 215         RemoteOperation synchFolderOp 
= new SynchronizeFolderOperation( folder
,   
 218                                                                         getFileOperationsHelper().isSharedSupported(), 
 221                                                                         getApplicationContext() 
 223         synchFolderOp
.execute(getAccount(), this, null
, null
); 
 225         setSupportProgressBarIndeterminateVisibility(true
); 
 231     protected void onResume() { 
 233         Log_OC
.e(TAG
, "onResume() start"); 
 235         // refresh list of files 
 236         refreshListOfFilesFragment(); 
 238         // Listen for sync messages 
 239         IntentFilter syncIntentFilter 
= new IntentFilter(FileSyncAdapter
.EVENT_FULL_SYNC_START
); 
 240         syncIntentFilter
.addAction(FileSyncAdapter
.EVENT_FULL_SYNC_END
); 
 241         syncIntentFilter
.addAction(FileSyncAdapter
.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED
); 
 242         syncIntentFilter
.addAction(SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED
); 
 243         syncIntentFilter
.addAction(SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_SHARES_SYNCED
); 
 244         mSyncBroadcastReceiver 
= new SyncBroadcastReceiver(); 
 245         registerReceiver(mSyncBroadcastReceiver
, syncIntentFilter
); 
 247         Log_OC
.d(TAG
, "onResume() end"); 
 251     protected void onPause() { 
 252         Log_OC
.e(TAG
, "onPause() start"); 
 253         if (mSyncBroadcastReceiver 
!= null
) { 
 254             unregisterReceiver(mSyncBroadcastReceiver
); 
 255             //LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadcastReceiver); 
 256             mSyncBroadcastReceiver 
= null
; 
 259         Log_OC
.d(TAG
, "onPause() end"); 
 264     public boolean onCreateOptionsMenu(Menu menu
) { 
 265         MenuInflater inflater 
= getSherlock().getMenuInflater(); 
 266         inflater
.inflate(R
.menu
.main_menu
, menu
); 
 267         menu
.findItem(R
.id
.action_upload
).setVisible(false
); 
 268         menu
.findItem(R
.id
.action_settings
).setVisible(false
); 
 269         menu
.findItem(R
.id
.action_sync_account
).setVisible(false
); 
 274     public boolean onOptionsItemSelected(MenuItem item
) { 
 275         boolean retval 
= true
; 
 276         switch (item
.getItemId()) { 
 277         case R
.id
.action_create_dir
: { 
 278             CreateFolderDialogFragment dialog 
=  
 279                     CreateFolderDialogFragment
.newInstance(getCurrentFolder()); 
 281                     getSupportFragmentManager(),  
 282                     CreateFolderDialogFragment
.CREATE_FOLDER_FRAGMENT
 
 286         case android
.R
.id
.home
: { 
 287             OCFile currentDir 
= getCurrentFolder(); 
 288             if(currentDir 
!= null 
&& currentDir
.getParentId() != 0) { 
 294             retval 
= super.onOptionsItemSelected(item
); 
 299     private OCFile 
getCurrentFolder() { 
 300         OCFile file 
= getFile(); 
 302             if (file
.isFolder()) { 
 304             } else if (getStorageManager() != null
) { 
 305                 String parentPath 
= file
.getRemotePath().substring(0, file
.getRemotePath().lastIndexOf(file
.getFileName())); 
 306                 return getStorageManager().getFileByPath(parentPath
); 
 312     protected void refreshListOfFilesFragment() { 
 313         OCFileListFragment fileListFragment 
= getListOfFilesFragment(); 
 314         if (fileListFragment 
!= null
) {  
 315             fileListFragment
.listDirectory(); 
 319     public void browseToRoot() { 
 320         OCFileListFragment listOfFiles 
= getListOfFilesFragment();  
 321         if (listOfFiles 
!= null
) {  // should never be null, indeed 
 322             OCFile root 
= getStorageManager().getFileByPath(OCFile
.ROOT_PATH
); 
 323             listOfFiles
.listDirectory(root
); 
 324             setFile(listOfFiles
.getCurrentFile()); 
 325             updateNavigationElementsInActionBar(); 
 326             startSyncFolderOperation(root
); 
 331     public void onBackPressed() { 
 332         OCFileListFragment listOfFiles 
= getListOfFilesFragment(); 
 333         if (listOfFiles 
!= null
) {  // should never be null, indeed 
 334             int levelsUp 
= listOfFiles
.onBrowseUp(); 
 339             setFile(listOfFiles
.getCurrentFile()); 
 340             updateNavigationElementsInActionBar(); 
 344     private void updateNavigationElementsInActionBar() { 
 345         ActionBar actionBar 
= getSupportActionBar(); 
 346         OCFile currentDir 
= getCurrentFolder(); 
 347         boolean atRoot 
= (currentDir 
== null 
|| currentDir
.getParentId() == 0); 
 348         actionBar
.setDisplayHomeAsUpEnabled(!atRoot
); 
 349         actionBar
.setHomeButtonEnabled(!atRoot
); 
 352                 ? 
getString(R
.string
.default_display_name_for_root_folder
)  
 353                 : currentDir
.getFileName() 
 358      * Set per-view controllers 
 360     private void initControls(){ 
 361         mCancelBtn 
= (Button
) findViewById(R
.id
.move_files_btn_cancel
); 
 362         mCancelBtn
.setOnClickListener(this); 
 363         mChooseBtn 
= (Button
) findViewById(R
.id
.move_files_btn_choose
); 
 364         mChooseBtn
.setOnClickListener(this); 
 368     public void onClick(View v
) { 
 369         if (v 
== mCancelBtn
) { 
 371         } else if (v 
== mChooseBtn
) { 
 372             Intent i 
= getIntent(); 
 373             OCFile targetFile 
= (OCFile
) i
.getParcelableExtra(MoveActivity
.EXTRA_TARGET_FILE
); 
 375             Intent data 
= new Intent(); 
 376             data
.putExtra(EXTRA_CURRENT_FOLDER
, getCurrentFolder()); 
 377             data
.putExtra(EXTRA_TARGET_FILE
, targetFile
); 
 378             setResult(RESULT_OK_AND_MOVE
, data
); 
 385     public void onRemoteOperationFinish(RemoteOperation operation
, RemoteOperationResult result
) { 
 386         super.onRemoteOperationFinish(operation
, result
); 
 388         if (operation 
instanceof CreateFolderOperation
) { 
 389             onCreateFolderOperationFinish((CreateFolderOperation
)operation
, result
); 
 396      * Updates the view associated to the activity after the finish of an operation trying  
 397      * to create a new folder. 
 399      * @param operation     Creation operation performed. 
 400      * @param result        Result of the creation. 
 402     private void onCreateFolderOperationFinish( 
 403             CreateFolderOperation operation
, RemoteOperationResult result
 
 406         if (result
.isSuccess()) { 
 407             dismissLoadingDialog(); 
 408             refreshListOfFilesFragment(); 
 410             dismissLoadingDialog(); 
 412                 Toast msg 
= Toast
.makeText(MoveActivity
.this,  
 413                         ErrorMessageAdapter
.getErrorCauseMessage(result
, operation
, getResources()),  
 417             } catch (NotFoundException e
) { 
 418                 Log_OC
.e(TAG
, "Error while trying to show fail message " , e
); 
 425     private class SyncBroadcastReceiver 
extends BroadcastReceiver 
{ 
 428          * {@link BroadcastReceiver} to enable syncing feedback in UI 
 431         public void onReceive(Context context
, Intent intent
) { 
 433                 String event 
= intent
.getAction(); 
 434                 Log_OC
.d(TAG
, "Received broadcast " + event
); 
 435                 String accountName 
= intent
.getStringExtra(FileSyncAdapter
.EXTRA_ACCOUNT_NAME
); 
 436                 String synchFolderRemotePath 
= intent
.getStringExtra(FileSyncAdapter
.EXTRA_FOLDER_PATH
);  
 437                 RemoteOperationResult synchResult 
= (RemoteOperationResult
)intent
.getSerializableExtra(FileSyncAdapter
.EXTRA_RESULT
); 
 438                 boolean sameAccount 
= (getAccount() != null 
&& accountName
.equals(getAccount().name
) && getStorageManager() != null
);  
 442                     if (FileSyncAdapter
.EVENT_FULL_SYNC_START
.equals(event
)) { 
 443                         mSyncInProgress 
= true
; 
 446                         OCFile currentFile 
= (getFile() == null
) ? null 
: getStorageManager().getFileByPath(getFile().getRemotePath()); 
 447                         OCFile currentDir 
= (getCurrentFolder() == null
) ? null 
: getStorageManager().getFileByPath(getCurrentFolder().getRemotePath()); 
 449                         if (currentDir 
== null
) { 
 450                             // current folder was removed from the server  
 451                             Toast
.makeText( MoveActivity
.this,  
 452                                             String
.format(getString(R
.string
.sync_current_folder_was_removed
), getCurrentFolder().getFileName()),  
 458                             if (currentFile 
== null 
&& !getFile().isFolder()) { 
 459                                 // currently selected file was removed in the server, and now we know it 
 460                                 currentFile 
= currentDir
; 
 463                             if (synchFolderRemotePath 
!= null 
&& currentDir
.getRemotePath().equals(synchFolderRemotePath
)) { 
 464                                 OCFileListFragment fileListFragment 
= getListOfFilesFragment(); 
 465                                 if (fileListFragment 
!= null
) { 
 466                                     fileListFragment
.listDirectory(currentDir
); 
 469                             setFile(currentFile
); 
 472                         mSyncInProgress 
= (!FileSyncAdapter
.EVENT_FULL_SYNC_END
.equals(event
) && !SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_SHARES_SYNCED
.equals(event
)); 
 474                         if (SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED
. 
 476                                 /// TODO refactor and make common 
 477                                 synchResult 
!= null 
&& !synchResult
.isSuccess() &&   
 478                                 (synchResult
.getCode() == ResultCode
.UNAUTHORIZED   
||  
 479                                     synchResult
.isIdPRedirection()                  || 
 480                                     (synchResult
.isException() && synchResult
.getException()  
 481                                             instanceof AuthenticatorException
))) { 
 483                             OwnCloudClient client 
= null
; 
 485                                 OwnCloudAccount ocAccount 
=  
 486                                         new OwnCloudAccount(getAccount(), context
); 
 487                                 client 
= (OwnCloudClientManagerFactory
.getDefaultSingleton(). 
 488                                         removeClientFor(ocAccount
)); 
 489                                 // TODO get rid of these exceptions 
 490                             } catch (AccountNotFoundException e
) { 
 492                             } catch (AuthenticatorException e
) { 
 494                             } catch (OperationCanceledException e
) { 
 496                             } catch (IOException e
) { 
 500                             if (client 
!= null
) { 
 501                                 OwnCloudCredentials cred 
= client
.getCredentials(); 
 503                                     AccountManager am 
= AccountManager
.get(context
); 
 504                                     if (cred
.authTokenExpires()) { 
 505                                         am
.invalidateAuthToken( 
 510                                         am
.clearPassword(getAccount()); 
 515                             requestCredentialsUpdate(); 
 519                     removeStickyBroadcast(intent
); 
 520                     Log_OC
.d(TAG
, "Setting progress visibility to " + mSyncInProgress
); 
 521                     setSupportProgressBarIndeterminateVisibility(mSyncInProgress 
/*|| mRefreshSharesInProgress*/); 
 527             } catch (RuntimeException e
) { 
 528                 // avoid app crashes after changing the serial id of RemoteOperationResult  
 529                 // in owncloud library with broadcast notifications pending to process 
 530                 removeStickyBroadcast(intent
); 
 538      * Shows the information of the {@link OCFile} received as a  
 539      * parameter in the second fragment. 
 541      * @param file          {@link OCFile} whose details will be shown 
 544     public void showDetails(OCFile file
) { 
 552     public void onTransferStateChanged(OCFile file
, boolean downloading
, boolean uploading
) { 
 558     public void onRefresh() { 
 559         OCFileListFragment listOfFiles 
= getListOfFilesFragment(); 
 560         if (listOfFiles 
!= null
) { 
 561             OCFile folder 
= listOfFiles
.getCurrentFile(); 
 562             if (folder 
!= null
) { 
 563                 startSyncFolderOperation(folder
);