Merge branch 'develop' into refresh_folder_contents_when_browsed_into
authormasensio <masensio@solidgear.es>
Fri, 20 Sep 2013 08:19:44 +0000 (10:19 +0200)
committermasensio <masensio@solidgear.es>
Fri, 20 Sep 2013 08:19:44 +0000 (10:19 +0200)
Conflicts:
src/com/owncloud/android/operations/SynchronizeFolderOperation.java

1  2 
src/com/owncloud/android/datamodel/FileDataStorageManager.java
src/com/owncloud/android/operations/RemoteOperationResult.java
src/com/owncloud/android/operations/SynchronizeFileOperation.java
src/com/owncloud/android/operations/SynchronizeFolderOperation.java
src/com/owncloud/android/providers/FileContentProvider.java

@@@ -135,7 -135,6 +135,7 @@@ public class FileDataStorageManager imp
          cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
          cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
          cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
 +        cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
  
          boolean sameRemotePath = fileExists(file.getRemotePath());
          boolean changesSizeOfAncestors = false;
              cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
              cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
              cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
 +            cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
  
              if (fileExists(file.getRemotePath())) {
                  OCFile oldFile = getFileByPath(file.getRemotePath());
                      getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
              file.setKeepInSync(c.getInt(
                      c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
 +            file.setEtag(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG)));
 +                    
          }
          return file;
      }
                  try {
                      c = getContentProvider().query(ProviderTableMeta.CONTENT_URI, 
                              null,
-                             ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ?",
-                             new String[] { mAccount.name, dir.getRemotePath() + "%" }, null);
+                             ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
+                             new String[] { mAccount.name, dir.getRemotePath() + "%"  }, ProviderTableMeta.FILE_PATH + " ASC ");
                  } catch (RemoteException e) {
                      Log_OC.e(TAG, e.getMessage());
                  }
              } else {
                  c = getContentResolver().query(ProviderTableMeta.CONTENT_URI, 
                          null,
-                         ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ?",
-                         new String[] { mAccount.name, dir.getRemotePath() + "%" }, null);
+                         ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
+                         new String[] { mAccount.name, dir.getRemotePath() + "%"  }, ProviderTableMeta.FILE_PATH + " ASC ");
              }
  
              /// 2. prepare a batch of update operations to change all the descendants
@@@ -28,6 -28,7 +28,7 @@@ import java.net.UnknownHostException
  import javax.net.ssl.SSLException;
  
  import org.apache.commons.httpclient.ConnectTimeoutException;
+ import org.apache.commons.httpclient.Header;
  import org.apache.commons.httpclient.HttpException;
  import org.apache.commons.httpclient.HttpStatus;
  import org.apache.jackrabbit.webdav.DavException;
@@@ -50,7 -51,7 +51,7 @@@ import com.owncloud.android.network.Cer
  public class RemoteOperationResult implements Serializable {
  
      /** Generated - should be refreshed every time the class changes!! */
-     private static final long serialVersionUID = 6106167714625712390L;
+     private static final long serialVersionUID = -4415103901492836870L;
  
      
      private static final String TAG = "RemoteOperationResult";
@@@ -59,7 -60,6 +60,7 @@@
          OK,
          OK_SSL,
          OK_NO_SSL,
 +        OK_NO_CHANGES_ON_DIR,
          UNHANDLED_HTTP_CODE,
          UNAUTHORIZED,        
          FILE_NOT_FOUND, 
          OAUTH2_ERROR_ACCESS_DENIED,
          QUOTA_EXCEEDED, 
          ACCOUNT_NOT_FOUND, 
-         ACCOUNT_EXCEPTION
+         ACCOUNT_EXCEPTION, 
+         ACCOUNT_NOT_NEW, 
+         ACCOUNT_NOT_THE_SAME
      }
  
      private boolean mSuccess = false;
      private int mHttpCode = -1;
      private Exception mException = null;
      private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
+     private String mRedirectedLocation;
  
      public RemoteOperationResult(ResultCode code) {
          mCode = code;
 -        mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL);
 +        mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL || code == ResultCode.OK_NO_CHANGES_ON_DIR);
      }
  
-     public RemoteOperationResult(boolean success, int httpCode) {
+     private RemoteOperationResult(boolean success, int httpCode) {
          mSuccess = success;
          mHttpCode = httpCode;
  
                  break;
              default:
                  mCode = ResultCode.UNHANDLED_HTTP_CODE;
-                 Log_OC.d(TAG, "RemoteOperationResult has prcessed UNHANDLED_HTTP_CODE: " + httpCode);
+                 Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode);
              }
          }
      }
+     
+     public RemoteOperationResult(boolean success, int httpCode, Header[] headers) {
+         this(success, httpCode);
+         if (headers != null) {
+             Header current;
+             for (int i=0; i<headers.length; i++) {
+                 current = headers[i];
+                 if ("Location".equals(current.getName())) {
+                     mRedirectedLocation = current.getValue();
+                     break;
+                 }
+             }
+         }
+     }    
  
      public RemoteOperationResult(Exception e) {
          mException = e;
  
          } else if (mCode == ResultCode.LOCAL_STORAGE_NOT_MOVED) {
              return "Error while moving file to final directory";
+         } else if (mCode == ResultCode.ACCOUNT_NOT_NEW) {
+             return "Account already existing when creating a new one";
+         } else if (mCode == ResultCode.ACCOUNT_NOT_THE_SAME) {
+             return "Authenticated with a different account than the one updating";
          }
  
          return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess() ? "success" : "fail") + ")";
          return (mException != null);
      }
  
+     public boolean isTemporalRedirection() {
+         return (mHttpCode == 302 || mHttpCode == 307);
+     }
+     public String getRedirectedLocation() {
+         return mRedirectedLocation;
+     }
+     
+     public boolean isIdPRedirection() {
+         return (mRedirectedLocation != null &&
+                 (mRedirectedLocation.toUpperCase().contains("SAML") || 
+                 mRedirectedLocation.toLowerCase().contains("wayf")));
+     }
  }
@@@ -101,7 -101,7 +101,7 @@@ public class SynchronizeFileOperation e
                          
                      } else {
                          client.exhaustResponse(propfind.getResponseBodyAsStream());
-                         result = new RemoteOperationResult(false, status);
+                         result = new RemoteOperationResult(false, status, propfind.getResponseHeaders());
                      }
                  }
                  
          file.setFileLength(we.contentLength());
          file.setMimetype(we.contentType());
          file.setModificationTimestamp(we.modifiedTimestamp());
 +        file.setEtag(we.etag());
 +        
          return file;
      }
  
@@@ -23,18 -23,19 +23,19 @@@ import java.io.FileOutputStream
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
 +import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Vector;
  
+ import org.apache.commons.httpclient.Header;
  import org.apache.http.HttpStatus;
  import org.apache.jackrabbit.webdav.MultiStatus;
  import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
  
  import android.accounts.Account;
  import android.content.Context;
 -
  import com.owncloud.android.Log_OC;
  import com.owncloud.android.datamodel.DataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
@@@ -63,7 -64,7 +64,7 @@@ public class SynchronizeFolderOperatio
      
      /** Id of the folder to synchronize in the local database */
      private long mParentId;
 -    
 +
      /** Access to the local database */
      private DataStorageManager mStorageManager;
      
          return mChildren;
      }
      
 -    
 +    public String getRemotePath() {
 +        return mRemotePath;
 +    }
 +
 +    public long getParentId() {
 +        return mParentId;
 +    }
 +
      @Override
      protected RemoteOperationResult run(WebdavClient client) {
          RemoteOperationResult result = null;
          mFailsInFavouritesFound = 0;
          mConflictsFound = 0;
          mForgottenLocalFiles.clear();
 -        
 +        boolean fileChanged = false;
 +
          // code before in FileSyncAdapter.fetchData
          PropFindMethod query = null;
          try {
              Log_OC.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
 -            
 +
              // remote request 
              query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
              int status = client.executeMethod(query);
 -            
 +
              // check and process response   - /// TODO take into account all the possible status per child-resource
              if (isMultiStatus(status)) { 
                  MultiStatus resp = query.getResponseBodyAsMultiStatus();
 -            
 +
                  // synchronize properties of the parent folder, if necessary
                  if (mParentId == DataStorageManager.ROOT_PARENT_ID) {
                      WebdavEntry we = new WebdavEntry(resp.getResponses()[0], client.getBaseUri().getPath());
 +
 +                    // Properties of server folder
                      OCFile parent = fillOCFile(we);
 -                    mStorageManager.saveFile(parent);
 -                    mParentId = parent.getFileId();
 +                    // Properties of local folder
 +                    OCFile localParent = mStorageManager.getFileById(1);
 +                    if (parent.getEtag() != localParent.getEtag()) {
 +                        mStorageManager.saveFile(parent);
 +                        mParentId = parent.getFileId();
 +                    }
                  }
 -                
 +
 +
                  // read contents in folder
 +                List<String> filesOnServer = new ArrayList<String> (); // Contains the lists of files on server
                  List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
                  List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
                  for (int i = 1; i < resp.getResponses().length; ++i) {
                      /// new OCFile instance with the data from the server
                      WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
                      OCFile file = fillOCFile(we);
 -                    
 +
 +                    filesOnServer.add(file.getRemotePath()); // Registry the file in the list
 +
                      /// set data about local state, keeping unchanged former data if existing
                      file.setLastSyncDateForProperties(mCurrentSyncTime);
                      OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
 +
 +                    // Check if it is needed to synchronize the folder
 +                    fileChanged = false;
                      if (oldFile != null) {
 -                        file.setKeepInSync(oldFile.keepInSync());
 -                        file.setLastSyncDateForData(oldFile.getLastSyncDateForData());
 -                        file.setModificationTimestampAtLastSyncForData(oldFile.getModificationTimestampAtLastSyncForData());    // must be kept unchanged when the file contents are not updated
 -                        checkAndFixForeignStoragePath(oldFile);
 -                        file.setStoragePath(oldFile.getStoragePath());
 -                    }
 +                        if (!file.getEtag().equalsIgnoreCase(oldFile.getEtag())) {
 +                            fileChanged = true; 
 +                        }                        
 +                    } else
 +                        fileChanged= true;
  
 -                    /// scan default location if local copy of file is not linked in OCFile instance
 -                    if (file.getStoragePath() == null && !file.isDirectory()) {
 -                        File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
 -                        if (f.exists()) {
 -                            file.setStoragePath(f.getAbsolutePath());
 -                            file.setLastSyncDateForData(f.lastModified());
 +                    if (fileChanged){               
 +                        if (oldFile != null) { 
 +                            file.setKeepInSync(oldFile.keepInSync());
 +                            file.setLastSyncDateForData(oldFile.getLastSyncDateForData());
 +                            file.setModificationTimestampAtLastSyncForData(oldFile.getModificationTimestampAtLastSyncForData());    // must be kept unchanged when the file contents are not updated
 +                            checkAndFixForeignStoragePath(oldFile);
 +                            file.setStoragePath(oldFile.getStoragePath());
                          }
 +                        /// scan default location if local copy of file is not linked in OCFile instance
 +                        if (file.getStoragePath() == null && !file.isDirectory()) {
 +                            File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
 +                            if (f.exists()) {
 +                                file.setStoragePath(f.getAbsolutePath());
 +                                file.setLastSyncDateForData(f.lastModified());
 +                            }
 +                        }
 +
 +                        /// prepare content synchronization for kept-in-sync files
 +                        if (file.keepInSync()) {
 +                            SynchronizeFileOperation operation = new SynchronizeFileOperation(  oldFile,        
 +                                    file, 
 +                                    mStorageManager,
 +                                    mAccount,       
 +                                    true, 
 +                                    false,          
 +                                    mContext
 +                                    );
 +                            filesToSyncContents.add(operation);
 +                        }
 +
 +                        updatedFiles.add(file);
                      }
 -                    
 -                    /// prepare content synchronization for kept-in-sync files
 -                    if (file.keepInSync()) {
 -                        SynchronizeFileOperation operation = new SynchronizeFileOperation(  oldFile,        
 -                                                                                            file, 
 -                                                                                            mStorageManager,
 -                                                                                            mAccount,       
 -                                                                                            true, 
 -                                                                                            false,          
 -                                                                                            mContext
 -                                                                                            );
 -                        filesToSyncContents.add(operation);
 -                    }
 -                
 -                    updatedFiles.add(file);
                  }
 -                                
 +
                  // save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed)
                  mStorageManager.saveFiles(updatedFiles);
 -                
 +
                  // request for the synchronization of files AFTER saving last properties
 -                SynchronizeFileOperation op = null;
 +                //SynchronizeFileOperation op = null;
                  RemoteOperationResult contentsResult = null;
 -                for (int i=0; i < filesToSyncContents.size(); i++) {
 -                    op = filesToSyncContents.get(i);
 +                for (SynchronizeFileOperation op: filesToSyncContents) {//int i=0; i < filesToSyncContents.size(); i++) {
 +                    //op = filesToSyncContents.get(i);
                      contentsResult = op.execute(client);   // returns without waiting for upload or download finishes
                      if (!contentsResult.isSuccess()) {
                          if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
                          } else {
                              mFailsInFavouritesFound++;
                              if (contentsResult.getException() != null) {
-                                 Log_OC.d(TAG, "Error while synchronizing favourites : " +  contentsResult.getLogMessage(), contentsResult.getException());
+                                 Log_OC.e(TAG, "Error while synchronizing favourites : " +  contentsResult.getLogMessage(), contentsResult.getException());
                              } else {
-                                 Log_OC.d(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
+                                 Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
                              }
                          }
                      }   // won't let these fails break the synchronization process
                  }
  
 -                    
 +
                  // removal of obsolete files
                  mChildren = mStorageManager.getDirectoryContent(mStorageManager.getFileById(mParentId));
 -                OCFile file;
 +                //OCFile file;
                  String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
 -                for (int i=0; i < mChildren.size(); ) {
 -                    file = mChildren.get(i);
 -                    if (file.getLastSyncDateForProperties() != mCurrentSyncTime) {
 -                        Log_OC.d(TAG, "removing file: " + file);
 -                        mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));
 -                        mChildren.remove(i);
 -                    } else {
 -                        i++;
 +                for (OCFile fileChild: mChildren) {
 +                    //file = mChildren.get(i);
 +                    //if (file.getLastSyncDateForProperties() != mCurrentSyncTime) {
 +                    if (!filesOnServer.contains(fileChild.getRemotePath())) {
 +                        Log_OC.d(TAG, "removing file: " + fileChild.getFileName());
 +                        mStorageManager.removeFile(fileChild, (fileChild.isDown() && fileChild.getStoragePath().startsWith(currentSavePath)));
 +                        mChildren.remove(fileChild); //.remove(i);
                      }
 +                    //                    } else {
 +                    //                        i++;
 +                    //                    }
                  }
 -                
 +                // }
 +                //}
 +
              } else {
                  client.exhaustResponse(query.getResponseBodyAsStream());
              }
 -            
 +
              // prepare result object
 -            if (isMultiStatus(status)) {
 +            if (!fileChanged) {
 +                result = new RemoteOperationResult(ResultCode.OK_NO_CHANGES_ON_DIR);
 +
 +            } else if (isMultiStatus(status)) {
                  if (mConflictsFound > 0  || mFailsInFavouritesFound > 0) { 
                      result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);   // should be different result, but will do the job
 -                            
 +
                  } else {
-                     result = new RemoteOperationResult(true, status);
+                     result = new RemoteOperationResult(true, status, query.getResponseHeaders());
                  }
              } else {
-                 result = new RemoteOperationResult(false, status);
+                 result = new RemoteOperationResult(false, status, query.getResponseHeaders());
              }
 -            
 -            
 -            
 +            Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
 +
          } catch (Exception e) {
              result = new RemoteOperationResult(e);
-             Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
+             
  
          } finally {
              if (query != null)
                  query.releaseConnection();  // let the connection available for other methods
+             if (result.isSuccess()) {
+                 Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+             } else {
+                 if (result.isException()) {
+                     Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
+                 } else {
+                     Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+                 }
+             }
          }
 -        
 +
          return result;
      }
 -    
 +
  
      public boolean isMultiStatus(int status) {
          return (status == HttpStatus.SC_MULTI_STATUS); 
          file.setMimetype(we.contentType());
          file.setModificationTimestamp(we.modifiedTimestamp());
          file.setParentId(mParentId);
 +        file.setEtag(we.etag());
          return file;
      }
      
       * 
       * If the copy fails, the link to the local file is nullified. The account of forgotten files is kept in 
       * {@link #mForgottenLocalFiles}
 -     * 
 +     *) 
       * @param file      File to check and fix.
       */
      private void checkAndFixForeignStoragePath(OCFile file) {
@@@ -78,8 -78,6 +78,8 @@@ public class FileContentProvider extend
                  ProviderTableMeta.FILE_KEEP_IN_SYNC);
          mProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER,
                  ProviderTableMeta.FILE_ACCOUNT_OWNER);
 +        mProjectionMap.put(ProviderTableMeta.FILE_ETAG, 
 +                ProviderTableMeta.FILE_ETAG);
      }
  
      private static final int SINGLE_FILE = 1;
          }
  
          SQLiteDatabase db = mDbHelper.getReadableDatabase();
+         // DB case_sensitive
+         db.execSQL("PRAGMA case_sensitive_like = true");
          Cursor c = sqlQuery.query(db, projection, selection, selectionArgs,
                  null, null, order);
  
                      + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "
                      + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "
                      + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "
 -                    + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER );"
 +                    + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER, "
 +                    + ProviderTableMeta.FILE_ETAG + " TEXT );"
                      );
          }
  
              }
              if (!upgraded)
                  Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);
 +        
 +            if (oldVersion < 5 && newVersion >= 5) {
 +                Log_OC.i("SQL", "Entering in the #4 ADD in onUpgrade");
 +                db.beginTransaction();
 +                try {
 +                    db .execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
 +                            " ADD COLUMN " + ProviderTableMeta.FILE_ETAG + " TEXT " +
 +                            " DEFAULT NULL");
 +                    
 +                    upgraded = true;
 +                    db.setTransactionSuccessful();
 +                } finally {
 +                    db.endTransaction();
 +                }
 +            }
 +            if (!upgraded)
 +                Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);
          }
 -
      }
  
  }