mirror of https://github.com/logseq/logseq
feat(android): add fs watcher plugin
parent
d8fc5d4a27
commit
a2ce773aba
|
@ -0,0 +1,189 @@
|
|||
package com.logseq.app;
|
||||
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.StructStat;
|
||||
import android.util.Log;
|
||||
import android.os.FileObserver;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.getcapacitor.JSObject;
|
||||
import com.getcapacitor.Plugin;
|
||||
import com.getcapacitor.annotation.CapacitorPlugin;
|
||||
import com.getcapacitor.PluginMethod;
|
||||
import com.getcapacitor.PluginCall;
|
||||
|
||||
@CapacitorPlugin(name = "FsWatcher")
|
||||
public class FsWatcher extends Plugin {
|
||||
|
||||
List<SingleFileObserver> observers;
|
||||
private String mPath;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
Log.i("FsWatcher", "fs-watcher loaded!");
|
||||
}
|
||||
|
||||
@PluginMethod()
|
||||
public void watch(PluginCall call) {
|
||||
String pathParam = call.getString("path");
|
||||
if (pathParam.startsWith("file://")) {
|
||||
pathParam = pathParam.substring(7);
|
||||
}
|
||||
File path = new File(pathParam);
|
||||
mPath = path.getAbsolutePath();
|
||||
|
||||
Log.i("FsWatcher", "watching..." + path);
|
||||
if (path == null) {
|
||||
call.reject("invalid watch path: " + path);
|
||||
return;
|
||||
}
|
||||
int mask = FileObserver.CLOSE_WRITE |
|
||||
FileObserver.MOVE_SELF | FileObserver.MOVED_FROM | FileObserver.MOVED_TO |
|
||||
FileObserver.DELETE | FileObserver.DELETE_SELF;
|
||||
|
||||
if (observers != null) {
|
||||
call.reject("Already watching");
|
||||
return;
|
||||
}
|
||||
observers = new ArrayList<SingleFileObserver>();
|
||||
observers.add(new SingleFileObserver(path, mask));
|
||||
|
||||
// NOTE: only watch first level of directory
|
||||
File[] files = path.listFiles();
|
||||
if (files != null) {
|
||||
for (int i = 0; i < files.length; ++i) {
|
||||
if (files[i].isDirectory() && !files[i].getName().startsWith(".")) {
|
||||
observers.add(new SingleFileObserver(files[i], mask));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < observers.size(); i++)
|
||||
observers.get(i).startWatching();
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
@PluginMethod()
|
||||
public void unwatch(PluginCall call) {
|
||||
Log.i("FsWatcher", "unwatching...");
|
||||
|
||||
if (observers != null) {
|
||||
for (int i = 0; i < observers.size(); ++i)
|
||||
observers.get(i).stopWatching();
|
||||
observers.clear();
|
||||
observers = null;
|
||||
}
|
||||
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
// add, change, unlink events
|
||||
public void onObserverEvent(int event, String path) {
|
||||
Log.i("FsWatcher", "got path=" + path + " event=" + event);
|
||||
|
||||
JSObject obj = new JSObject();
|
||||
String content = null;
|
||||
File f = null;
|
||||
// FIXME: Current repo/path impl requires path to be a URL, dir to be a bare path.
|
||||
obj.put("path", "file://" + path);
|
||||
obj.put("dir", mPath);
|
||||
switch (event) {
|
||||
case FileObserver.CLOSE_WRITE:
|
||||
obj.put("event", "change");
|
||||
f = new File(path);
|
||||
try {
|
||||
obj.put("stat", getFileStat(path));
|
||||
content = getFileContents(f);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ErrnoException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
obj.put("content", content);
|
||||
break;
|
||||
case FileObserver.MOVED_TO:
|
||||
case FileObserver.CREATE:
|
||||
obj.put("event", "add");
|
||||
f = new File(path);
|
||||
try {
|
||||
obj.put("stat", getFileStat(path));
|
||||
content = getFileContents(f);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ErrnoException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
obj.put("content", content);
|
||||
break;
|
||||
case FileObserver.MOVE_SELF:
|
||||
case FileObserver.MOVED_FROM:
|
||||
case FileObserver.DELETE:
|
||||
case FileObserver.DELETE_SELF:
|
||||
obj.put("event", "unlink");
|
||||
break;
|
||||
default:
|
||||
// unreachable?
|
||||
obj.put("event", "unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
notifyListeners("watcher", obj);
|
||||
}
|
||||
|
||||
public static String getFileContents(final File file) throws IOException {
|
||||
InputStream inputStream = new FileInputStream(file);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int length = 0;
|
||||
|
||||
while ((length = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, length);
|
||||
}
|
||||
|
||||
inputStream.close();
|
||||
return outputStream.toString("utf-8");
|
||||
}
|
||||
|
||||
public static JSObject getFileStat(final String path) throws ErrnoException {
|
||||
StructStat stat = Os.stat(path);
|
||||
JSObject obj = new JSObject();
|
||||
obj.put("atime", stat.st_atime);
|
||||
obj.put("mtime", stat.st_mtime);
|
||||
obj.put("ctime", stat.st_ctime);
|
||||
return obj;
|
||||
}
|
||||
|
||||
private class SingleFileObserver extends FileObserver {
|
||||
private String mPath;
|
||||
|
||||
public SingleFileObserver(String path, int mask) {
|
||||
super(path, mask);
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
public SingleFileObserver(File path, int mask) {
|
||||
super(path, mask);
|
||||
mPath = path.getAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event, String path) {
|
||||
if (path != null) {
|
||||
Log.i("FsWatcher", "got path=" + path + " event=" + event);
|
||||
if (Pattern.matches("[^.].*?\\.(md|org|css|edn|text|markdown|yml|yaml|json|js)$", path)) {
|
||||
String fullPath = mPath + "/" + path;
|
||||
FsWatcher.this.onObserverEvent(event, fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ public class MainActivity extends BridgeActivity {
|
|||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
registerPlugin(FolderPicker.class);
|
||||
registerPlugin(FsWatcher.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
[]
|
||||
(.ensureDocuments mobile-util/ios-file-container)))
|
||||
|
||||
(when (mobile-util/native-ios?)
|
||||
(when (mobile-util/is-native-platform?)
|
||||
(.addListener mobile-util/fs-watcher "watcher"
|
||||
(fn [event]
|
||||
(state/pub-event! [:file-watcher/changed event]))))
|
||||
|
@ -265,7 +265,6 @@
|
|||
(get-files [_this path-or-handle _ok-handler]
|
||||
(readdir path-or-handle))
|
||||
(watch-dir! [_this dir]
|
||||
(when (mobile-util/native-ios?)
|
||||
(p/do!
|
||||
(.unwatch mobile-util/fs-watcher)
|
||||
(.watch mobile-util/fs-watcher #js {:path dir})))))
|
||||
(p/do!
|
||||
(.unwatch mobile-util/fs-watcher)
|
||||
(.watch mobile-util/fs-watcher #js {:path dir}))))
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
(when (native-ios?)
|
||||
(defonce download-icloud-files (registerPlugin "DownloadiCloudFiles"))
|
||||
(defonce ios-file-container (registerPlugin "FileContainer")))
|
||||
(when (native-ios?)
|
||||
;; NOTE: both iOS and android share the same FsWatcher API
|
||||
(when (is-native-platform?)
|
||||
(defonce fs-watcher (registerPlugin "FsWatcher")))
|
||||
|
||||
(defn sync-icloud-repo [repo-dir]
|
||||
|
|
Loading…
Reference in New Issue