OC-1508: App crashes when changing the orientation
[pub/Android/ownCloud.git] / src / com / owncloud / android / operations / SynchronizeFileOperation.java
index 71c6d43..cb4485e 100644 (file)
@@ -1,10 +1,10 @@
 /* ownCloud Android client application
  *   Copyright (C) 2012 Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 3 of the License, or
- *   (at your option) any later version.
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
  *
  *   This program is distributed in the hope that it will be useful,
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -25,8 +25,8 @@ import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
 import android.accounts.Account;
 import android.content.Context;
 import android.content.Intent;
-import android.util.Log;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader;
@@ -40,7 +40,11 @@ import eu.alefzero.webdav.WebdavUtils;
 public class SynchronizeFileOperation extends RemoteOperation {
 
     private String TAG = SynchronizeFileOperation.class.getSimpleName();
-    private String mRemotePath;
+    private static final int SYNC_READ_TIMEOUT = 10000;
+    private static final int SYNC_CONNECTION_TIMEOUT = 5000;
+    
+    private OCFile mLocalFile;
+    private OCFile mServerFile;
     private DataStorageManager mStorageManager;
     private Account mAccount;
     private boolean mSyncFileContents;
@@ -50,22 +54,24 @@ public class SynchronizeFileOperation extends RemoteOperation {
     private boolean mTransferWasRequested = false;
     
     public SynchronizeFileOperation(
-            String remotePath, 
-            DataStorageManager dataStorageManager, 
+            OCFile localFile,
+            OCFile serverFile,          // make this null to let the operation checks the server; added to reuse info from SynchronizeFolderOperation 
+            DataStorageManager storageManager, 
             Account account, 
             boolean syncFileContents,
-            boolean localChangeAlreadyKnown,
+            boolean localChangeAlreadyKnown, 
             Context context) {
         
-        mRemotePath = remotePath;
-        mStorageManager = dataStorageManager;
+        mLocalFile = localFile;
+        mServerFile = serverFile;
+        mStorageManager = storageManager;
         mAccount = account;
         mSyncFileContents = syncFileContents;
         mLocalChangeAlreadyKnown = localChangeAlreadyKnown;
         mContext = context;
     }
 
-    
+
     @Override
     protected RemoteOperationResult run(WebdavClient client) {
         
@@ -73,43 +79,52 @@ public class SynchronizeFileOperation extends RemoteOperation {
         RemoteOperationResult result = null;
         mTransferWasRequested = false;
         try {
-            OCFile localFile = mStorageManager.getFileByPath(mRemotePath);
-            
-            if (!localFile.isDown()) {
+            if (!mLocalFile.isDown()) {
                 /// easy decision
-                requestForDownload(localFile);
+                requestForDownload(mLocalFile);
                 result = new RemoteOperationResult(ResultCode.OK);
                 
             } else {
-                /// local copy in the device -> need to think a bit more before do nothing
+                /// local copy in the device -> need to think a bit more before do anything
                 
-                propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
-                int status = client.executeMethod(propfind);
-                boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
-                if (isMultiStatus) {
-                    MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
-                    WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
+                if (mServerFile == null) {
+                    /// take the duty of check the server for the current state of the file there
+                    propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mLocalFile.getRemotePath()));
+                    int status = client.executeMethod(propfind, SYNC_READ_TIMEOUT, SYNC_CONNECTION_TIMEOUT);
+                    boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
+                    if (isMultiStatus) {
+                        MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
+                        WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
                                                client.getBaseUri().getPath());
-                    OCFile serverFile = fillOCFile(we);
+                        mServerFile = fillOCFile(we);
+                        mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
+                        
+                    } else {
+                        client.exhaustResponse(propfind.getResponseBodyAsStream());
+                        result = new RemoteOperationResult(false, status, propfind.getResponseHeaders());
+                    }
+                }
+                
+                if (result == null) {   // true if the server was not checked. nothing was wrong with the remote request
               
                     /// check changes in server and local file
                     boolean serverChanged = false;
-                    if (serverFile.getEtag() != null) {
-                        serverChanged = (!serverFile.getEtag().equals(localFile.getEtag()));
+                    if (mServerFile.getEtag() != null) {
+                        serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag()));   // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged?
                     } else {
                         // server without etags
-                        serverChanged = (serverFile.getModificationTimestamp() > localFile.getModificationTimestamp());
+                        serverChanged = (mServerFile.getModificationTimestamp() > mLocalFile.getModificationTimestampAtLastSyncForData());
                     }
-                    boolean localChanged = (mLocalChangeAlreadyKnown || localFile.getLocalModificationTimestamp() > localFile.getLastSyncDateForData());
+                    boolean localChanged = (mLocalChangeAlreadyKnown || mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData());
+                        // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads
               
                     /// decide action to perform depending upon changes
                     if (localChanged && serverChanged) {
-                        // conflict
                         result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
                   
                     } else if (localChanged) {
                         if (mSyncFileContents) {
-                            requestForUpload(localFile);
+                            requestForUpload(mLocalFile);
                             // the local update of file properties will be done by the FileUploader service when the upload finishes
                         } else {
                             // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid; 
@@ -120,13 +135,15 @@ public class SynchronizeFileOperation extends RemoteOperation {
                   
                     } else if (serverChanged) {
                         if (mSyncFileContents) {
-                            requestForDownload(serverFile);
+                            requestForDownload(mLocalFile); // local, not server; we won't to keep the value of keepInSync!
                             // the update of local data will be done later by the FileUploader service when the upload finishes
                         } else {
                             // TODO CHECK: is this really useful in some point in the code?
-                            serverFile.setKeepInSync(localFile.keepInSync());
-                            serverFile.setParentId(localFile.getParentId());
-                            mStorageManager.saveFile(serverFile);
+                            mServerFile.setKeepInSync(mLocalFile.keepInSync());
+                            mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
+                            mServerFile.setStoragePath(mLocalFile.getStoragePath());
+                            mServerFile.setParentId(mLocalFile.getParentId());
+                            mStorageManager.saveFile(mServerFile);
                             
                         }
                         result = new RemoteOperationResult(ResultCode.OK);
@@ -136,18 +153,15 @@ public class SynchronizeFileOperation extends RemoteOperation {
                         result = new RemoteOperationResult(ResultCode.OK);
                     }
               
-                } else {
-                    client.exhaustResponse(propfind.getResponseBodyAsStream());
-                    result = new RemoteOperationResult(false, status);
-                }
+                } 
           
             }
             
-            Log.i(TAG, "Synchronizing " + mAccount.name + ", file " + mRemotePath + ": " + result.getLogMessage());
+            Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
           
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
-            Log.e(TAG, "Synchronizing " + mAccount.name + ", file " + mRemotePath + ": " + result.getLogMessage(), result.getException());
+            Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", file " + (mLocalFile != null ? mLocalFile.getRemotePath() : "NULL") + ": " + result.getLogMessage(), result.getException());
 
         } finally {
             if (propfind != null)
@@ -160,13 +174,14 @@ public class SynchronizeFileOperation extends RemoteOperation {
     /**
      * Requests for an upload to the FileUploader service
      * 
-     * @param localFile     OCFile object representing the file to upload
+     * @param file     OCFile object representing the file to upload
      */
-    private void requestForUpload(OCFile localFile) {
+    private void requestForUpload(OCFile file) {
         Intent i = new Intent(mContext, FileUploader.class);
         i.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
-        i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath);
-        i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());
+        i.putExtra(FileUploader.KEY_FILE, file);
+        /*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!  
+        i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());*/
         i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
         i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
         mContext.startService(i);
@@ -199,9 +214,9 @@ public class SynchronizeFileOperation extends RemoteOperation {
         file.setCreationTimestamp(we.createTimestamp());
         file.setFileLength(we.contentLength());
         file.setMimetype(we.contentType());
-        file.setModificationTimestamp(we.modifiedTimesamp());
-        file.setLastSyncDateForProperties(System.currentTimeMillis());
-        file.setLastSyncDateForData(0);
+        file.setModificationTimestamp(we.modifiedTimestamp());
+        file.setEtag(we.etag());
+        
         return file;
     }
 
@@ -210,4 +225,9 @@ public class SynchronizeFileOperation extends RemoteOperation {
         return mTransferWasRequested;
     }
 
+
+    public OCFile getLocalFile() {
+        return mLocalFile;
+    }
+
 }