From 5cfda24d490d04fc507d8161b706e9d954ccc702 Mon Sep 17 00:00:00 2001 From: Kaloyan Raev Date: Fri, 15 Dec 2017 15:12:58 +0200 Subject: [PATCH] #3: Experimental overlay icons on Windows (#64) Only for the Goobox folder only, but not its files and subfolders. --- README.md | 9 ++ pom.xml | 5 + src/main/java/io/goobox/sync/storj/App.java | 52 ++++++ .../io/goobox/sync/storj/CheckStateTask.java | 3 + .../sync/storj/overlay/OverlayHelper.java | 150 ++++++++++++++++++ 5 files changed, 219 insertions(+) create mode 100644 src/main/java/io/goobox/sync/storj/overlay/OverlayHelper.java diff --git a/README.md b/README.md index 67fd911..5799c67 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,12 @@ The app uses an embedded Nitrine database for storing the current sync state of - `~/Library/Application Support/Goobox` for macOS The `list-db.bat` script can be used to dump the content of the database. This might be useful for debugging. + +### Overlay icons on Windows + +Setting up the overlay icons on Windows required the following steps: + +1. Open a Command Prompt as Administrator +1. Execute the `register-dlls.bat` file +1. Restart Windows +1. Run the app as usual diff --git a/pom.xml b/pom.xml index f5b390e..abe197d 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,11 @@ libstorj-java 0.4 + + com.liferay + com.liferay.nativity + 1.0.5 + org.dizitart nitrite diff --git a/src/main/java/io/goobox/sync/storj/App.java b/src/main/java/io/goobox/sync/storj/App.java index 2068585..9ce888d 100644 --- a/src/main/java/io/goobox/sync/storj/App.java +++ b/src/main/java/io/goobox/sync/storj/App.java @@ -21,9 +21,17 @@ import java.nio.file.Path; import java.util.concurrent.CountDownLatch; +import com.liferay.nativity.control.NativityControl; +import com.liferay.nativity.control.NativityControlUtil; +import com.liferay.nativity.modules.fileicon.FileIconControl; +import com.liferay.nativity.modules.fileicon.FileIconControlCallback; +import com.liferay.nativity.modules.fileicon.FileIconControlUtil; +import com.liferay.nativity.util.OSDetector; + import io.goobox.sync.common.Utils; import io.goobox.sync.common.systemtray.ShutdownListener; import io.goobox.sync.common.systemtray.SystemTrayHelper; +import io.goobox.sync.storj.overlay.OverlayHelper; import io.storj.libstorj.Bucket; import io.storj.libstorj.CreateBucketCallback; import io.storj.libstorj.GetBucketsCallback; @@ -42,6 +50,49 @@ public class App implements ShutdownListener { public static void main(String[] args) { instance = new App(); instance.init(); + + NativityControl nativityControl = NativityControlUtil.getNativityControl(); + nativityControl.connect(); + + // Setting filter folders is required for Mac's Finder Sync plugin + // nativityControl.setFilterFolder(Utils.getSyncDir().toString()); + + /* File Icons */ + + int testIconId = 1; + + // FileIconControlCallback used by Windows and Mac + FileIconControlCallback fileIconControlCallback = new FileIconControlCallback() { + @Override + public int getIconForFile(String path) { + return 1; // testIconId; + } + }; + + FileIconControl fileIconControl = FileIconControlUtil.getFileIconControl(nativityControl, + fileIconControlCallback); + + fileIconControl.enableFileIcons(); + + String testFilePath = Utils.getSyncDir().toString(); + + if (OSDetector.isWindows()) { + // This id is determined when building the DLL + testIconId = 1; + } else if (OSDetector.isMinimumAppleVersion(OSDetector.MAC_YOSEMITE_10_10)) { + // Used by Mac Finder Sync. This unique id can be set at runtime. + testIconId = 1; + + fileIconControl.registerIconWithId("/tmp/goobox.icns", + "test label", "" + testIconId); + } else if (OSDetector.isLinux()) { + // Used by Mac Injector and Linux + testIconId = fileIconControl.registerIcon("/tmp/git-clean.png"); + } + + // FileIconControl.setFileIcon() method only used by Linux + fileIconControl.setFileIcon(testFilePath, testIconId); + nativityControl.disconnect(); } public static App getInstance() { @@ -97,6 +148,7 @@ private void init() { @Override public void shutdown() { // TODO graceful shutdown + OverlayHelper.getInstance().shutdown(); System.exit(0); } diff --git a/src/main/java/io/goobox/sync/storj/CheckStateTask.java b/src/main/java/io/goobox/sync/storj/CheckStateTask.java index 1293502..4125348 100644 --- a/src/main/java/io/goobox/sync/storj/CheckStateTask.java +++ b/src/main/java/io/goobox/sync/storj/CheckStateTask.java @@ -31,6 +31,7 @@ import io.goobox.sync.storj.db.DB; import io.goobox.sync.storj.db.SyncFile; import io.goobox.sync.storj.db.SyncState; +import io.goobox.sync.storj.overlay.OverlayHelper; import io.storj.libstorj.Bucket; import io.storj.libstorj.File; import io.storj.libstorj.ListFilesCallback; @@ -56,6 +57,7 @@ public void run() { System.out.println("Checking for changes..."); SystemTrayHelper.setSynchronizing(); + OverlayHelper.getInstance().setSynchronizing(); Storj.getInstance().listFiles(gooboxBucket, new ListFilesCallback() { @Override @@ -68,6 +70,7 @@ public void onFilesReceived(File[] files) { // Sleep some time to avoid overloading the bridge tasks.add(new SleepTask()); SystemTrayHelper.setIdle(); + OverlayHelper.getInstance().setOK(); } // Add itself to the queueAdd itself to the queue tasks.add(CheckStateTask.this); diff --git a/src/main/java/io/goobox/sync/storj/overlay/OverlayHelper.java b/src/main/java/io/goobox/sync/storj/overlay/OverlayHelper.java new file mode 100644 index 0000000..fb160a3 --- /dev/null +++ b/src/main/java/io/goobox/sync/storj/overlay/OverlayHelper.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 Kaloyan Raev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package io.goobox.sync.storj.overlay; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.attribute.DosFileAttributeView; +import java.util.ArrayList; +import java.util.List; + +import com.liferay.nativity.control.NativityControl; +import com.liferay.nativity.control.NativityControlUtil; +import com.liferay.nativity.modules.contextmenu.ContextMenuControlCallback; +import com.liferay.nativity.modules.contextmenu.ContextMenuControlUtil; +import com.liferay.nativity.modules.contextmenu.model.ContextMenuAction; +import com.liferay.nativity.modules.contextmenu.model.ContextMenuItem; +import com.liferay.nativity.modules.fileicon.FileIconControl; +import com.liferay.nativity.modules.fileicon.FileIconControlCallback; +import com.liferay.nativity.modules.fileicon.FileIconControlUtil; +import com.liferay.nativity.util.OSDetector; + +import io.goobox.sync.common.Utils; + +public class OverlayHelper { + + private NativityControl nativityControl; + private FileIconControl fileIconControl; + + private int stateIconId = 0; + + private static OverlayHelper instance; + + public static OverlayHelper getInstance() { + if (instance == null) { + instance = new OverlayHelper(); + } + return instance; + } + + public OverlayHelper() { + init(); + } + + public void setOK() { + if (!OSDetector.isWindows()) { + return; + } + + stateIconId = 1; + refresh(); + } + + public void setSynchronizing() { + if (!OSDetector.isWindows()) { + return; + } + + stateIconId = 2; + refresh(); + } + + public void shutdown() { + if (!OSDetector.isWindows()) { + return; + } + + stateIconId = 0; + refresh(); + nativityControl.disconnect(); + } + + private void refresh() { + fileIconControl.refreshIcons(new String[] { Utils.getSyncDir().toString() }); + } + + private void init() { + if (!OSDetector.isWindows()) { + return; + } + + nativityControl = NativityControlUtil.getNativityControl(); + nativityControl.connect(); + + // Setting filter folders is required for Mac's Finder Sync plugin + nativityControl.setFilterFolder(Utils.getSyncDir().toString()); + + DosFileAttributeView attr = Files.getFileAttributeView(Utils.getSyncDir(), DosFileAttributeView.class); + try { + attr.setSystem(true); + } catch (IOException e) { + e.printStackTrace(); + } + + // FileIconControlCallback used by Windows and Mac + FileIconControlCallback fileIconControlCallback = new FileIconControlCallback() { + @Override + public int getIconForFile(String path) { + if (Utils.getSyncDir().toString().equals(path)) { + return stateIconId; + } + return 0; + } + }; + + fileIconControl = FileIconControlUtil.getFileIconControl(nativityControl, fileIconControlCallback); + + fileIconControl.enableFileIcons(); + + /* Context Menus */ + + ContextMenuControlCallback contextMenuControlCallback = new ContextMenuControlCallback() { + @Override + public List getContextMenuItems(String[] paths) { + ContextMenuItem contextMenuItem = new ContextMenuItem("Goobox"); + + ContextMenuAction contextMenuAction = new ContextMenuAction() { + @Override + public void onSelection(String[] paths) { + System.out.println("Context menu selection: " + String.join("; ", paths)); + } + }; + + contextMenuItem.setContextMenuAction(contextMenuAction); + + List contextMenuItems = new ArrayList(); + contextMenuItems.add(contextMenuItem); + + // Mac Finder Sync will only show the parent level of context menus + return contextMenuItems; + } + }; + + ContextMenuControlUtil.getContextMenuControl(nativityControl, contextMenuControlCallback); + } + +}