Loading app/build.gradle +1 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ plugins { def versionMajor = 1 def versionMinor = 2 def versionPatch = 0 def versionPatch = 1 Loading app/src/main/AndroidManifest.xml +0 −1 Original line number Diff line number Diff line Loading @@ -94,7 +94,6 @@ android:name=".services.ObserverService" android:enabled="true" /> <service android:name=".services.SynchronizationService" /> <receiver android:name=".receivers.BootCompletedReceiver" android:enabled="true" Loading app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java +100 −36 Original line number Diff line number Diff line Loading @@ -42,24 +42,64 @@ public class FileEventListener { } public void onEvent(int event, File file) { if (event == FileObserver.DELETE) { if (file.isHidden()) return; if (file.isDirectory()) { handleDirectoryDelete(file); handleDirectoryEvent(event, file); } else { handleFileDelete(file); handleFileEvent(event, file); } } } else if (event == FileObserver.CLOSE_WRITE) { if (file.isDirectory()) { handleDirectoryCloseWrite(file); } else { /** * Handle some file event for a file which is not a directory * @param event the event mask. CLOSE_WRITE, DELETE & MOVE_SELF are handled * @param file the file concerned by the event */ private void handleFileEvent(int event, File file) { switch(event) { case FileObserver.CLOSE_WRITE: //todo it is called two times per file except if screenshot by example or take a picture handleFileCloseWrite(file); } } else if (event == FileObserver.MOVE_SELF){ break; case FileObserver.DELETE: handleFileDelete(file); break; case FileObserver.MOVE_SELF: //todo to be able to catch that, we probably need a buffer to catch a succession (MOVE_FROM, MOVE_TO, then MOVE_SELF). Log.d(TAG, file.getAbsolutePath() + " has been moved. Not handled yet"); break; default: break; } } /** * Handle FileEvent for a directory * @param event FileEvent mask. CREATE, CLOSE_WRITE, DELETE, MOVE_SELF * @param dir directory concerned by file event */ private void handleDirectoryEvent(int event, File dir) { switch(event) { case FileObserver.CREATE: handleDirectoryCreate(dir); break; case FileObserver.CLOSE_WRITE: handleDirectoryCloseWrite(dir); break; case FileObserver.DELETE: //todo #1 Fix: never used. when done on a dir, it triggers handleFileEvent. Why ?! handleDirectoryDelete(dir); break; case FileObserver.MOVE_SELF: Log.d(TAG, dir.getAbsolutePath() + " has been moved. Not handled yet"); break; default: break; } } /** * Send syncRequest to SynchronizationService * @param request */ private void sendSyncRequestToSynchronizationService(SyncRequest request) { Log.d(TAG, "Sending a SyncRequest for " + request.getSyncedFileState().getName()); if (serviceConnection.isBoundToSynchronizationService()) { Loading @@ -70,23 +110,42 @@ public class FileEventListener { } } 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 /** * When a new directory is detected, it must be inserted in database * if it's parent directory is already in the database * @param directory */ private void handleDirectoryCreate(File directory) { Log.d(TAG, "handleDirectoryCreate(" + directory.getAbsolutePath() + ")"); 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(), ""); final SyncedFolder parentFolder = DbHelper.getSyncedFolderByLocalPath(parentPath, appContext); if (parentFolder != null) { final SyncedFolder folder = new SyncedFolder(parentFolder, directory.getName() + FileUtils.PATH_SEPARATOR, directory.lastModified(), ""); DbHelper.insertSyncedFolder(folder, appContext); } } /** * Handle CLOSE_WRITE event for a directory * todo: check in which condition a directory can generate a close_write * @param directory */ private void handleDirectoryCloseWrite(File directory) { final String fileLocalPath = CommonUtils.getLocalPath(directory); Log.d(TAG, "handleDirectoryCloseWrite(" + fileLocalPath + ")"); final SyncedFolder folder = DbHelper.getSyncedFolderByLocalPath(fileLocalPath, appContext); if (folder == null) { handleDirectoryCreate(directory); //todo check if really relevant } else { //It's a directory update folder.setLastModified(directory.lastModified()); DbHelper.updateSyncedFolder(folder, appContext); } } /** * Handle a file deletion event for a directory * @param directory */ private void handleDirectoryDelete(File directory) { final String fileLocalPath = CommonUtils.getLocalPath(directory); Log.d(TAG, "handleDirectoryDelete("+fileLocalPath+")"); Loading @@ -94,20 +153,23 @@ public class FileEventListener { 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 final SyncedFolder parentFolder = DbHelper.getSyncedFolderByLocalPath(parentPath, appContext); if (parentFolder == null ) { //if parent is not in the DB return; } 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()) { } else if (folder.isEnabled()) { folder.setEnabled(false); DbHelper.updateSyncedFolder(folder, appContext); } } } /** * handle a file close_write event for a file which is not a directory * @param file */ private void handleFileCloseWrite(File file) { final String fileLocalPath = CommonUtils.getLocalPath(file); Log.d(TAG, "handleFileCloseWrite("+fileLocalPath+")"); Loading Loading @@ -146,13 +208,16 @@ public class FileEventListener { } } /** * Handle a file deletion event for a file which is not a directory * @param file */ private void handleFileDelete(File file) { final String fileLocalPath = CommonUtils.getLocalPath(file); Log.d(TAG, "handleFileDelete("+fileLocalPath+")"); SyncedFileState fileState = DbHelper.loadSyncedFile( appContext, fileLocalPath, true); final SyncedFileState fileState = DbHelper.loadSyncedFile( appContext, fileLocalPath, true); if (fileState == null) { Log.d(TAG, "Ignore event because file is not already in database"); return; return; //Todo #1: should we call handleDirectoryDelete before to return ? } //If already in DB Loading @@ -170,7 +235,6 @@ public class FileEventListener { } public void bindToSynchronizationService(){ Log.d(TAG, "bindToSynchronizationService()"); final Intent SynchronizationServiceIntent = new Intent(appContext, SynchronizationService.class); appContext.bindService(SynchronizationServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); } Loading app/src/main/java/foundation/e/drive/database/DbHelper.java +12 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ package foundation.e.drive.database; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; Loading Loading @@ -324,4 +325,15 @@ public final class DbHelper extends SQLiteOpenHelper { } Log.e(TAG,"Failed to dump Database"); } /** * Remove syncedFileState for hidden file inserted in previous version * @param context * @throws SQLiteException if database can't be open */ public static void removeHiddenSyncedFileState(Context context) throws SQLiteException { SyncedFileStateDAO dao = openSyncedFileStateDAO(context, true); dao.deleteHiddenFileState(); dao.close(); } } app/src/main/java/foundation/e/drive/database/SyncedFileStateDAO.java +9 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,15 @@ class SyncedFileStateDAO { + " = " + id, null); } /** * Remove SyncedFileState for hidden file (starting with '.') * @return number of deleted input */ public int deleteHiddenFileState() { return mDB.delete(TABLE_NAME, FILE_NAME + " LIKE ?", new String[]{".%"}); } /** * Delete each syncedFileState which is bound to syncedFOlder with specified ID * @param folderId syncedFolder's id used as foreign key Loading Loading
app/build.gradle +1 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ plugins { def versionMajor = 1 def versionMinor = 2 def versionPatch = 0 def versionPatch = 1 Loading
app/src/main/AndroidManifest.xml +0 −1 Original line number Diff line number Diff line Loading @@ -94,7 +94,6 @@ android:name=".services.ObserverService" android:enabled="true" /> <service android:name=".services.SynchronizationService" /> <receiver android:name=".receivers.BootCompletedReceiver" android:enabled="true" Loading
app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java +100 −36 Original line number Diff line number Diff line Loading @@ -42,24 +42,64 @@ public class FileEventListener { } public void onEvent(int event, File file) { if (event == FileObserver.DELETE) { if (file.isHidden()) return; if (file.isDirectory()) { handleDirectoryDelete(file); handleDirectoryEvent(event, file); } else { handleFileDelete(file); handleFileEvent(event, file); } } } else if (event == FileObserver.CLOSE_WRITE) { if (file.isDirectory()) { handleDirectoryCloseWrite(file); } else { /** * Handle some file event for a file which is not a directory * @param event the event mask. CLOSE_WRITE, DELETE & MOVE_SELF are handled * @param file the file concerned by the event */ private void handleFileEvent(int event, File file) { switch(event) { case FileObserver.CLOSE_WRITE: //todo it is called two times per file except if screenshot by example or take a picture handleFileCloseWrite(file); } } else if (event == FileObserver.MOVE_SELF){ break; case FileObserver.DELETE: handleFileDelete(file); break; case FileObserver.MOVE_SELF: //todo to be able to catch that, we probably need a buffer to catch a succession (MOVE_FROM, MOVE_TO, then MOVE_SELF). Log.d(TAG, file.getAbsolutePath() + " has been moved. Not handled yet"); break; default: break; } } /** * Handle FileEvent for a directory * @param event FileEvent mask. CREATE, CLOSE_WRITE, DELETE, MOVE_SELF * @param dir directory concerned by file event */ private void handleDirectoryEvent(int event, File dir) { switch(event) { case FileObserver.CREATE: handleDirectoryCreate(dir); break; case FileObserver.CLOSE_WRITE: handleDirectoryCloseWrite(dir); break; case FileObserver.DELETE: //todo #1 Fix: never used. when done on a dir, it triggers handleFileEvent. Why ?! handleDirectoryDelete(dir); break; case FileObserver.MOVE_SELF: Log.d(TAG, dir.getAbsolutePath() + " has been moved. Not handled yet"); break; default: break; } } /** * Send syncRequest to SynchronizationService * @param request */ private void sendSyncRequestToSynchronizationService(SyncRequest request) { Log.d(TAG, "Sending a SyncRequest for " + request.getSyncedFileState().getName()); if (serviceConnection.isBoundToSynchronizationService()) { Loading @@ -70,23 +110,42 @@ public class FileEventListener { } } 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 /** * When a new directory is detected, it must be inserted in database * if it's parent directory is already in the database * @param directory */ private void handleDirectoryCreate(File directory) { Log.d(TAG, "handleDirectoryCreate(" + directory.getAbsolutePath() + ")"); 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(), ""); final SyncedFolder parentFolder = DbHelper.getSyncedFolderByLocalPath(parentPath, appContext); if (parentFolder != null) { final SyncedFolder folder = new SyncedFolder(parentFolder, directory.getName() + FileUtils.PATH_SEPARATOR, directory.lastModified(), ""); DbHelper.insertSyncedFolder(folder, appContext); } } /** * Handle CLOSE_WRITE event for a directory * todo: check in which condition a directory can generate a close_write * @param directory */ private void handleDirectoryCloseWrite(File directory) { final String fileLocalPath = CommonUtils.getLocalPath(directory); Log.d(TAG, "handleDirectoryCloseWrite(" + fileLocalPath + ")"); final SyncedFolder folder = DbHelper.getSyncedFolderByLocalPath(fileLocalPath, appContext); if (folder == null) { handleDirectoryCreate(directory); //todo check if really relevant } else { //It's a directory update folder.setLastModified(directory.lastModified()); DbHelper.updateSyncedFolder(folder, appContext); } } /** * Handle a file deletion event for a directory * @param directory */ private void handleDirectoryDelete(File directory) { final String fileLocalPath = CommonUtils.getLocalPath(directory); Log.d(TAG, "handleDirectoryDelete("+fileLocalPath+")"); Loading @@ -94,20 +153,23 @@ public class FileEventListener { 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 final SyncedFolder parentFolder = DbHelper.getSyncedFolderByLocalPath(parentPath, appContext); if (parentFolder == null ) { //if parent is not in the DB return; } 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()) { } else if (folder.isEnabled()) { folder.setEnabled(false); DbHelper.updateSyncedFolder(folder, appContext); } } } /** * handle a file close_write event for a file which is not a directory * @param file */ private void handleFileCloseWrite(File file) { final String fileLocalPath = CommonUtils.getLocalPath(file); Log.d(TAG, "handleFileCloseWrite("+fileLocalPath+")"); Loading Loading @@ -146,13 +208,16 @@ public class FileEventListener { } } /** * Handle a file deletion event for a file which is not a directory * @param file */ private void handleFileDelete(File file) { final String fileLocalPath = CommonUtils.getLocalPath(file); Log.d(TAG, "handleFileDelete("+fileLocalPath+")"); SyncedFileState fileState = DbHelper.loadSyncedFile( appContext, fileLocalPath, true); final SyncedFileState fileState = DbHelper.loadSyncedFile( appContext, fileLocalPath, true); if (fileState == null) { Log.d(TAG, "Ignore event because file is not already in database"); return; return; //Todo #1: should we call handleDirectoryDelete before to return ? } //If already in DB Loading @@ -170,7 +235,6 @@ public class FileEventListener { } public void bindToSynchronizationService(){ Log.d(TAG, "bindToSynchronizationService()"); final Intent SynchronizationServiceIntent = new Intent(appContext, SynchronizationService.class); appContext.bindService(SynchronizationServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); } Loading
app/src/main/java/foundation/e/drive/database/DbHelper.java +12 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ package foundation.e.drive.database; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; Loading Loading @@ -324,4 +325,15 @@ public final class DbHelper extends SQLiteOpenHelper { } Log.e(TAG,"Failed to dump Database"); } /** * Remove syncedFileState for hidden file inserted in previous version * @param context * @throws SQLiteException if database can't be open */ public static void removeHiddenSyncedFileState(Context context) throws SQLiteException { SyncedFileStateDAO dao = openSyncedFileStateDAO(context, true); dao.deleteHiddenFileState(); dao.close(); } }
app/src/main/java/foundation/e/drive/database/SyncedFileStateDAO.java +9 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,15 @@ class SyncedFileStateDAO { + " = " + id, null); } /** * Remove SyncedFileState for hidden file (starting with '.') * @return number of deleted input */ public int deleteHiddenFileState() { return mDB.delete(TABLE_NAME, FILE_NAME + " LIKE ?", new String[]{".%"}); } /** * Delete each syncedFileState which is bound to syncedFOlder with specified ID * @param folderId syncedFolder's id used as foreign key Loading