FileObserver: start monitoring new directories when they are created
This commit is contained in:
		
							parent
							
								
									e10b182d6b
								
							
						
					
					
						commit
						f5f9075da2
					
				| 
						 | 
					@ -239,8 +239,8 @@ public class XmppConnectionService extends Service {
 | 
				
			||||||
            Environment.getExternalStorageDirectory().getAbsolutePath()
 | 
					            Environment.getExternalStorageDirectory().getAbsolutePath()
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        @Override
 | 
					        @Override
 | 
				
			||||||
        public void onEvent(int event, String path) {
 | 
					        public void onEvent(final int event, final File file) {
 | 
				
			||||||
            markFileDeleted(path);
 | 
					            markFileDeleted(file);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
 | 
					    private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
 | 
				
			||||||
| 
						 | 
					@ -1859,17 +1859,16 @@ public class XmppConnectionService extends Service {
 | 
				
			||||||
        return this.conversations;
 | 
					        return this.conversations;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void markFileDeleted(final String path) {
 | 
					    private void markFileDeleted(final File file) {
 | 
				
			||||||
        synchronized (FILENAMES_TO_IGNORE_DELETION) {
 | 
					        synchronized (FILENAMES_TO_IGNORE_DELETION) {
 | 
				
			||||||
            if (FILENAMES_TO_IGNORE_DELETION.remove(path)) {
 | 
					            if (FILENAMES_TO_IGNORE_DELETION.remove(file.getAbsolutePath())) {
 | 
				
			||||||
                Log.d(Config.LOGTAG, "ignored deletion of " + path);
 | 
					                Log.d(Config.LOGTAG, "ignored deletion of " + file.getAbsolutePath());
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        final File file = new File(path);
 | 
					 | 
				
			||||||
        final boolean isInternalFile = fileBackend.isInternalFile(file);
 | 
					        final boolean isInternalFile = fileBackend.isInternalFile(file);
 | 
				
			||||||
        final List<String> uuids = databaseBackend.markFileAsDeleted(file, isInternalFile);
 | 
					        final List<String> uuids = databaseBackend.markFileAsDeleted(file, isInternalFile);
 | 
				
			||||||
        Log.d(Config.LOGTAG, "deleted file " + path + " internal=" + isInternalFile + ", database hits=" + uuids.size());
 | 
					        Log.d(Config.LOGTAG, "deleted file " + file.getAbsolutePath() + " internal=" + isInternalFile + ", database hits=" + uuids.size());
 | 
				
			||||||
        markUuidsAsDeletedFiles(uuids);
 | 
					        markUuidsAsDeletedFiles(uuids);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,8 +6,11 @@ import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.File;
 | 
					import java.io.File;
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Stack;
 | 
					import java.util.Stack;
 | 
				
			||||||
 | 
					import java.util.concurrent.Executor;
 | 
				
			||||||
 | 
					import java.util.concurrent.Executors;
 | 
				
			||||||
import java.util.concurrent.atomic.AtomicBoolean;
 | 
					import java.util.concurrent.atomic.AtomicBoolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import eu.siacs.conversations.Config;
 | 
					import eu.siacs.conversations.Config;
 | 
				
			||||||
| 
						 | 
					@ -19,6 +22,9 @@ import eu.siacs.conversations.Config;
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public abstract class ConversationsFileObserver {
 | 
					public abstract class ConversationsFileObserver {
 | 
				
			||||||
 | 
					    private static final Executor EVENT_EXECUTOR = Executors.newSingleThreadExecutor();
 | 
				
			||||||
 | 
					    private static final int MASK = FileObserver.DELETE | FileObserver.MOVED_FROM | FileObserver.CREATE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final String path;
 | 
					    private final String path;
 | 
				
			||||||
    private final List<SingleFileObserver> mObservers = new ArrayList<>();
 | 
					    private final List<SingleFileObserver> mObservers = new ArrayList<>();
 | 
				
			||||||
| 
						 | 
					@ -34,50 +40,47 @@ public abstract class ConversationsFileObserver {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private synchronized void startWatchingInternal() {
 | 
					    private synchronized void startWatchingInternal() {
 | 
				
			||||||
        Stack<String> stack = new Stack<>();
 | 
					        final Stack<String> stack = new Stack<>();
 | 
				
			||||||
        stack.push(path);
 | 
					        stack.push(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while (!stack.empty()) {
 | 
					        while (!stack.empty()) {
 | 
				
			||||||
            if (shouldStop.get()) {
 | 
					            if (shouldStop.get()) {
 | 
				
			||||||
                Log.d(Config.LOGTAG,"file observer received command to stop");
 | 
					                Log.d(Config.LOGTAG, "file observer received command to stop");
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            String parent = stack.pop();
 | 
					            final String parent = stack.pop();
 | 
				
			||||||
            mObservers.add(new SingleFileObserver(parent, FileObserver.DELETE| FileObserver.MOVED_FROM));
 | 
					 | 
				
			||||||
            final File path = new File(parent);
 | 
					            final File path = new File(parent);
 | 
				
			||||||
 | 
					            mObservers.add(new SingleFileObserver(path, MASK));
 | 
				
			||||||
            final File[] files = path.listFiles();
 | 
					            final File[] files = path.listFiles();
 | 
				
			||||||
            if (files == null) {
 | 
					            for (final File file : (files == null ? new File[0] : files)) {
 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for(File file : files) {
 | 
					 | 
				
			||||||
                if (shouldStop.get()) {
 | 
					                if (shouldStop.get()) {
 | 
				
			||||||
                    Log.d(Config.LOGTAG,"file observer received command to stop");
 | 
					                    Log.d(Config.LOGTAG, "file observer received command to stop");
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (file.isDirectory() && file.getName().charAt(0) != '.') {
 | 
					                if (file.isDirectory() && file.getName().charAt(0) != '.') {
 | 
				
			||||||
                    final String currentPath = file.getAbsolutePath();
 | 
					                    final String currentPath = file.getAbsolutePath();
 | 
				
			||||||
                    if (depth(file) <= 8 && !stack.contains(currentPath) && !observing(currentPath)) {
 | 
					                    if (depth(file) <= 8 && !stack.contains(currentPath) && !observing(file)) {
 | 
				
			||||||
                        stack.push(currentPath);
 | 
					                        stack.push(currentPath);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        for(FileObserver observer : mObservers) {
 | 
					        for (FileObserver observer : mObservers) {
 | 
				
			||||||
            observer.startWatching();
 | 
					            observer.startWatching();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static int depth(File file) {
 | 
					    private static int depth(File file) {
 | 
				
			||||||
        int depth = 0;
 | 
					        int depth = 0;
 | 
				
			||||||
        while((file = file.getParentFile()) != null) {
 | 
					        while ((file = file.getParentFile()) != null) {
 | 
				
			||||||
            depth++;
 | 
					            depth++;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return depth;
 | 
					        return depth;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private boolean observing(String path) {
 | 
					    private boolean observing(final File path) {
 | 
				
			||||||
        for(SingleFileObserver observer : mObservers) {
 | 
					        for (final SingleFileObserver observer : mObservers) {
 | 
				
			||||||
            if(path.equals(observer.path)) {
 | 
					            if (path.equals(observer.path)) {
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -90,13 +93,13 @@ public abstract class ConversationsFileObserver {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private synchronized void stopWatchingInternal() {
 | 
					    private synchronized void stopWatchingInternal() {
 | 
				
			||||||
        for(FileObserver observer : mObservers) {
 | 
					        for (FileObserver observer : mObservers) {
 | 
				
			||||||
            observer.stopWatching();
 | 
					            observer.stopWatching();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        mObservers.clear();
 | 
					        mObservers.clear();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract public void onEvent(int event, String path);
 | 
					    abstract public void onEvent(final int event, File path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void restartWatching() {
 | 
					    public void restartWatching() {
 | 
				
			||||||
        stopWatching();
 | 
					        stopWatching();
 | 
				
			||||||
| 
						 | 
					@ -104,21 +107,33 @@ public abstract class ConversationsFileObserver {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private class SingleFileObserver extends FileObserver {
 | 
					    private class SingleFileObserver extends FileObserver {
 | 
				
			||||||
        private final String path;
 | 
					        private final File path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SingleFileObserver(String path, int mask) {
 | 
					        SingleFileObserver(final File path, final int mask) {
 | 
				
			||||||
            super(path, mask);
 | 
					            super(path.getAbsolutePath(), mask);
 | 
				
			||||||
            this.path = path;
 | 
					            this.path = path;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Override
 | 
					        @Override
 | 
				
			||||||
        public void onEvent(int event, String filename) {
 | 
					        public void onEvent(final int event, final String filename) {
 | 
				
			||||||
            if (filename == null) {
 | 
					            if (filename == null) {
 | 
				
			||||||
                Log.d(Config.LOGTAG,"ignored file event with NULL filename (event="+event+")");
 | 
					                Log.d(Config.LOGTAG, "ignored file event with NULL filename (event=" + event + ")");
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ConversationsFileObserver.this.onEvent(event, path+'/'+filename);
 | 
					            EVENT_EXECUTOR.execute(() -> {
 | 
				
			||||||
 | 
					                final File file = new File(this.path, filename);
 | 
				
			||||||
 | 
					                if ((event & FileObserver.ALL_EVENTS) == FileObserver.CREATE) {
 | 
				
			||||||
 | 
					                    if (file.isDirectory()) {
 | 
				
			||||||
 | 
					                        Log.d(Config.LOGTAG, "file observer observed new directory creation " + file);
 | 
				
			||||||
 | 
					                        if (!observing(file)) {
 | 
				
			||||||
 | 
					                            final SingleFileObserver observer = new SingleFileObserver(file, MASK);
 | 
				
			||||||
 | 
					                            observer.startWatching();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ConversationsFileObserver.this.onEvent(event, file);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue