1 /* ownCloud Android client application 
   2  *   Copyright (C) 2012 Bartek Przybylski 
   3  *   Copyright (C) 2012-2014 ownCloud Inc. 
   5  *   This program is free software: you can redistribute it and/or modify 
   6  *   it under the terms of the GNU General Public License version 2, 
   7  *   as published by the Free Software Foundation. 
   9  *   This program is distributed in the hope that it will be useful, 
  10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
  11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  12  *   GNU General Public License for more details. 
  14  *   You should have received a copy of the GNU General Public License 
  15  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  19 package com
.owncloud
.android
.operations
; 
  21 import com
.owncloud
.android
.datamodel
.OCFile
; 
  22 import com
.owncloud
.android
.files
.services
.FileDownloader
; 
  23 import com
.owncloud
.android
.files
.services
.FileUploader
; 
  24 import com
.owncloud
.android
.lib
.common
.OwnCloudClient
; 
  25 import com
.owncloud
.android
.lib
.resources
.files
.RemoteFile
; 
  26 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
; 
  27 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
.ResultCode
; 
  28 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
; 
  29 import com
.owncloud
.android
.lib
.resources
.files
.ReadRemoteFileOperation
; 
  30 import com
.owncloud
.android
.operations
.common
.SyncOperation
; 
  31 import com
.owncloud
.android
.utils
.FileStorageUtils
; 
  33 import android
.accounts
.Account
; 
  34 import android
.content
.Context
; 
  35 import android
.content
.Intent
; 
  38  * Remote operation performing the read of remote file in the ownCloud server. 
  40  * @author David A. Velasco 
  44 public class SynchronizeFileOperation 
extends SyncOperation 
{ 
  46     private String TAG 
= SynchronizeFileOperation
.class.getSimpleName(); 
  48     private OCFile mLocalFile
; 
  49     private String mRemotePath
; 
  50     private OCFile mServerFile
; 
  51     private Account mAccount
; 
  52     private boolean mSyncFileContents
; 
  53     private Context mContext
; 
  55     private boolean mTransferWasRequested 
= false
; 
  61      * Uses remotePath to retrieve all the data in local cache and remote server when the operation 
  62      * is executed, instead of reusing {@link OCFile} instances. 
  65      * @param account               ownCloud account holding the file. 
  66      * @param syncFileContents      When 'true', transference of data will be started by the  
  67      *                              operation if needed and no conflict is detected. 
  68      * @param context               Android context; needed to start transfers. 
  70     public SynchronizeFileOperation( 
  73             boolean syncFileContents
, 
  76         mRemotePath 
= remotePath
; 
  80         mSyncFileContents 
= syncFileContents
; 
  86      * Constructor allowing to reuse {@link OCFile} instances just queried from cache or network. 
  88      * Useful for folder / account synchronizations. 
  90      * @param localFile             Data of file currently hold in device cache. MUSTN't be null. 
  91      * @param serverFile            Data of file just retrieved from network. If null, will be 
  92      *                              retrieved from network by the operation when executed. 
  93      * @param account               ownCloud account holding the file. 
  94      * @param syncFileContents      When 'true', transference of data will be started by the  
  95      *                              operation if needed and no conflict is detected. 
  96      * @param context               Android context; needed to start transfers. 
  98     public SynchronizeFileOperation( 
 102             boolean syncFileContents
, 
 105         mLocalFile 
= localFile
; 
 106         mServerFile 
= serverFile
; 
 107         mRemotePath 
= localFile
.getRemotePath(); 
 109         mSyncFileContents 
= syncFileContents
; 
 115     protected RemoteOperationResult 
run(OwnCloudClient client
) { 
 117         RemoteOperationResult result 
= null
; 
 118         mTransferWasRequested 
= false
; 
 120         if (mLocalFile 
== null
) { 
 121             // Get local file from the DB 
 122             mLocalFile 
= getStorageManager().getFileByPath(mRemotePath
); 
 125         if (!mLocalFile
.isDown()) { 
 127             requestForDownload(mLocalFile
); 
 128             result 
= new RemoteOperationResult(ResultCode
.OK
); 
 131             /// local copy in the device -> need to think a bit more before do anything 
 133             if (mServerFile 
== null
) { 
 134                 ReadRemoteFileOperation operation 
= new ReadRemoteFileOperation(mRemotePath
); 
 135                 result 
= operation
.execute(client
); 
 136                 if (result
.isSuccess()){ 
 137                     mServerFile 
= FileStorageUtils
.fillOCFile((RemoteFile
) result
.getData().get(0)); 
 138                     mServerFile
.setLastSyncDateForProperties(System
.currentTimeMillis()); 
 142             if (mServerFile 
!= null
) {    
 144                 /// check changes in server and local file 
 145                 boolean serverChanged 
= false
; 
 146                 /* time for eTag is coming, but not yet 
 147                     if (mServerFile.getEtag() != null) { 
 148                         serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag()));   // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged? 
 150                 // server without etags 
 151                 serverChanged 
= (mServerFile
.getModificationTimestamp() != mLocalFile
.getModificationTimestampAtLastSyncForData()); 
 153                 boolean localChanged 
= (mLocalFile
.getLocalModificationTimestamp() > mLocalFile
.getLastSyncDateForData()); 
 154                 // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads 
 156                 /// decide action to perform depending upon changes 
 157                 //if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) { 
 158                 if (localChanged 
&& serverChanged
) { 
 159                     result 
= new RemoteOperationResult(ResultCode
.SYNC_CONFLICT
); 
 161                 } else if (localChanged
) { 
 162                     if (mSyncFileContents
) { 
 163                         requestForUpload(mLocalFile
); 
 164                         // the local update of file properties will be done by the FileUploader service when the upload finishes 
 166                         // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid;  
 167                         // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect 
 168                         // that an upload is necessary (for instance, in FileObserverService). 
 170                     result 
= new RemoteOperationResult(ResultCode
.OK
); 
 172                 } else if (serverChanged
) { 
 173                     mLocalFile
.setRemoteId(mServerFile
.getRemoteId()); 
 175                     if (mSyncFileContents
) { 
 176                         requestForDownload(mLocalFile
); // local, not server; we won't to keep the value of keepInSync! 
 177                         // the update of local data will be done later by the FileUploader service when the upload finishes 
 179                         // TODO CHECK: is this really useful in some point in the code? 
 180                         mServerFile
.setKeepInSync(mLocalFile
.keepInSync()); 
 181                         mServerFile
.setLastSyncDateForData(mLocalFile
.getLastSyncDateForData()); 
 182                         mServerFile
.setStoragePath(mLocalFile
.getStoragePath()); 
 183                         mServerFile
.setParentId(mLocalFile
.getParentId()); 
 184                         getStorageManager().saveFile(mServerFile
); 
 187                     result 
= new RemoteOperationResult(ResultCode
.OK
); 
 190                     // nothing changed, nothing to do 
 191                     result 
= new RemoteOperationResult(ResultCode
.OK
); 
 198         Log_OC
.i(TAG
, "Synchronizing " + mAccount
.name 
+ ", file " + mLocalFile
.getRemotePath() + ": " + result
.getLogMessage()); 
 205      * Requests for an upload to the FileUploader service 
 207      * @param file     OCFile object representing the file to upload 
 209     private void requestForUpload(OCFile file
) { 
 210         Intent i 
= new Intent(mContext
, FileUploader
.class); 
 211         i
.putExtra(FileUploader
.KEY_ACCOUNT
, mAccount
); 
 212         i
.putExtra(FileUploader
.KEY_FILE
, file
); 
 213         /*i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath);    // doing this we would lose the value of keepInSync in the road, and maybe it's not updated in the database when the FileUploader service gets it!   
 214         i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());*/ 
 215         i
.putExtra(FileUploader
.KEY_UPLOAD_TYPE
, FileUploader
.UPLOAD_SINGLE_FILE
); 
 216         i
.putExtra(FileUploader
.KEY_FORCE_OVERWRITE
, true
); 
 217         mContext
.startService(i
); 
 218         mTransferWasRequested 
= true
; 
 223      * Requests for a download to the FileDownloader service 
 225      * @param file     OCFile object representing the file to download 
 227     private void requestForDownload(OCFile file
) { 
 228         Intent i 
= new Intent(mContext
, FileDownloader
.class); 
 229         i
.putExtra(FileDownloader
.EXTRA_ACCOUNT
, mAccount
); 
 230         i
.putExtra(FileDownloader
.EXTRA_FILE
, file
); 
 231         mContext
.startService(i
); 
 232         mTransferWasRequested 
= true
; 
 236     public boolean transferWasRequested() { 
 237         return mTransferWasRequested
; 
 241     public OCFile 
getLocalFile() {