Commit f18487cf authored by Vincent Bourgmayer's avatar Vincent Bourgmayer 🎼
Browse files

rewrote begin() & startScan(boolean) from ObserverService. Make Davclient a...

rewrote begin() & startScan(boolean) from ObserverService. Make Davclient a local  var instead of a class's field
parent 51dcb107
Pipeline #28753 passed with stage
in 1 minute and 47 seconds
......@@ -16,7 +16,7 @@ android {
applicationId "foundation.e.drive"
minSdkVersion 24
versionCode 1
versionName "alpha-${versionMajor}-build-${buildTime()}"
versionName "beta-${versionMajor}-build-n-${buildTime()}"
setProperty("archivesBaseName", "$applicationId.$versionName")
}
......
......@@ -8,12 +8,15 @@
package foundation.e.drive.models;
import android.os.Parcel;
import android.os.Parcelable;
/**
* @author Vincent Bourgmayer
* Describe a file state which will be Synchronized (= Synced) or which has already been synced one times
*/
public class SyncedFileState {
public class SyncedFileState implements Parcelable {
protected SyncedFileState(){}; //@ToRemove. Test Only. It's to allow to make a mock SyncedFileState Class in test.
private int id;
......@@ -46,6 +49,42 @@ public class SyncedFileState {
this.isMediaType = isMediaType;
}
protected SyncedFileState(Parcel in) {
id = in.readInt();
name = in.readString();
localPath = in.readString();
remotePath = in.readString();
lastETAG = in.readString();
localLastModified = in.readLong();
syncedFolderId = in.readLong();
isMediaType = in.readByte() != 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
dest.writeString(localPath);
dest.writeString(remotePath);
dest.writeString(lastETAG);
dest.writeLong(localLastModified);
dest.writeLong(syncedFolderId);
dest.writeByte((byte) (isMediaType ? 1 : 0));
}
public static final Creator<SyncedFileState> CREATOR = new Creator<SyncedFileState>() {
@Override
public SyncedFileState createFromParcel(Parcel in) {
return new SyncedFileState(in);
}
@Override
public SyncedFileState[] newArray(int size) {
return new SyncedFileState[size];
}
};
public int getId() {
return id;
}
......@@ -125,4 +164,9 @@ public class SyncedFileState {
+"\nSyncedFolderId: "+this.syncedFolderId
+"\nisMediaType: "+this.isMediaType;
}
@Override
public int describeContents() {
return 0;
}
}
......@@ -4,18 +4,6 @@ import com.owncloud.android.lib.common.operations.RemoteOperation;
public interface ComparableOperation {
/**
* Return the path that point on a remote file
* @return String URI of the file
*/
public String getRemotePath();
/**
* Return the path that point on a local file
* @return String URI of the file
*/
public String getLocalPath();
/**
* Say if File affected by operation is a media or a settings
* @return
......
......@@ -9,16 +9,15 @@
package foundation.e.drive.operations;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.resources.files.FileUtils;
import com.owncloud.android.lib.resources.files.model.RemoteFile;
import java.io.File;
import foundation.e.drive.database.DbHelper;
import foundation.e.drive.models.SyncedFileState;
import foundation.e.drive.utils.CommonUtils;
......@@ -28,61 +27,68 @@ import foundation.e.drive.utils.CommonUtils;
* Encapsulate a global download process for a file
*/
public class DownloadFileOperation extends RemoteOperation implements ComparableOperation{
public class DownloadFileOperation extends RemoteOperation implements ComparableOperation, Parcelable {
private final static String TAG = DownloadFileOperation.class.getSimpleName();
private final RemoteFile mRFile;
private final Context mContext;
private Context mContext;
private String mTargetPath;
private int restartCounter =0;
private SyncedFileState mSyncedState;
private String previousEtag;
/**
* Constructor download operation for new file
* @param remoteFile Must not be null. File to download
* @param targetPath Local Path where to download the file Must not be null or empty
* @param context context of the app not service context
* COnstructor of download operation where syncedFileState is already known
* @param remoteFile remote file to Download
* @param syncedFileState SyncedFileState corresponding to remote file
*/
public DownloadFileOperation(RemoteFile remoteFile, String targetPath, Long syncedFolderId,boolean syncedFolderIsMediaType, Context context){
this.mContext = context;
public DownloadFileOperation(RemoteFile remoteFile, SyncedFileState syncedFileState){
this.mRFile = remoteFile;
String remotePath = remoteFile.getRemotePath();
this.mSyncedState = DbHelper.loadSyncedFile(mContext, remotePath, false );
this.mTargetPath = targetPath;
this.mSyncedState = syncedFileState;
this.previousEtag = mSyncedState.getLastETAG();
this.mTargetPath = this.mSyncedState.getLocalPath();
}
protected DownloadFileOperation(Parcel in) {
mRFile = in.readParcelable(RemoteFile.class.getClassLoader());
mTargetPath = in.readString();
restartCounter = in.readInt();
mSyncedState = in.readParcelable(SyncedFileState.class.getClassLoader());
previousEtag = in.readString();
}
if(this.mSyncedState == null){ //It's the first synchronisation of this file
Log.d(TAG, " no SyncedFileState stored in DB for: "+remoteFile.getRemotePath()
+"\n so it's first synchronisation of this file");
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mRFile, flags);
dest.writeString(mTargetPath);
dest.writeInt(restartCounter);
dest.writeParcelable(mSyncedState, flags);
dest.writeString(previousEtag);
}
this.mSyncedState = new SyncedFileState(-1,
CommonUtils.getFileNameFromPath( remotePath ), //No need to check if null. It has already be done in ObserverService
this.mTargetPath,
remotePath,
this.mRFile.getEtag(),
0L,
syncedFolderId, syncedFolderIsMediaType);
public static final Creator<DownloadFileOperation> CREATOR = new Creator<DownloadFileOperation>() {
@Override
public DownloadFileOperation createFromParcel(Parcel in) {
return new DownloadFileOperation(in);
}
this.previousEtag = mSyncedState.getLastETAG();
@Override
public DownloadFileOperation[] newArray(int size) {
return new DownloadFileOperation[size];
}
};
/**
* COnstructor of download operation where syncedFileState is already known
* @param remoteFile remote file to Download
* @param targetPath can be null if defined in syncedFileState
* @param context calling context
* @param syncedFileState SyncedFileState corresponding to remote file
* Set the context required to use DB
* @param context
*/
public DownloadFileOperation(RemoteFile remoteFile, String targetPath, Context context, SyncedFileState syncedFileState){
this.mRFile = remoteFile;
public void setContext(Context context){
this.mContext = context;
this.mSyncedState = syncedFileState;
this.previousEtag = mSyncedState.getLastETAG();
this.mTargetPath = (targetPath == null)?this.mSyncedState.getLocalPath(): targetPath;
}
@Override
protected RemoteOperationResult run(OwnCloudClient ownCloudClient) {
Log.i(TAG, "run(ownCloudClient)");
......@@ -181,17 +187,12 @@ public class DownloadFileOperation extends RemoteOperation implements Comparable
}
@Override
public String getRemotePath() {
return mSyncedState.getRemotePath();
}
@Override
public String getLocalPath() {
return mTargetPath;
public RemoteOperation toRemoteOperation() {
return this;
}
@Override
public RemoteOperation toRemoteOperation() {
return this;
public int describeContents() {
return 0;
}
}
......@@ -8,17 +8,18 @@
package foundation.e.drive.operations;
import android.os.Parcel;
import android.os.Parcelable;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.resources.files.RemoveFileRemoteOperation;
import foundation.e.drive.models.SyncedFileState;
/**
*
* @author Vincent Bourgmayer
* Created by Vincent on 19/06/2018.
* Class to be able to wrap concerned SyncedFileState in operation, so it can be retrieve at the end
*/
public class RemoveFileOperation extends RemoveFileRemoteOperation implements ComparableOperation{
public class RemoveFileOperation extends RemoveFileRemoteOperation implements ComparableOperation, Parcelable {
private SyncedFileState mSyncedFileState;
......@@ -27,6 +28,29 @@ public class RemoveFileOperation extends RemoveFileRemoteOperation implements Co
this.mSyncedFileState = syncedFileState;
}
protected RemoveFileOperation(Parcel in) {
super(in.readString());
this.mSyncedFileState = in.readParcelable(SyncedFileState.class.getClassLoader());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mSyncedFileState.getRemotePath());
dest.writeParcelable(this.mSyncedFileState, flags);
}
public static final Creator<RemoveFileOperation> CREATOR = new Creator<RemoveFileOperation>() {
@Override
public RemoveFileOperation createFromParcel(Parcel in) {
return new RemoveFileOperation(in);
}
@Override
public RemoveFileOperation[] newArray(int size) {
return new RemoveFileOperation[size];
}
};
public SyncedFileState getSyncedFileState() {
return mSyncedFileState;
}
......@@ -41,19 +65,12 @@ public class RemoveFileOperation extends RemoveFileRemoteOperation implements Co
}
@Override
public String getRemotePath() {
return this.mSyncedFileState.getRemotePath();
public RemoteOperation toRemoteOperation() {
return this;
}
/**
* @inherited
*/
@Override
public String getLocalPath() {
return getSyncedFileState().getLocalPath();
}
@Override
public RemoteOperation toRemoteOperation() {
return this;
public int describeContents() {
return 0;
}
}
......@@ -9,8 +9,11 @@
package foundation.e.drive.operations;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import java.io.File;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.UserInfo;
......@@ -29,71 +32,61 @@ import foundation.e.drive.utils.CommonUtils;
* @author Vincent Bourgmayer
* High level Operation which upload a local file to a remote cloud storage
*/
public class UploadFileOperation extends RemoteOperation implements ComparableOperation{
public class UploadFileOperation extends RemoteOperation implements ComparableOperation, Parcelable {
private final static String TAG = UploadFileOperation.class.getSimpleName();
private int restartCounter =0;
private long previousLastModified; //get to restore real value if all trials fails
private boolean checkEtag;
private String mTargetPath;
private final File mFile;
private final Context mContext;
private Context mContext;
private SyncedFileState mSyncedState;
private long availableQuota = -1;
protected UploadFileOperation(Parcel in) {
restartCounter = in.readInt();
previousLastModified = in.readLong();
checkEtag = in.readByte() != 0;
mSyncedState = in.readParcelable(SyncedFileState.class.getClassLoader());
availableQuota = in.readLong();
}
public static final Creator<UploadFileOperation> CREATOR = new Creator<UploadFileOperation>() {
@Override
public RemoteOperation toRemoteOperation() {
return this;
public UploadFileOperation createFromParcel(Parcel in) {
return new UploadFileOperation(in);
}
/**
* Constructor of uploadFileOperation when there is no syncedFileState provide
* @param file The local file to synchronize
* @param targetPath target path to upload filte. must not be null.
* @param syncedFolderId needed for new file to upload
* @param context executing context
*/
public UploadFileOperation(File file, String targetPath, Long syncedFolderId,boolean syncedFolderIsMediaType, Context context){
this.mFile = file;
this.mContext = context;
this.mTargetPath = targetPath;
String localPath = CommonUtils.getLocalPath( mFile );
@Override
public UploadFileOperation[] newArray(int size) {
return new UploadFileOperation[size];
}
};
//Try to load syncedFileState from DB
this.mSyncedState = DbHelper.loadSyncedFile( mContext,
localPath, true );
//Create a new instance and stored it if not stored in DB before
if( this.mSyncedState == null ){
//It's the first synchronisation of this file
Log.d( TAG, "it's first synchronisation of:"+mFile.getName() );
this.mSyncedState = new SyncedFileState(-1, mFile.getName(),
localPath,
mTargetPath ,
"",
0,
syncedFolderId, syncedFolderIsMediaType);
}
Log.v(TAG, ""+mSyncedState.getLocalLastModified() );
this.previousLastModified = mSyncedState.getLocalLastModified();
@Override
public RemoteOperation toRemoteOperation() {
return this;
}
/**
* Construct an upload operation with an already known syncedFileState
* @param file File to upload
* @param context context in which to upload
* @param syncedFileState syncedFileState corresponding to file.
* @param checkEtag if we should use IF MATCH header with etag
*/
public UploadFileOperation (File file, Context context, SyncedFileState syncedFileState, boolean checkEtag){
this.mFile = file;
this.mContext = context;
public UploadFileOperation (SyncedFileState syncedFileState, boolean checkEtag){
this.mSyncedState = syncedFileState;
this.mTargetPath = syncedFileState.getRemotePath();
this.previousLastModified = mSyncedState.getLocalLastModified();
this.checkEtag = checkEtag;
}
public void setContext(Context context){
this.mContext = context;
}
/**
* Execute the operation:
*
......@@ -108,29 +101,34 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp
@Override
protected RemoteOperationResult run( OwnCloudClient client ) {
//as operation isn't executed immediatly, file might have been deleted since creation of operation
if(mSyncedState == null || mTargetPath == null || !mFile.exists()){
if(mSyncedState == null){
Log.e(TAG, "run(client): no syncedFileState or target path, can't perform upload operation");
return new RemoteOperationResult(ResultCode.FORBIDDEN);
}
if(mSyncedState.getId() == -1){
this.mSyncedState.setId( DbHelper.manageSyncedFileStateDB(this.mSyncedState, "INSERT", mContext) );
File file = new File(mSyncedState.getLocalPath());
if(file == null || !file.exists()){
Log.w(TAG, "Can't get the file. It might have been deleted");
return new RemoteOperationResult(ResultCode.FORBIDDEN);
}
String targetPath = mSyncedState.getRemotePath();
//If an Etag is already Stored and LastModified from DB is the same as real file
if (mSyncedState.isLastEtagStored()
&& mSyncedState.getLocalLastModified() == mFile.lastModified()) {
Log.d(TAG, "mySyncedState last modified: "+mSyncedState.getLocalLastModified()+" <=> mFile last modified: "+mFile.lastModified() +": So return sync_conflict");
&& mSyncedState.getLocalLastModified() == file.lastModified()) {
Log.d(TAG, "mySyncedState last modified: "+mSyncedState.getLocalLastModified()+" <=> mFile last modified: "+file.lastModified() +": So return sync_conflict");
return new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
}
if(this.availableQuota == -1){
RemoteOperationResult checkQuotaResult = checkAvailableSpace(client);
RemoteOperationResult checkQuotaResult = checkAvailableSpace(client, file.length());
if( checkQuotaResult.getCode() != ResultCode.OK ){
return new RemoteOperationResult(checkQuotaResult.getCode());
}
}
UploadFileRemoteOperation uploadOperation = buildUploadOperation();
UploadFileRemoteOperation uploadOperation = buildUploadOperation(file, targetPath);
// Execute UploadFileOperation
RemoteOperationResult uploadResult = uploadOperation.execute( client );
......@@ -143,14 +141,14 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp
if(data != null) {
mSyncedState.setLastETAG((String) data);
}
mSyncedState.setLocalLastModified(mFile.lastModified());
mSyncedState.setLocalLastModified(file.lastModified());
mResultCode = uploadResult.getCode();
mustRestart = false;
}else{
//Si les répértoires ou mettre le fichier n'existe pas, on les ajoutes.
if( uploadResult.getCode() == ResultCode.FILE_NOT_FOUND ){
Log.d(TAG, "Catched a File not found result for : "+mFile.getName()+", create missing remote path then retry");
String remoteFoldersPath = mTargetPath.substring( 0, mTargetPath.lastIndexOf(FileUtils.PATH_SEPARATOR)+1 );
Log.d(TAG, "Catched a File not found result for : "+file.getName()+", create missing remote path then retry");
String remoteFoldersPath = targetPath.substring( 0, targetPath.lastIndexOf(FileUtils.PATH_SEPARATOR)+1 );
mResultCode = ResultCode.FILE_NOT_FOUND;
CreateFolderRemoteOperation createFolderOperation = new CreateFolderRemoteOperation( remoteFoldersPath, true );
try{
......@@ -171,7 +169,7 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp
mustRestart = false;
}else{
//Upload failed
Log.e(TAG, "UploadFileRemoteOperation for : " + mFile.getName() + " failed => code: " + uploadResult.getCode());
Log.e(TAG, "UploadFileRemoteOperation for : " + file.getName() + " failed => code: " + uploadResult.getCode());
mResultCode = ResultCode.UNKNOWN_ERROR;
mustRestart = false;
}
......@@ -201,12 +199,12 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp
* Build the operation to put the file on server
* @return the operation to execute
*/
private UploadFileRemoteOperation buildUploadOperation(){
String timeStamp = ( (Long) ( mFile.lastModified() / 1000) ).toString() ;
private UploadFileRemoteOperation buildUploadOperation(File file, String targetPath){
String timeStamp = ( (Long) ( file.lastModified() / 1000) ).toString() ;
//create UploadFileOperation
UploadFileRemoteOperation uploadRemoteFileOperation = new UploadFileRemoteOperation( mSyncedState.getLocalPath(),
( mTargetPath != null ) ? mTargetPath : mSyncedState.getRemotePath(),
CommonUtils.getMimeType( mFile ),
( targetPath != null ) ? targetPath : mSyncedState.getRemotePath(),
CommonUtils.getMimeType( file ),
( !checkEtag || mSyncedState.getLastETAG().isEmpty() )? null : mSyncedState.getLastETAG(), //If not null, This can cause error 412; that means remote file has change
timeStamp );
uploadRemoteFileOperation.askResultEtag(true);
......@@ -218,13 +216,13 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp
* @param client OwnCloudClient
* @return RemoteOperationResult
*/
private RemoteOperationResult checkAvailableSpace(OwnCloudClient client){
private RemoteOperationResult checkAvailableSpace(OwnCloudClient client, long fileSize){
GetRemoteUserInfoOperation getRemoteUserInfoOperation = new GetRemoteUserInfoOperation();
RemoteOperationResult ocsResult = getRemoteUserInfoOperation.execute(client);
if(ocsResult.isSuccess() && ocsResult.getData() != null){
UserInfo userInfo = (UserInfo) ocsResult.getData().get(0);
this.availableQuota = userInfo.getQuota().getFree();
if( ((UserInfo) ocsResult.getData().get(0)).getQuota().getFree() < mFile.length() ) {
if( ((UserInfo) ocsResult.getData().get(0)).getQuota().getFree() < fileSize ) {
Log.w(TAG, "quota exceeded!");
return new RemoteOperationResult(ResultCode.QUOTA_EXCEEDED);
}else{
......@@ -237,14 +235,6 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp
}
}
/**
* Return the file that should be uploading
* @return File given to this operation
*/
public File getFile() {
return mFile;
}
/**
* tell what type of operation is it
* @return true if it is an operation for media's element, then false mean its a settings operation
......@@ -254,21 +244,17 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp
return this.mSyncedState.isMediaType();
}
/**
* @inherited
* @return
*/
@Override
public String getRemotePath() {
return mTargetPath;
public int describeContents() {
return 0;
}
/**
* @inherited
* @return
*/
@Override
public String getLocalPath() {
return CommonUtils.getLocalPath(mFile);
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(restartCounter);
dest.writeLong(previousLastModified);
dest.writeByte((byte) (checkEtag ? 1: 0) ); //No method to write boolean
dest.writeParcelable(mSyncedState, flags);
dest.writeLong(availableQuota);
}
}
......@@ -9,27 +9,28 @@
package foundation.e.drive.services;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.PendingIntent;
import android.app.Service;