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
);