Merge pull request #453 from owncloud/improve_action_bar_pr_392_with_develop
[pub/Android/ownCloud.git] / src / com / owncloud / android / operations / SynchronizeFileOperation.java
index 71c6d43..7189167 100644 (file)
@@ -1,10 +1,10 @@
 /* ownCloud Android client application
  *   Copyright (C) 2012 Bartek Przybylski
 /* 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
  *
  *   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
  *
  *   This program is distributed in the hope that it will be useful,
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 package com.owncloud.android.operations;
 
 
 package com.owncloud.android.operations;
 
-import org.apache.http.HttpStatus;
-import org.apache.jackrabbit.webdav.MultiStatus;
-import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.resources.files.RemoteFile;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
+import com.owncloud.android.utils.FileStorageUtils;
+import com.owncloud.android.utils.Log_OC;
 
 import android.accounts.Account;
 import android.content.Context;
 import android.content.Intent;
 
 import android.accounts.Account;
 import android.content.Context;
 import android.content.Intent;
-import android.util.Log;
-
-import com.owncloud.android.datamodel.DataStorageManager;
-import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.files.services.FileDownloader;
-import com.owncloud.android.files.services.FileUploader;
-import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
 
 
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavEntry;
-import eu.alefzero.webdav.WebdavUtils;
+/**
+ * Remote operation performing the read of remote file in the ownCloud server.
+ * 
+ * @author David A. Velasco
+ * @author masensio
+ */
 
 public class SynchronizeFileOperation extends RemoteOperation {
 
     private String TAG = SynchronizeFileOperation.class.getSimpleName();
 
 public class SynchronizeFileOperation extends RemoteOperation {
 
     private String TAG = SynchronizeFileOperation.class.getSimpleName();
-    private String mRemotePath;
-    private DataStorageManager mStorageManager;
+    
+    private OCFile mLocalFile;
+    private OCFile mServerFile;
+    private FileDataStorageManager mStorageManager;
     private Account mAccount;
     private boolean mSyncFileContents;
     private Account mAccount;
     private boolean mSyncFileContents;
-    private boolean mLocalChangeAlreadyKnown;
     private Context mContext;
     
     private boolean mTransferWasRequested = false;
     
     public SynchronizeFileOperation(
     private Context mContext;
     
     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 
+            FileDataStorageManager storageManager, 
             Account account, 
             boolean syncFileContents,
             Account account, 
             boolean syncFileContents,
-            boolean localChangeAlreadyKnown,
             Context context) {
         
             Context context) {
         
-        mRemotePath = remotePath;
-        mStorageManager = dataStorageManager;
+        mLocalFile = localFile;
+        mServerFile = serverFile;
+        mStorageManager = storageManager;
         mAccount = account;
         mSyncFileContents = syncFileContents;
         mAccount = account;
         mSyncFileContents = syncFileContents;
-        mLocalChangeAlreadyKnown = localChangeAlreadyKnown;
         mContext = context;
     }
 
         mContext = context;
     }
 
-    
+
     @Override
     @Override
-    protected RemoteOperationResult run(WebdavClient client) {
-        
-        PropFindMethod propfind = null;
+    protected RemoteOperationResult run(OwnCloudClient client) {
+
         RemoteOperationResult result = null;
         mTransferWasRequested = false;
         RemoteOperationResult result = null;
         mTransferWasRequested = false;
-        try {
-            OCFile localFile = mStorageManager.getFileByPath(mRemotePath);
-            
-            if (!localFile.isDown()) {
-                /// easy decision
-                requestForDownload(localFile);
-                result = new RemoteOperationResult(ResultCode.OK);
-                
-            } else {
-                /// local copy in the device -> need to think a bit more before do nothing
-                
-                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],
-                                               client.getBaseUri().getPath());
-                    OCFile serverFile = fillOCFile(we);
-              
-                    /// check changes in server and local file
-                    boolean serverChanged = false;
-                    if (serverFile.getEtag() != null) {
-                        serverChanged = (!serverFile.getEtag().equals(localFile.getEtag()));
+        if (!mLocalFile.isDown()) {
+            /// easy decision
+            requestForDownload(mLocalFile);
+            result = new RemoteOperationResult(ResultCode.OK);
+
+        } else {
+            /// local copy in the device -> need to think a bit more before do anything
+
+            if (mServerFile == null) {
+                String remotePath = mLocalFile.getRemotePath();
+                ReadRemoteFileOperation operation = new ReadRemoteFileOperation(remotePath);
+                result = operation.execute(client);
+                if (result.isSuccess()){
+                    mServerFile = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
+                    mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
+                }
+            }
+
+            if (mServerFile != null) {   
+
+                /// check changes in server and local file
+                boolean serverChanged = false;
+                /* time for eTag is coming, but not yet
+                    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 = (mServerFile.getModificationTimestamp() != mLocalFile.getModificationTimestampAtLastSyncForData());
+                //}
+                boolean localChanged = (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 (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) {
+                if (localChanged && serverChanged) {
+                    result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
+
+                } else if (localChanged) {
+                    if (mSyncFileContents) {
+                        requestForUpload(mLocalFile);
+                        // the local update of file properties will be done by the FileUploader service when the upload finishes
                     } else {
                     } else {
-                        // server without etags
-                        serverChanged = (serverFile.getModificationTimestamp() > localFile.getModificationTimestamp());
+                        // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid; 
+                        // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
+                        // that an upload is necessary (for instance, in FileObserverService).
                     }
                     }
-                    boolean localChanged = (mLocalChangeAlreadyKnown || localFile.getLocalModificationTimestamp() > localFile.getLastSyncDateForData());
-              
-                    /// decide action to perform depending upon changes
-                    if (localChanged && serverChanged) {
-                        // conflict
-                        result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
-                  
-                    } else if (localChanged) {
-                        if (mSyncFileContents) {
-                            requestForUpload(localFile);
-                            // 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; 
-                            // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
-                            // that an upload is necessary (for instance, in FileObserverService).
-                        }
-                        result = new RemoteOperationResult(ResultCode.OK);
-                  
-                    } else if (serverChanged) {
-                        if (mSyncFileContents) {
-                            requestForDownload(serverFile);
-                            // 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);
-                            
-                        }
-                        result = new RemoteOperationResult(ResultCode.OK);
-              
+                    result = new RemoteOperationResult(ResultCode.OK);
+
+                } else if (serverChanged) {
+                    if (mSyncFileContents) {
+                        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 {
                     } else {
-                        // nothing changed, nothing to do
-                        result = new RemoteOperationResult(ResultCode.OK);
+                        // TODO CHECK: is this really useful in some point in the code?
+                        mServerFile.setKeepInSync(mLocalFile.keepInSync());
+                        mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
+                        mServerFile.setStoragePath(mLocalFile.getStoragePath());
+                        mServerFile.setParentId(mLocalFile.getParentId());
+                        mStorageManager.saveFile(mServerFile);
+
                     }
                     }
-              
+                    result = new RemoteOperationResult(ResultCode.OK);
+
                 } else {
                 } else {
-                    client.exhaustResponse(propfind.getResponseBodyAsStream());
-                    result = new RemoteOperationResult(false, status);
+                    // nothing changed, nothing to do
+                    result = new RemoteOperationResult(ResultCode.OK);
                 }
                 }
-          
-            }
-            
-            Log.i(TAG, "Synchronizing " + mAccount.name + ", file " + mRemotePath + ": " + result.getLogMessage());
-          
-        } catch (Exception e) {
-            result = new RemoteOperationResult(e);
-            Log.e(TAG, "Synchronizing " + mAccount.name + ", file " + mRemotePath + ": " + result.getLogMessage(), result.getException());
-
-        } finally {
-            if (propfind != null)
-                propfind.releaseConnection();
+
+            } 
+
         }
         }
+
+        Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
+
         return result;
     }
 
         return result;
     }
 
@@ -160,13 +158,14 @@ public class SynchronizeFileOperation extends RemoteOperation {
     /**
      * Requests for an upload to the FileUploader service
      * 
     /**
      * 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);
         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);
         i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
         i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
         mContext.startService(i);
@@ -188,26 +187,13 @@ public class SynchronizeFileOperation extends RemoteOperation {
     }
 
 
     }
 
 
-    /**
-     * Creates and populates a new {@link OCFile} object with the data read from the server.
-     * 
-     * @param we        WebDAV entry read from the server for a WebDAV resource (remote file or folder).
-     * @return          New OCFile instance representing the remote resource described by we.
-     */
-    private OCFile fillOCFile(WebdavEntry we) {
-        OCFile file = new OCFile(we.decodedPath());
-        file.setCreationTimestamp(we.createTimestamp());
-        file.setFileLength(we.contentLength());
-        file.setMimetype(we.contentType());
-        file.setModificationTimestamp(we.modifiedTimesamp());
-        file.setLastSyncDateForProperties(System.currentTimeMillis());
-        file.setLastSyncDateForData(0);
-        return file;
+    public boolean transferWasRequested() {
+        return mTransferWasRequested;
     }
 
 
     }
 
 
-    public boolean transferWasRequested() {
-        return mTransferWasRequested;
+    public OCFile getLocalFile() {
+        return mLocalFile;
     }
 
 }
     }
 
 }