Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9982181d authored by Vincent Bourgmayer's avatar Vincent Bourgmayer
Browse files

Merge branch 'e1-i4-FileEventTriggerSynchronization' into '1-epic-refactoring-p1'

Make synchronization works from FileEvent and ObserverService

See merge request !85
parents c24a9073 022327c6
Loading
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -31,17 +31,17 @@ public class EdriveApplication extends Application {

    private static final String TAG = "EdriveApplication";
    private RecursiveFileObserver mFileObserver = null;

    private FileEventListener fileEventListener;

    @Override
    public void onCreate() {
        super.onCreate();

        fileEventListener = new FileEventListener(getApplicationContext());
        Log.i(TAG, "Starting");
        resetOperationManagerSetting();

        final String pathForObserver = Environment.getExternalStorageDirectory().getAbsolutePath();
        mFileObserver = new RecursiveFileObserver(getApplicationContext(), pathForObserver, new FileEventListener());
        mFileObserver = new RecursiveFileObserver(getApplicationContext(), pathForObserver, fileEventListener);

        SharedPreferences prefs = getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);

@@ -77,8 +77,9 @@ public class EdriveApplication extends Application {
     */
    public void startRecursiveFileObserver(){
        if (!mFileObserver.isWatching()) {
            fileEventListener.bindToSynchronizationService();
            mFileObserver.startWatching();
            Log.d(TAG, "Starting RecursiveFileObserver on media's root folder");
            Log.d(TAG, "Starting RecursiveFileObserver on root folder");
        }
        else {
            Log.w(TAG, "RecursiveFileObserver (for media's root folder) was already running");
@@ -87,7 +88,8 @@ public class EdriveApplication extends Application {

    public void stopRecursiveFileObserver(){
        mFileObserver.stopWatching();
        Log.d(TAG, "RecursiveFileObserver on media's root folder stops watching ");
        fileEventListener.unbindFromSynchronizationService();
        Log.d(TAG, "RecursiveFileObserver on root folder stops watching ");
    }

    @Override
+153 −5
Original line number Diff line number Diff line
/*
 * Copyright © Narinder Rana (/e/ foundation).
 * Copyright © Vincent Bourgmayer (/e/ foundation).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
@@ -8,23 +9,170 @@

package foundation.e.drive.FileObservers;

import static foundation.e.drive.models.SyncRequest.Type.UPLOAD;

import android.content.Context;
import android.content.Intent;
import android.os.FileObserver;
import android.util.Log;

import com.owncloud.android.lib.resources.files.FileUtils;

import java.io.File;

import foundation.e.drive.database.DbHelper;
import foundation.e.drive.models.SyncRequest;
import foundation.e.drive.models.SyncedFileState;
import foundation.e.drive.models.SyncedFolder;
import foundation.e.drive.services.SynchronizationService;
import foundation.e.drive.utils.CommonUtils;
import foundation.e.drive.utils.SynchronizationServiceConnection;

/**
 * @author Narinder Rana
 * @author vincent Bourgmayer
 */
public class FileEventListener  {
    private final static String TAG = FileEventListener.class.getSimpleName();
    private static final String TAG = FileEventListener.class.getSimpleName();

    private final Context appContext;
    private final SynchronizationServiceConnection serviceConnection = new SynchronizationServiceConnection();

    public FileEventListener(Context applicationContext) {
        this.appContext = applicationContext;
    }

    public void onEvent(int event, File file) {
        if (event == FileObserver.CLOSE_WRITE) { //Event triggered after modification/creation
            Log.d(TAG, "CLOSE_WRITE event for :" + file.getName());
        } else if (event == FileObserver.DELETE) {
            Log.d(TAG, "DELETE event for :" + file.getName());
        if (event == FileObserver.DELETE) {
            if (file.isDirectory()) {
                handleDirectoryDelete(file);
            } else {
                handleFileDelete(file);
            }
        } else if (event == FileObserver.CLOSE_WRITE) {

            if (file.isDirectory()) {
                handleDirectoryCloseWrite(file);
            } else {
                handleFileCloseWrite(file);
            }
        } else if (event == FileObserver.MOVE_SELF){
            Log.d(TAG, file.getAbsolutePath() + " has been moved. Not handled yet");
        }
    }

    private void sendSyncRequestToSynchronizationService(SyncRequest request) {
        Log.d(TAG, "Sending a SyncRequest for " + request.getSyncedFileState().getName());
        if (serviceConnection.isBoundToSynchronizationService()) {
            serviceConnection.getSynchronizationService().queueOperation(request);
            serviceConnection.getSynchronizationService().startSynchronization();
        }else{
            Log.w(TAG, "Impossible to send SyncRequest. FileEventListener is not bound to SynchronizationService");
        }
    }

    private void handleDirectoryCloseWrite(File directory) {
        final String fileLocalPath = CommonUtils.getLocalPath(directory);
        Log.d(TAG, "handleDirectoryCloseWrite(" + fileLocalPath + ")");
        SyncedFolder folder = DbHelper.getSyncedFolderByLocalPath(fileLocalPath, appContext);
        if (folder == null) { //it's a directory creation
            final String parentPath = CommonUtils.getLocalPath(directory.getParentFile());
            SyncedFolder parentFolder = DbHelper.getSyncedFolderByLocalPath(parentPath, appContext);
            if (parentFolder != null) { //if parent is in the DB
                folder = new SyncedFolder(parentFolder, directory.getName() + FileUtils.PATH_SEPARATOR, directory.lastModified(), "");
                DbHelper.insertSyncedFolder(folder, appContext);
            }
        } else {  //It's a directory update
            folder.setLastModified(directory.lastModified());
            DbHelper.updateSyncedFolder(folder, appContext);
        }
    }

    private void handleDirectoryDelete(File directory) {
        final String fileLocalPath = CommonUtils.getLocalPath(directory);
        Log.d(TAG, "handleDirectoryDelete("+fileLocalPath+")");
        SyncedFolder folder = DbHelper.getSyncedFolderByLocalPath(fileLocalPath, appContext);
        if (folder == null) {
            //look for parent
            final String parentPath = CommonUtils.getLocalPath(directory.getParentFile());
            SyncedFolder parentFolder = DbHelper.getSyncedFolderByLocalPath(parentPath, appContext);
            if (parentFolder != null ) { //if parent is in the DB
                folder = new SyncedFolder(parentFolder, directory.getName()+ FileUtils.PATH_SEPARATOR, directory.lastModified(), "");
                folder.setEnabled(false);
                DbHelper.insertSyncedFolder(folder, appContext);
            }
        } else {  //If already in DB
            if (folder.isEnabled()) {
                folder.setEnabled(false);
                DbHelper.updateSyncedFolder(folder, appContext);
            }
        }
    }

    private void handleFileCloseWrite(File file) {
        final String fileLocalPath = CommonUtils.getLocalPath(file);
        Log.d(TAG, "handleFileCloseWrite("+fileLocalPath+")");
        SyncRequest request = null;
        SyncedFileState fileState = DbHelper.loadSyncedFile( appContext, fileLocalPath, true);

        if (fileState == null) { //New file discovered
            final String parentPath = CommonUtils.getLocalPath(file.getParentFile());
            SyncedFolder parentFolder = DbHelper.getSyncedFolderByLocalPath(parentPath, appContext);
            if (parentFolder == null || !parentFolder.isEnabled()) {
                Log.w(TAG, "Won't send sync request: no parent are known for new file: "+file.getName());
                return;
            }
            int scannableValue = 0;
            if (parentFolder.isEnabled()) {
                if (parentFolder.isScanRemote()) scannableValue++;
                if (parentFolder.isScanLocal()) scannableValue += 2;
            }

            final String remotePath = parentFolder.getRemoteFolder()+file.getName();
            fileState = new SyncedFileState(-1, file.getName(), CommonUtils.getLocalPath(file), remotePath, "", 0L, parentFolder.getId(), parentFolder.isMediaType(), scannableValue);
            int storedId = DbHelper.manageSyncedFileStateDB(fileState, "INSERT", appContext);
            if (storedId > 0) {
                fileState.setId(storedId);
                request = new SyncRequest(fileState, UPLOAD);
            } else {
                Log.w(TAG, "New File " + file.getName() + " observed but impossible to insert it in DB");
            }
        } else { //File update
            if (fileState.getScannable() > 1) {
                request = new SyncRequest(fileState, UPLOAD);
            }
        }
        if (request != null) {
            sendSyncRequestToSynchronizationService(request);
        }
    }

    private void handleFileDelete(File file) {
        final String fileLocalPath = CommonUtils.getLocalPath(file);
        Log.d(TAG, "handleFileDelete("+fileLocalPath+")");
        SyncedFileState fileState = DbHelper.loadSyncedFile( appContext, fileLocalPath, true);
        if (fileState == null) {
            Log.d(TAG, "Ignore event because file is not already in database");
            return;
        }

        //If already in DB
        if (fileState.getScannable() > 0) {
            fileState.setScannable(0);
            DbHelper.manageSyncedFileStateDB(fileState, "UPDATE", appContext);
        }
    }

    public void unbindFromSynchronizationService(){
        if(serviceConnection.isBoundToSynchronizationService())
            appContext.unbindService(serviceConnection);
        else
            Log.w(TAG, "Not bound to SynchronizationService: can't unbind.");
    }

    public void bindToSynchronizationService(){
        Log.d(TAG, "bindToSynchronizationService()");
        final Intent SynchronizationServiceIntent = new Intent(appContext, SynchronizationService.class);
        appContext.bindService(SynchronizationServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    }
}
+33 −7
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ import foundation.e.drive.models.SyncedFileState;
 */
public final class DbHelper extends SQLiteOpenHelper {
    final private static String TAG = DbHelper.class.getSimpleName(); //Tag for log
    private static final int DATABASE_VERSION = 19; //20/09/2018
    private static final int DATABASE_VERSION = 20; //16/03/2022
    public static final String DATABASE_NAME = "eelo_drive.db";

    /**
@@ -53,20 +53,24 @@ public final class DbHelper extends SQLiteOpenHelper {
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.i(TAG, "onUpgrade(db, "+oldVersion+", "+newVersion+")");
        if(oldVersion < 19){
        try {
            if (oldVersion < 19) {
                db.execSQL(SyncedFolderContract.UPDATE_TABLE_TO_VERSION_19);
                db.execSQL(SyncedFileStateContract.UPDATE_TABLE_TO_VERSION_19);

                db.execSQL(SyncedFileStateContract.UPDATE_MEDIA_DATA_TO_VERSION_19);
                db.execSQL(SyncedFileStateContract.UPDATE_SETTINGS_DATA_TO_VERSION_19);
                db.execSQL(SyncedFolderContract.UPDATE_MEDIA_DATA_TO_VERSION_19);
                db.execSQL(SyncedFolderContract.UPDATE_SETTINGS_DATA_TO_VERSION_19);
            }
            if (oldVersion < 20) {
                db.execSQL(SyncedFileStateContract.UPDATE_TABLE_TO_VERSION_20);
                db.execSQL(SyncedFileStateContract.UPDATE_MEDIA_DATA_TO_VERSION_20);
                db.execSQL(SyncedFileStateContract.UPDATE_SETTINGS_DATA_TO_VERSION_20);
            }
        } catch(Exception e) {
            Log.e(TAG, toString());
        }
    }
    }


    private static SyncedFileStateDAO openSyncedFileStateDAO(Context context, boolean writeMod){
@@ -116,7 +120,6 @@ public final class DbHelper extends SQLiteOpenHelper {
            }
            dao.close();
        }

        return result;
    }

@@ -178,6 +181,19 @@ public final class DbHelper extends SQLiteOpenHelper {
        return result;
    }


    public static int updateSyncedFolder(SyncedFolder syncedFolder, Context context) {
        int result = -1;
        //Connect to DB
        SyncedFolderDAO dao = openSyncedFolderDAO(context, true);
        if (dao == null){
            return result;
        }
        result = dao.update( syncedFolder );
        dao.close();
        return result;
    }

    /**
     * Load SyncedFolder's from DB
     * @param context app or service activity
@@ -243,6 +259,16 @@ public final class DbHelper extends SQLiteOpenHelper {
        return result;
    }

    public static SyncedFolder getSyncedFolderByLocalPath(String localPath, Context context){
        SyncedFolderDAO dao = openSyncedFolderDAO(context, true);
        if (dao == null) {
            return null;
        }
        SyncedFolder syncedFolder = dao.getSyncedFolderByLocalPath(localPath);
        dao.close();
        return syncedFolder;
    }

    /**
     * Set the lastModified value of SyncedFolder to 1.
     * The goal is to force to rescan it next time.
+10 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ class SyncedFileStateContract implements BaseColumns{
    static final String LOCAL_LAST_MODIFIED = "local_last_modified";
    static final String SYNCEDFOLDER_ID = "synced_folder_id";
    static final String IS_MEDIA_TYPE = "is_media_type";
    static final String SCANNABLE = "scannable";

    static final String SQL_CREATE_TABLE_SYNCEDFILESTATE =
         "CREATE TABLE "+TABLE_NAME+" ( "
@@ -36,6 +37,7 @@ class SyncedFileStateContract implements BaseColumns{
         +LOCAL_LAST_MODIFIED+" INTEGER, "
         + SYNCEDFOLDER_ID +" INTEGER, "
         +IS_MEDIA_TYPE+" BOOLEAN,"
         +SCANNABLE+" INTEGER, "
         +"CONSTRAINT synced_unicity_constraint UNIQUE ("
         +FILE_NAME+", "
         +LOCAL_PATH+", "
@@ -45,8 +47,8 @@ class SyncedFileStateContract implements BaseColumns{
    static final String SQL_DELETE_TABLE_SYNCEDFILESTATE = " DROP TABLE IF EXISTS "
           + TABLE_NAME;

    //Update for version 18 and lower
    static final String UPDATE_TABLE_TO_VERSION_19 = "ALTER TABLE "+TABLE_NAME+" ADD COLUMN "+IS_MEDIA_TYPE+" BOOLEAN;";

    static final String UPDATE_MEDIA_DATA_TO_VERSION_19 = "UPDATE "+TABLE_NAME+
            " SET "+IS_MEDIA_TYPE+" = 1 WHERE "+
            REMOTE_PATH+" LIKE \"/Photos/%\" OR "+
@@ -60,4 +62,11 @@ class SyncedFileStateContract implements BaseColumns{
    static final String UPDATE_SETTINGS_DATA_TO_VERSION_19 = "UPDATE "+TABLE_NAME+
            " SET "+IS_MEDIA_TYPE+" = 0 WHERE "+REMOTE_PATH+" LIKE \"/Devices/%\";";

    //update for version 19 and lower
    static final String UPDATE_TABLE_TO_VERSION_20 = "ALTER TABLE "+TABLE_NAME+" ADD COLUMN "+SCANNABLE+" INTEGER;";
    static final String UPDATE_MEDIA_DATA_TO_VERSION_20 = "UPDATE "+TABLE_NAME+" SET "+SCANNABLE+" = 3 WHERE"
            + IS_MEDIA_TYPE+" = 1 ;" ;
    static final String UPDATE_SETTINGS_DATA_TO_VERSION_20 = "UPDATE "+TABLE_NAME+" SET "+SCANNABLE+" = 2 WHERE"
         + IS_MEDIA_TYPE+" = 0 ;" ;

}
+16 −20
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import java.util.ArrayList;
import java.util.List;

import foundation.e.drive.models.SyncedFileState;

import static foundation.e.drive.database.SyncedFileStateContract.SCANNABLE;
import static foundation.e.drive.database.SyncedFileStateContract.TABLE_NAME;
import static foundation.e.drive.database.SyncedFileStateContract.FILE_NAME;
import static foundation.e.drive.database.SyncedFileStateContract.IS_MEDIA_TYPE;
@@ -39,16 +41,6 @@ class SyncedFileStateDAO {

    private SQLiteDatabase mDB;
    private final DbHelper mHelper;
    /*private final String[] allColumns = { SyncedFileStateContract._ID,
            SyncedFileStateContract.FILE_NAME,
            SyncedFileStateContract.LOCAL_PATH,
            SyncedFileStateContract.REMOTE_PATH,
            SyncedFileStateContract.LAST_ETAG,

            SyncedFileStateContract.LOCAL_LAST_MODIFIED,
            SyncedFileStateContract.SYNCEDFOLDER_ID,
            SyncedFileStateContract.IS_MEDIA_TYPE
    };*/

    SyncedFileStateDAO(Context context){
        this.mHelper = new DbHelper(context);
@@ -84,6 +76,7 @@ class SyncedFileStateDAO {
        values.put( LOCAL_LAST_MODIFIED, syncedFileState.getLocalLastModified() );
        values.put( SYNCEDFOLDER_ID, syncedFileState.getSyncedFolderId() );
        values.put( IS_MEDIA_TYPE, syncedFileState.isMediaType() ? 1 : 0 );
        values.put( SCANNABLE, syncedFileState.getScannable());
        return values;
    }

@@ -151,7 +144,8 @@ class SyncedFileStateDAO {
            +LAST_ETAG+", "
            +LOCAL_LAST_MODIFIED+", "
            + SYNCEDFOLDER_ID+", "
            + IS_MEDIA_TYPE
            + IS_MEDIA_TYPE+", "
            + SCANNABLE
            +" FROM "
            +TABLE_NAME;
         if (syncedFolderids.size() > 0) {
@@ -190,7 +184,8 @@ class SyncedFileStateDAO {
                +LAST_ETAG+", "
                +LOCAL_LAST_MODIFIED+", "
                + SYNCEDFOLDER_ID+", "
                + IS_MEDIA_TYPE+
                + IS_MEDIA_TYPE+", "
                + SCANNABLE+
                " FROM "
                +TABLE_NAME+" WHERE ";
        if (isLocalPath)
@@ -224,7 +219,8 @@ class SyncedFileStateDAO {
                cursor.getString(4 ),// last Etag
                cursor.getLong(5 ),//Local last modified
                cursor.getLong(6 ), //SyncedFolderID
                (cursor.getInt(7) == 1 ) //is Media Type
                (cursor.getInt(7) == 1), //is Media Type
                cursor.getInt(8) //scannable
        );
    }
}
Loading