Skip to content
This repository has been archived by the owner on Sep 6, 2019. It is now read-only.

Commit

Permalink
Fixes #3: Display status icon on files and folders in Windows Explorer (
Browse files Browse the repository at this point in the history
  • Loading branch information
kaloyan-raev authored Dec 22, 2017
1 parent 452478b commit 1c37ec6
Show file tree
Hide file tree
Showing 6 changed files with 385 additions and 37 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>io.goobox</groupId>
<artifactId>goobox-sync-storj</artifactId>
<version>0.0.10</version>
<version>0.0.11</version>
<packaging>jar</packaging>

<name>Goobox sync app for Storj</name>
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/goobox/sync/storj/db/DB.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import io.goobox.sync.common.Utils;
import io.goobox.sync.storj.StorjUtil;
import io.goobox.sync.storj.overlay.OverlayHelper;
import io.storj.libstorj.File;

public class DB {
Expand Down Expand Up @@ -141,6 +142,7 @@ public synchronized static void setSynced(File storjFile, Path localFile) throws
syncFile.setLocalData(localFile);
syncFile.setState(SyncState.SYNCED);
repo().update(syncFile);
OverlayHelper.getInstance().refresh(localFile);
}

public synchronized static void addForDownload(File file) {
Expand All @@ -157,6 +159,7 @@ public synchronized static void addForDownload(File storjFile, Path localFile) t
syncFile.setLocalData(localFile);
syncFile.setState(SyncState.FOR_DOWNLOAD);
repo().update(syncFile);
OverlayHelper.getInstance().refresh(localFile);
}

public synchronized static void addForUpload(Path path) throws IOException {
Expand All @@ -165,6 +168,7 @@ public synchronized static void addForUpload(Path path) throws IOException {
syncFile.setLocalData(path);
syncFile.setState(SyncState.FOR_UPLOAD);
repo().update(syncFile);
OverlayHelper.getInstance().refresh(path);
}

public synchronized static void addForUpload(File storjFile, Path localFile) throws IOException {
Expand All @@ -173,6 +177,7 @@ public synchronized static void addForUpload(File storjFile, Path localFile) thr
syncFile.setLocalData(localFile);
syncFile.setState(SyncState.FOR_UPLOAD);
repo().update(syncFile);
OverlayHelper.getInstance().refresh(localFile);
}

public synchronized static void setDownloadFailed(File storjFile, Path localFile) throws IOException {
Expand All @@ -183,6 +188,7 @@ public synchronized static void setDownloadFailed(File storjFile, Path localFile
}
syncFile.setState(SyncState.DOWNLOAD_FAILED);
repo().update(syncFile);
OverlayHelper.getInstance().refresh(localFile);
}

public synchronized static void setUploadFailed(Path path) throws IOException {
Expand All @@ -192,13 +198,15 @@ public synchronized static void setUploadFailed(Path path) throws IOException {
}
syncFile.setState(SyncState.UPLOAD_FAILED);
repo().update(syncFile);
OverlayHelper.getInstance().refresh(path);
}

public synchronized static void setForLocalDelete(Path path) throws IOException {
SyncFile syncFile = get(path);
syncFile.setLocalData(path);
syncFile.setState(SyncState.FOR_LOCAL_DELETE);
repo().update(syncFile);
OverlayHelper.getInstance().refresh(path);
}

public synchronized static void setForCloudDelete(File file) {
Expand All @@ -220,6 +228,7 @@ public synchronized static void addForCloudCreateDir(Path path) throws IOExcepti
syncFile.setLocalData(path);
syncFile.setState(SyncState.FOR_CLOUD_CREATE_DIR);
repo().update(syncFile);
OverlayHelper.getInstance().refresh(path);
}

public synchronized static void setConflict(File storjFile, Path localFile) throws IOException {
Expand All @@ -228,6 +237,7 @@ public synchronized static void setConflict(File storjFile, Path localFile) thro
syncFile.setLocalData(localFile);
syncFile.setState(SyncState.CONFLICT);
repo().update(syncFile);
OverlayHelper.getInstance().refresh(localFile);
}

public static void main(String[] args) {
Expand Down
94 changes: 58 additions & 36 deletions src/main/java/io/goobox/sync/storj/overlay/OverlayHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.DosFileAttributeView;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import com.liferay.nativity.control.NativityControl;
import com.liferay.nativity.control.NativityControlUtil;
Expand All @@ -34,13 +37,14 @@
import com.liferay.nativity.util.OSDetector;

import io.goobox.sync.common.Utils;
import io.goobox.sync.storj.db.DB;

public class OverlayHelper {
public class OverlayHelper implements FileIconControlCallback, ContextMenuControlCallback {

private NativityControl nativityControl;
private FileIconControl fileIconControl;

private int stateIconId = 0;
private int globalStateIconId = OverlayIcon.NONE.id();

private static OverlayHelper instance;

Expand All @@ -60,7 +64,7 @@ public void setOK() {
return;
}

stateIconId = 1;
globalStateIconId = OverlayIcon.OK.id();
refresh();
}

Expand All @@ -69,7 +73,7 @@ public void setSynchronizing() {
return;
}

stateIconId = 2;
globalStateIconId = OverlayIcon.SYNCING.id();
refresh();
}

Expand All @@ -78,11 +82,21 @@ public void shutdown() {
return;
}

stateIconId = 0;
globalStateIconId = OverlayIcon.NONE.id();
refresh();
nativityControl.disconnect();
}

public void refresh(Path path) {
if (fileIconControl != null && path != null) {
String[] pathAndParents = Stream.iterate(path, p -> p.getParent())
.limit(Utils.getSyncDir().relativize(path).getNameCount())
.map(Path::toString)
.toArray(String[]::new);
fileIconControl.refreshIcons(pathAndParents);
}
}

private void refresh() {
fileIconControl.refreshIcons(new String[] { Utils.getSyncDir().toString() });
}
Expand All @@ -98,53 +112,61 @@ private void init() {
// Setting filter folders is required for Mac's Finder Sync plugin
nativityControl.setFilterFolder(Utils.getSyncDir().toString());

// Make Goobox a system folder
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 = FileIconControlUtil.getFileIconControl(nativityControl, this);
fileIconControl.enableFileIcons();

/* Context Menus */
ContextMenuControlUtil.getContextMenuControl(nativityControl, this);
}

ContextMenuControlCallback contextMenuControlCallback = new ContextMenuControlCallback() {
@Override
public List<ContextMenuItem> 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);
/* FileIconControlCallback used by Windows and Mac */
@Override
public int getIconForFile(String path) {
Path p = Paths.get(path);
if (!p.startsWith(Utils.getSyncDir())) {
return OverlayIcon.NONE.id();
} else if (Utils.getSyncDir().equals(p)) {
return globalStateIconId;
} else {
try {
return Files.walk(p)
.map(DB::get)
.map(OverlayIcon::from)
.map(OverlayIcon::id)
.reduce(OverlayIcon.NONE.id(), Integer::max);
} catch (IOException e) {
e.printStackTrace();
}
}
return 0;
}

List<ContextMenuItem> contextMenuItems = new ArrayList<ContextMenuItem>();
contextMenuItems.add(contextMenuItem);
@Override
public List<ContextMenuItem> getContextMenuItems(String[] paths) {
ContextMenuItem contextMenuItem = new ContextMenuItem("Goobox");

// Mac Finder Sync will only show the parent level of context menus
return contextMenuItems;
ContextMenuAction contextMenuAction = new ContextMenuAction() {
@Override
public void onSelection(String[] paths) {
System.out.println("Context menu selection: " + String.join("; ", paths));
}
};

ContextMenuControlUtil.getContextMenuControl(nativityControl, contextMenuControlCallback);
contextMenuItem.setContextMenuAction(contextMenuAction);

List<ContextMenuItem> contextMenuItems = new ArrayList<ContextMenuItem>();
contextMenuItems.add(contextMenuItem);

// Mac Finder Sync will only show the parent level of context menus
return contextMenuItems;
}

}
47 changes: 47 additions & 0 deletions src/main/java/io/goobox/sync/storj/overlay/OverlayIcon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package io.goobox.sync.storj.overlay;

import io.goobox.sync.storj.db.SyncFile;

public enum OverlayIcon {

NONE(0), OK(1), SYNCING(2), WARNING(3), ERROR(4);

private int id;

private OverlayIcon(int id) {
this.id = id;
}

public int id() {
return id;
}

public static OverlayIcon from(SyncFile file) {
if (file == null || file.getState().isPending()) {
return OverlayIcon.SYNCING;
} else if (file.getState().isSynced()) {
return OverlayIcon.OK;
} else if (file.getState().isFailed()) {
return OverlayIcon.ERROR;
} else {
return OverlayIcon.WARNING;
}
}

}
9 changes: 9 additions & 0 deletions src/test/java/io/goobox/sync/storj/mocks/FilesMock.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
Expand All @@ -32,6 +33,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

import io.goobox.sync.common.Utils;
import mockit.Mock;
Expand Down Expand Up @@ -152,6 +154,13 @@ public Path write(Path path, byte[] bytes, OpenOption... options) throws IOExcep
return path;
}

@Mock
public Stream<Path> walk(Path start, FileVisitOption... options) throws IOException {
return Stream.of(files.toArray(new FileMock[files.size()]))
.map(FileMock::getPath)
.filter(p -> p.startsWith(start));
}

public void modifyFile(FileMock oldFile, FileMock newFile) {
if (files.contains(oldFile)) {
files.remove(oldFile);
Expand Down
Loading

0 comments on commit 1c37ec6

Please sign in to comment.