diff --git a/app/src/main/java/foundation/e/drive/EdriveApplication.java b/app/src/main/java/foundation/e/drive/EdriveApplication.java index b6f6ae3533aca2c1e3698aece545df0e162d4f0c..0543800c082fdd07941db1fe9e0905d08b4c277b 100644 --- a/app/src/main/java/foundation/e/drive/EdriveApplication.java +++ b/app/src/main/java/foundation/e/drive/EdriveApplication.java @@ -13,8 +13,11 @@ import android.accounts.AccountManager; import android.app.Application; import android.content.Context; import android.content.SharedPreferences; +import android.os.Environment; import android.util.Log; +import foundation.e.drive.FileObservers.FileEventListener; +import foundation.e.drive.FileObservers.RecursiveFileObserver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; @@ -25,6 +28,8 @@ import foundation.e.drive.utils.CommonUtils; public class EdriveApplication extends Application { private static final String TAG = "EdriveApplication"; + private RecursiveFileObserver mFileObserver = null; + @Override public void onCreate() { @@ -33,9 +38,14 @@ public class EdriveApplication extends Application { Log.i(TAG, "Starting"); resetOperationManagerSetting(); + final String pathForObserver = Environment.getExternalStorageDirectory().getAbsolutePath(); + mFileObserver = new RecursiveFileObserver(getApplicationContext(), pathForObserver, new FileEventListener()); + SharedPreferences prefs = getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); + if (prefs.getString(AccountManager.KEY_ACCOUNT_NAME, null) != null) { Log.d(TAG, "Account already registered"); + startRecursiveFileObserver(); } else { Account mAccount = CommonUtils.getAccount(getString(R.string.eelo_account_type), AccountManager.get(this)); if (mAccount != null) { @@ -56,6 +66,25 @@ public class EdriveApplication extends Application { .apply(); } + + /** + * Start Recursive FileObserver if not already watching + */ + public void startRecursiveFileObserver(){ + if (!mFileObserver.isWatching()) { + mFileObserver.startWatching(); + Log.d(TAG, "Starting RecursiveFileObserver on media's root folder"); + } + else { + Log.w(TAG, "RecursiveFileObserver (for media's root folder) was already running"); + } + } + + public void stopRecursiveFileObserver(){ + mFileObserver.stopWatching(); + Log.d(TAG, "RecursiveFileObserver on media's root folder stops watching "); + } + @Override public void onLowMemory() { super.onLowMemory(); diff --git a/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java new file mode 100644 index 0000000000000000000000000000000000000000..bcceca7b84ca2af9839339d5341490610fa07728 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java @@ -0,0 +1,30 @@ +/* + * Copyright © Narinder Rana (/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 + * http://www.gnu.org/licenses/gpl.html + */ + +package foundation.e.drive.FileObservers; + +import android.os.FileObserver; +import android.util.Log; + +import java.io.File; + +/** + * @author Narinder Rana + * @author vincent Bourgmayer + */ +public class FileEventListener { + private final static String TAG = FileEventListener.class.getSimpleName(); + + 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()); + } + } +} diff --git a/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java b/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java new file mode 100644 index 0000000000000000000000000000000000000000..1e4a98741522f50a92de2de4b61db631cf237a66 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java @@ -0,0 +1,174 @@ +/* + * Copyright © Narinder Rana (/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 + * http://www.gnu.org/licenses/gpl.html + */ + +package foundation.e.drive.FileObservers; + +import android.content.Context; +import android.os.FileObserver; + +import java.io.File; +import java.io.FileFilter; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +import foundation.e.drive.database.DbHelper; +import foundation.e.drive.models.SyncedFolder; + +/** + * @author Narinder Rana + * @author Vincent Bourgmayer + */ +public class RecursiveFileObserver extends FileObserver { + private final HashMap observers = new HashMap<>(); + private static final FileFilter WATCHABLE_DIRECTORIES_FILTER = new FileFilter() { + @Override + public boolean accept(File file) { + return file.isDirectory() && !file.getName().startsWith("."); + } + }; + + private boolean watching = false; + private final Context applicationContext; + private String path; + private int mask; + private FileEventListener listener; + + public RecursiveFileObserver(Context applicationContext, String path, FileEventListener listener) { + this(applicationContext, path, ALL_EVENTS, listener); + } + + public RecursiveFileObserver(Context applicationContext, String path, int mask, FileEventListener listener) { + super(path, mask); + this.path = path; + this.mask = mask | FileObserver.CREATE | FileObserver.DELETE_SELF; + this.listener = listener; + this.applicationContext = applicationContext; + } + + + @Override + public void onEvent(int event, String path) { + File file; + if (path == null) { + file = new File(this.path); + } else { + file = new File(this.path, path); + } + + notify(event, file); + } + + private void notify(int event, File file) { + if (listener != null) { + listener.onEvent(event & FileObserver.ALL_EVENTS, file); + } + } + + @Override + public void startWatching() { + Stack stack = new Stack<>(); + + List mSyncedFolders = DbHelper.getAllSyncedFolders(applicationContext); + if (!mSyncedFolders.isEmpty()){ + for (SyncedFolder syncedFolder:mSyncedFolders){ + stack.push(syncedFolder.getLocalFolder()); + stack.push(syncedFolder.getRemoteFolder()); + } + watching = true; + } + + // Recursively watch all child directories + while (!stack.empty()) { + String parent = stack.pop(); + startWatching(parent); + + File path = new File(parent); + File[] files = path.listFiles(WATCHABLE_DIRECTORIES_FILTER); + if (files != null) { + for (File file : files) { + stack.push(file.getAbsolutePath()); + } + } + } + } + + /** + * Start watching a single file + * @param path + */ + private void startWatching(String path) { + synchronized (observers) { + FileObserver observer = observers.remove(path); + if (observer != null) { + observer.stopWatching(); + } + observer = new SingleFileObserver(path, mask); + observer.startWatching(); + observers.put(path, observer); + } + } + + @Override + public void stopWatching() { + for (FileObserver observer : observers.values()) { + observer.stopWatching(); + } + observers.clear(); + watching = false; + } + + /** + * Stop watching a single file + * @param path + */ + private void stopWatching(String path) { + synchronized (observers) { + FileObserver observer = observers.remove(path); + if (observer != null) { + observer.stopWatching(); + } + } + } + + public boolean isWatching(){ + return watching; + } + + private class SingleFileObserver extends FileObserver { + private String filePath; + + public SingleFileObserver(String path, int mask) { + super(path, mask); + filePath = path; + } + + @Override + public void onEvent(int event, String path) { + File file; + if (path == null) { + file = new File(filePath); + } else { + file = new File(filePath, path); + } + + switch (event & FileObserver.ALL_EVENTS) { + case DELETE_SELF: + RecursiveFileObserver.this.stopWatching(filePath); + break; + case CREATE: + if (WATCHABLE_DIRECTORIES_FILTER.accept(file)) { + RecursiveFileObserver.this.startWatching(file.getAbsolutePath()); + } + break; + } + + RecursiveFileObserver.this.notify(event, file); + } + } +} diff --git a/app/src/main/java/foundation/e/drive/models/FileObserver.java b/app/src/main/java/foundation/e/drive/models/FileObserver.java new file mode 100644 index 0000000000000000000000000000000000000000..6407b8a66b1345c6b00ec03cf833dde3f97c5ee1 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/models/FileObserver.java @@ -0,0 +1,33 @@ +/* + * Copyright © Narinder Rana (/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 + * http://www.gnu.org/licenses/gpl.html + */ + +package foundation.e.drive.models; + +import java.io.File; +import java.io.Serializable; +import java.util.List; + +/** + * @author Narinder Rana + */ +public class FileObserver implements Serializable { + + private List files; + public FileObserver(List files) { + this.files = files; + + } + + public List getFiles() { + return files; + } + + public void setFiles(List files) { + this.files = files; + } +} diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index ad6822507535487a09b4e78b7ccb9c4a76c5b9c6..73a23a64d57b6b330038feb0be15100b15a64b5f 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import foundation.e.drive.EdriveApplication; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.operations.CreateInitialFolderRemoteOperation; import foundation.e.drive.utils.AppConstants; @@ -280,7 +281,7 @@ public class InitializerService extends Service implements OnRemoteOperationList CommonUtils.registerPeriodicFullScanWorker(WorkManager.getInstance(appContext)); //all folder have been created - + ((EdriveApplication) this.getApplication() ).startRecursiveFileObserver(); //Immediatly start ObserverService to not have to wait 30 minutes. Intent observersServiceIntent = new Intent(getApplicationContext(), foundation.e.drive.services.ObserverService.class); diff --git a/app/src/main/java/foundation/e/drive/services/ResetService.java b/app/src/main/java/foundation/e/drive/services/ResetService.java index 7b7c9d4e6e2ccba4fbd980968addeafc2bf2022e..3776c6e7de6003b3b6435cfeb7948f019b84dcb8 100644 --- a/app/src/main/java/foundation/e/drive/services/ResetService.java +++ b/app/src/main/java/foundation/e/drive/services/ResetService.java @@ -18,6 +18,7 @@ import android.util.Log; import java.io.File; +import foundation.e.drive.EdriveApplication; import foundation.e.drive.database.DbHelper; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; @@ -66,6 +67,8 @@ public class ResetService extends Service { WorkManager.getInstance(this).cancelAllWorkByTag(AppConstants.WORK_GENERIC_TAG); + ( (EdriveApplication) this.getApplication() ).stopRecursiveFileObserver(); + //3. delete DB result = this.deleteDatabase(DbHelper.DATABASE_NAME); Log.d(TAG, "Remove Database: "+result);