feat: introduce idCache for pCloud
This commit is contained in:
parent
7dc9456d97
commit
81ee67b378
@ -30,8 +30,8 @@ class PCloudCloudContentRepository extends InterceptingCloudContentRepository<PC
|
||||
|
||||
private final PCloudCloud cloud;
|
||||
|
||||
public PCloudCloudContentRepository(PCloudCloud cloud, Context context) {
|
||||
super(new Intercepted(cloud, context));
|
||||
public PCloudCloudContentRepository(PCloudCloud cloud, Context context, PCloudIdCache idCache) {
|
||||
super(new Intercepted(cloud, context, idCache));
|
||||
this.cloud = cloud;
|
||||
}
|
||||
|
||||
@ -57,8 +57,8 @@ class PCloudCloudContentRepository extends InterceptingCloudContentRepository<PC
|
||||
|
||||
private final PCloudImpl cloud;
|
||||
|
||||
public Intercepted(PCloudCloud cloud, Context context) {
|
||||
this.cloud = new PCloudImpl(cloud, context);
|
||||
public Intercepted(PCloudCloud cloud, Context context, PCloudIdCache idCache) {
|
||||
this.cloud = new PCloudImpl(cloud, context, idCache);
|
||||
}
|
||||
|
||||
public PCloudFolder root(PCloudCloud cloud) {
|
||||
@ -160,7 +160,7 @@ class PCloudCloudContentRepository extends InterceptingCloudContentRepository<PC
|
||||
try {
|
||||
return cloud.write(uploadFile, data, progressAware, replace, size);
|
||||
} catch (ApiError | IOException e) {
|
||||
if (((ApiError)e).errorCode() == PCloudApiErrorCodes.FILE_NOT_FOUND.getValue()) {
|
||||
if (e instanceof ApiError && ((ApiError)e).errorCode() == PCloudApiErrorCodes.FILE_NOT_FOUND.getValue()) {
|
||||
throw new NoSuchCloudFileException(uploadFile.getName());
|
||||
}
|
||||
throw new FatalBackendException(e);
|
||||
@ -172,7 +172,7 @@ class PCloudCloudContentRepository extends InterceptingCloudContentRepository<PC
|
||||
try {
|
||||
cloud.read(file, data, progressAware);
|
||||
} catch (ApiError | IOException e) {
|
||||
if (((ApiError)e).errorCode() == PCloudApiErrorCodes.FILE_NOT_FOUND.getValue()) {
|
||||
if (e instanceof ApiError && ((ApiError)e).errorCode() == PCloudApiErrorCodes.FILE_NOT_FOUND.getValue()) {
|
||||
throw new NoSuchCloudFileException(file.getName());
|
||||
}
|
||||
throw new FatalBackendException(e);
|
||||
@ -184,7 +184,7 @@ class PCloudCloudContentRepository extends InterceptingCloudContentRepository<PC
|
||||
try {
|
||||
cloud.delete(node);
|
||||
} catch (ApiError | IOException e) {
|
||||
if (((ApiError)e).errorCode() == PCloudApiErrorCodes.FILE_NOT_FOUND.getValue()) {
|
||||
if (e instanceof ApiError && ((ApiError)e).errorCode() == PCloudApiErrorCodes.FILE_NOT_FOUND.getValue()) {
|
||||
throw new NoSuchCloudFileException(node.getName());
|
||||
}
|
||||
throw new FatalBackendException(e);
|
||||
|
@ -16,10 +16,12 @@ import static org.cryptomator.domain.CloudType.PCLOUD;
|
||||
public class PCloudCloudContentRepositoryFactory implements CloudContentRepositoryFactory {
|
||||
|
||||
private final Context context;
|
||||
private final PCloudIdCache idCache;
|
||||
|
||||
@Inject
|
||||
public PCloudCloudContentRepositoryFactory(Context context) {
|
||||
public PCloudCloudContentRepositoryFactory(Context context, PCloudIdCache idCache) {
|
||||
this.context = context;
|
||||
this.idCache = idCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -29,7 +31,7 @@ public class PCloudCloudContentRepositoryFactory implements CloudContentReposito
|
||||
|
||||
@Override
|
||||
public CloudContentRepository cloudContentRepositoryFor(Cloud cloud) {
|
||||
return new PCloudCloudContentRepository((PCloudCloud) cloud, context);
|
||||
return new PCloudCloudContentRepository((PCloudCloud) cloud, context, idCache);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,8 +8,7 @@ import org.cryptomator.util.Optional;
|
||||
|
||||
class PCloudCloudNodeFactory {
|
||||
|
||||
public static PCloudFile from(PCloudFolder parent, RemoteFile metadata) {
|
||||
|
||||
public static PCloudFile file(PCloudFolder parent, RemoteFile metadata) {
|
||||
return new PCloudFile(parent, metadata.fileId(), metadata.name(), getNodePath(parent, metadata.name()), Optional.ofNullable(metadata.size()), Optional.ofNullable(metadata.lastModified()));
|
||||
}
|
||||
|
||||
@ -17,23 +16,39 @@ class PCloudCloudNodeFactory {
|
||||
return new PCloudFile(parent, null, name, path, size, Optional.empty());
|
||||
}
|
||||
|
||||
public static PCloudFolder from(PCloudFolder parent, RemoteFolder metadata) {
|
||||
return new PCloudFolder(parent, metadata.folderId(), metadata.name(), getNodePath(parent, metadata.name()));
|
||||
public static PCloudFile file(PCloudFolder parent, String name, Optional<Long> size) {
|
||||
return new PCloudFile(parent, null, name, getNodePath(parent, name), size, Optional.empty());
|
||||
}
|
||||
|
||||
private static String getNodePath(PCloudFolder parent, String name) {
|
||||
return parent.getPath() + "/" + name;
|
||||
public static PCloudFile file(PCloudFolder folder, String name, Optional<Long> size, String path, Long fileId) {
|
||||
return new PCloudFile(folder, fileId, name, path, size, Optional.empty());
|
||||
}
|
||||
|
||||
public static PCloudFolder folder(PCloudFolder parent, RemoteFolder metadata) {
|
||||
return new PCloudFolder(parent, metadata.folderId(), metadata.name(), getNodePath(parent, metadata.name()));
|
||||
}
|
||||
|
||||
public static PCloudFolder folder(PCloudFolder parent, String name, String path) {
|
||||
return new PCloudFolder(parent, null, name, path);
|
||||
}
|
||||
|
||||
public static PCloudFolder folder(PCloudFolder parent, String name) {
|
||||
return new PCloudFolder(parent, null, name, getNodePath(parent, name));
|
||||
}
|
||||
|
||||
public static PCloudFolder folder(PCloudFolder parent, String name, String path, Long folderId) {
|
||||
return new PCloudFolder(parent, folderId, name, path);
|
||||
}
|
||||
|
||||
public static String getNodePath(PCloudFolder parent, String name) {
|
||||
return parent.getPath() + "/" + name;
|
||||
}
|
||||
|
||||
public static PCloudNode from(PCloudFolder parent, RemoteEntry metadata) {
|
||||
if (metadata instanceof RemoteFile) {
|
||||
return from(parent, (RemoteFile) metadata);
|
||||
return file(parent, metadata.asFile());
|
||||
} else {
|
||||
return from(parent, (RemoteFolder) metadata);
|
||||
return folder(parent, metadata.asFolder());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,77 @@
|
||||
package org.cryptomator.data.cloud.pcloud;
|
||||
|
||||
import android.util.LruCache;
|
||||
|
||||
import org.cryptomator.domain.CloudFolder;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
class PCloudIdCache {
|
||||
|
||||
private final LruCache<String, NodeInfo> cache;
|
||||
|
||||
@Inject
|
||||
PCloudIdCache() {
|
||||
cache = new LruCache<>(1000);
|
||||
}
|
||||
|
||||
public NodeInfo get(String path) {
|
||||
return cache.get(path);
|
||||
}
|
||||
|
||||
<T extends PCloudIdCloudNode> T cache(T value) {
|
||||
add(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void add(PCloudIdCloudNode node) {
|
||||
add(node.getPath(), new NodeInfo(node));
|
||||
}
|
||||
|
||||
private void add(String path, NodeInfo info) {
|
||||
cache.put(path, info);
|
||||
}
|
||||
|
||||
public void remove(PCloudIdCloudNode node) {
|
||||
remove(node.getPath());
|
||||
}
|
||||
|
||||
private void remove(String path) {
|
||||
removeChildren(path);
|
||||
cache.remove(path);
|
||||
}
|
||||
|
||||
private void removeChildren(String path) {
|
||||
String prefix = path + '/';
|
||||
for (String key : cache.snapshot().keySet()) {
|
||||
if (key.startsWith(prefix)) {
|
||||
cache.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class NodeInfo {
|
||||
|
||||
private final Long id;
|
||||
private final boolean isFolder;
|
||||
|
||||
private NodeInfo(PCloudIdCloudNode node) {
|
||||
this(node.getId(), node instanceof CloudFolder);
|
||||
}
|
||||
|
||||
NodeInfo(Long id, boolean isFolder) {
|
||||
this.id = id;
|
||||
this.isFolder = isFolder;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean isFolder() {
|
||||
return isFolder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package org.cryptomator.data.cloud.pcloud;
|
||||
|
||||
import org.cryptomator.domain.CloudNode;
|
||||
|
||||
interface PCloudIdCloudNode extends CloudNode {
|
||||
|
||||
Long getId();
|
||||
|
||||
}
|
@ -16,10 +16,11 @@ import com.pcloud.sdk.UserInfo;
|
||||
|
||||
import org.cryptomator.data.util.CopyStream;
|
||||
import org.cryptomator.domain.CloudFile;
|
||||
import org.cryptomator.domain.CloudFolder;
|
||||
import org.cryptomator.domain.CloudNode;
|
||||
import org.cryptomator.domain.PCloudCloud;
|
||||
import org.cryptomator.domain.exception.BackendException;
|
||||
import org.cryptomator.domain.exception.CloudNodeAlreadyExistsException;
|
||||
import org.cryptomator.domain.exception.NoSuchCloudFileException;
|
||||
import org.cryptomator.domain.exception.authentication.NoAuthenticationProvidedException;
|
||||
import org.cryptomator.domain.usecases.ProgressAware;
|
||||
import org.cryptomator.domain.usecases.cloud.DataSource;
|
||||
@ -43,16 +44,20 @@ import okio.Source;
|
||||
import static org.cryptomator.domain.usecases.cloud.Progress.progress;
|
||||
|
||||
class PCloudImpl {
|
||||
|
||||
private final PCloudIdCache idCache;
|
||||
|
||||
private final PCloudClientFactory clientFactory = new PCloudClientFactory();
|
||||
private final PCloudCloud cloud;
|
||||
private final RootPCloudFolder root;
|
||||
private final Context context;
|
||||
|
||||
PCloudImpl(PCloudCloud cloud, Context context) {
|
||||
PCloudImpl(PCloudCloud cloud, Context context, PCloudIdCache idCache) {
|
||||
if (cloud.accessToken() == null) {
|
||||
throw new NoAuthenticationProvidedException(cloud);
|
||||
}
|
||||
this.cloud = cloud;
|
||||
this.idCache = idCache;
|
||||
this.root = new RootPCloudFolder(cloud);
|
||||
this.context = context;
|
||||
}
|
||||
@ -83,36 +88,87 @@ class PCloudImpl {
|
||||
return folder;
|
||||
}
|
||||
|
||||
public PCloudFile file(CloudFolder folder, String name) {
|
||||
return file(folder, name, Optional.empty());
|
||||
public PCloudFile file(PCloudFolder parent, String name) {
|
||||
return file(parent, name, Optional.empty());
|
||||
}
|
||||
|
||||
public PCloudFile file(CloudFolder folder, String name, Optional<Long> size) {
|
||||
public PCloudFile file(PCloudFolder parent, String name, Optional<Long> size) {
|
||||
if (parent.getId() == null) {
|
||||
return PCloudCloudNodeFactory.file(parent, name, size);
|
||||
}
|
||||
String path = PCloudCloudNodeFactory.getNodePath(parent, name);
|
||||
PCloudIdCache.NodeInfo nodeInfo = idCache.get(path);
|
||||
if (nodeInfo != null && !nodeInfo.isFolder()) {
|
||||
return PCloudCloudNodeFactory.file(parent, name, size, path, nodeInfo.getId());
|
||||
}
|
||||
|
||||
Optional<RemoteEntry> file = findEntry(parent.getId(), name, false);
|
||||
if (file.isPresent()) {
|
||||
return idCache.cache(PCloudCloudNodeFactory.file(parent, file.get().asFile()));
|
||||
}
|
||||
|
||||
return PCloudCloudNodeFactory.file( //
|
||||
(PCloudFolder) folder, //
|
||||
parent, //
|
||||
name, //
|
||||
size, //
|
||||
folder.getPath() + '/' + name);
|
||||
parent.getPath() + '/' + name);
|
||||
}
|
||||
|
||||
public PCloudFolder folder(CloudFolder folder, String name) {
|
||||
return PCloudCloudNodeFactory.folder( //
|
||||
(PCloudFolder) folder, //
|
||||
name, //
|
||||
folder.getPath() + '/' + name);
|
||||
public PCloudFolder folder(PCloudFolder parent, String name) {
|
||||
if (parent.getId() == null) {
|
||||
return PCloudCloudNodeFactory.folder(parent, name);
|
||||
}
|
||||
String path = PCloudCloudNodeFactory.getNodePath(parent, name);
|
||||
PCloudIdCache.NodeInfo nodeInfo = idCache.get(path);
|
||||
if (nodeInfo != null && nodeInfo.isFolder()) {
|
||||
return PCloudCloudNodeFactory.folder( //
|
||||
parent, //
|
||||
name, //
|
||||
path, //
|
||||
nodeInfo.getId());
|
||||
}
|
||||
|
||||
Optional<RemoteEntry> folder = findEntry(parent.getId(), name, true);
|
||||
if (folder.isPresent()) {
|
||||
return idCache.cache(PCloudCloudNodeFactory.folder(parent, folder.get().asFolder()));
|
||||
}
|
||||
return PCloudCloudNodeFactory.folder(parent, name, parent.getPath() + '/' + name);
|
||||
}
|
||||
|
||||
public boolean exists(CloudNode node) throws ApiError, IOException {
|
||||
private Optional<RemoteEntry> findEntry(Long folderId, String name, boolean isFolder) {
|
||||
try {
|
||||
RemoteFolder remoteFolder = client().listFolder(folderId).execute();
|
||||
for (RemoteEntry remoteEntry : remoteFolder.children()) {
|
||||
if (isFolder) {
|
||||
if (remoteEntry.isFolder() && remoteEntry.name().equals(name)) {
|
||||
return Optional.of(remoteEntry);
|
||||
}
|
||||
} else {
|
||||
if (remoteEntry.isFile() && remoteEntry.name().equals(name)) {
|
||||
return Optional.of(remoteEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
} catch(ApiError | IOException ex) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean exists(PCloudNode node) throws ApiError, IOException {
|
||||
try {
|
||||
if (node instanceof PCloudFolder) {
|
||||
client().listFolder(((PCloudFolder) node).getPath()).execute();
|
||||
RemoteFolder remoteFolder = client().listFolder(node.getPath()).execute();
|
||||
idCache.add(PCloudCloudNodeFactory.folder(node.getParent(), remoteFolder));
|
||||
return true;
|
||||
} else {
|
||||
client().stat(((PCloudFile)node).getPath()).execute();
|
||||
RemoteFile remoteFile = client().stat(node.getPath()).execute();
|
||||
idCache.add(PCloudCloudNodeFactory.file(node.getParent(), remoteFile));
|
||||
return true;
|
||||
}
|
||||
} catch (ApiError e) {
|
||||
if (e.errorCode() == PCloudApiErrorCodes.DIRECTORY_DOES_NOT_EXIST.getValue()
|
||||
|| e.errorCode() == PCloudApiErrorCodes.COMPONENT_OF_PARENT_DIRECTORY_DOES_NOT_EXIST.getValue()
|
||||
|| e.errorCode() == PCloudApiErrorCodes.INVALID_FILE_OR_FOLDER_NAME.getValue()
|
||||
|| e.errorCode() == PCloudApiErrorCodes.FILE_OR_FOLDER_NOT_FOUND.getValue()) {
|
||||
return false;
|
||||
@ -121,56 +177,60 @@ class PCloudImpl {
|
||||
}
|
||||
}
|
||||
|
||||
public List<PCloudNode> list(CloudFolder folder) throws ApiError, IOException {
|
||||
public List<PCloudNode> list(PCloudFolder folder) throws ApiError, IOException {
|
||||
List<PCloudNode> result = new ArrayList<>();
|
||||
|
||||
Long folderId = ((PCloudFolder)folder).getId();
|
||||
Long folderId = folder.getId();
|
||||
RemoteFolder listFolderResult;
|
||||
if (folderId == null) {
|
||||
listFolderResult = client().listFolder(folder.getPath()).execute();
|
||||
} else {
|
||||
listFolderResult = client() //
|
||||
.listFolder(((PCloudFolder) folder).getId()) //
|
||||
.listFolder(folder.getId()) //
|
||||
.execute();
|
||||
}
|
||||
|
||||
List<RemoteEntry> entryMetadata = listFolderResult.children();
|
||||
for (RemoteEntry metadata : entryMetadata) {
|
||||
result.add(PCloudCloudNodeFactory.from( //
|
||||
(PCloudFolder) folder, //
|
||||
metadata));
|
||||
result.add(PCloudCloudNodeFactory.from(folder, metadata));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public PCloudFolder create(CloudFolder folder) throws ApiError, IOException {
|
||||
RemoteFolder createFolderResult = client() //
|
||||
.createFolder(((PCloudFolder)folder.getParent()).getId(), folder.getName()) //
|
||||
public PCloudFolder create(PCloudFolder folder) throws ApiError, IOException {
|
||||
RemoteFolder createdFolder = client() //
|
||||
.createFolder(folder.getParent().getId(), folder.getName()) //
|
||||
.execute();
|
||||
|
||||
return PCloudCloudNodeFactory.from( //
|
||||
(PCloudFolder) folder.getParent(), //
|
||||
createFolderResult.asFolder());
|
||||
return idCache.cache( //
|
||||
PCloudCloudNodeFactory.folder(folder.getParent(), createdFolder));
|
||||
}
|
||||
|
||||
public CloudNode move(CloudNode source, CloudNode target) throws ApiError, IOException {
|
||||
public CloudNode move(PCloudNode source, PCloudNode target) throws ApiError, BackendException, IOException {
|
||||
RemoteEntry relocationResult;
|
||||
if (source instanceof PCloudFolder) {
|
||||
relocationResult = client().moveFolder(((PCloudFolder) source).getId(), ((PCloudFolder) target).getId()).execute();
|
||||
} else {
|
||||
relocationResult = client().moveFile(((PCloudFile) source).getId(), ((PCloudFolder) target).getId()).execute();
|
||||
if (exists(target)) {
|
||||
throw new CloudNodeAlreadyExistsException(target.getName());
|
||||
}
|
||||
|
||||
return PCloudCloudNodeFactory.from( //
|
||||
(PCloudFolder) target.getParent(), //
|
||||
relocationResult);
|
||||
if (source instanceof PCloudFolder) {
|
||||
relocationResult = client().moveFolder(source.getId(), target.getId()).execute();
|
||||
} else {
|
||||
relocationResult = client().moveFile(source.getId(), target.getId()).execute();
|
||||
}
|
||||
|
||||
idCache.remove(source);
|
||||
return PCloudCloudNodeFactory.from(target.getParent(), relocationResult);
|
||||
}
|
||||
|
||||
public PCloudFile write(PCloudFile file, DataSource data, final ProgressAware<UploadState> progressAware, boolean replace, long size) throws ApiError, IOException, CloudNodeAlreadyExistsException {
|
||||
public PCloudFile write(PCloudFile file, DataSource data, final ProgressAware<UploadState> progressAware, boolean replace, long size)
|
||||
throws ApiError, BackendException, IOException {
|
||||
if (exists(file) && !replace) {
|
||||
throw new CloudNodeAlreadyExistsException("CloudNode already exists and replace is false");
|
||||
}
|
||||
|
||||
if (file.getParent().getId() == null) {
|
||||
throw new NoSuchCloudFileException(String.format("The parent folder of %s doesn't have a folderId. The file would remain in root folder", file.getPath()));
|
||||
}
|
||||
|
||||
progressAware.onProgress(Progress.started(UploadState.upload(file)));
|
||||
UploadOptions uploadOptions = UploadOptions.DEFAULT;
|
||||
if (replace) {
|
||||
@ -181,9 +241,7 @@ class PCloudImpl {
|
||||
|
||||
progressAware.onProgress(Progress.completed(UploadState.upload(file)));
|
||||
|
||||
return PCloudCloudNodeFactory.from( //
|
||||
file.getParent(), //
|
||||
uploadedFile);
|
||||
return idCache.cache(PCloudCloudNodeFactory.file(file.getParent(), uploadedFile));
|
||||
}
|
||||
|
||||
private RemoteFile uploadFile(final PCloudFile file, DataSource data, final ProgressAware<UploadState> progressAware, UploadOptions uploadOptions, final long size) //
|
||||
@ -208,17 +266,30 @@ class PCloudImpl {
|
||||
}
|
||||
};
|
||||
|
||||
Long parentFolderId = file.getParent().getId();
|
||||
if (parentFolderId == null) {
|
||||
parentFolderId = idCache.get(file.getParent().getPath()).getId();
|
||||
}
|
||||
|
||||
return client() //
|
||||
.createFile(file.getParent().getId(), file.getName(), pCloudDataSource, new Date(), listener, uploadOptions) //
|
||||
.createFile(parentFolderId, file.getName(), pCloudDataSource, new Date(), listener, uploadOptions) //
|
||||
.execute();
|
||||
}
|
||||
|
||||
// private Long getFolderId(PCloudFolder folder) {
|
||||
// try {
|
||||
// return client().listFolder(folder.getPath()).execute().folderId();
|
||||
// } catch(ApiError | IOException e) {
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
public void read(CloudFile file, OutputStream data, final ProgressAware<DownloadState> progressAware) throws ApiError, IOException {
|
||||
progressAware.onProgress(Progress.started(DownloadState.download(file)));
|
||||
|
||||
Long fileId = ((PCloudFile)file).getId();
|
||||
if (fileId == null) {
|
||||
fileId = client().stat(file.getPath()).execute().fileId();
|
||||
fileId = idCache.get(file.getPath()).getId();
|
||||
}
|
||||
|
||||
FileLink fileLink = client().createFileLink(fileId, DownloadOptions.DEFAULT).execute();
|
||||
@ -241,15 +312,15 @@ class PCloudImpl {
|
||||
progressAware.onProgress(Progress.completed(DownloadState.download(file)));
|
||||
}
|
||||
|
||||
public void delete(CloudNode node) throws ApiError, IOException {
|
||||
public void delete(PCloudNode node) throws ApiError, IOException {
|
||||
if (node instanceof PCloudFolder) {
|
||||
client() //
|
||||
.deleteFolder(((PCloudFolder) node).getId()).execute();
|
||||
.deleteFolder(node.getId()).execute();
|
||||
} else {
|
||||
client() //
|
||||
.deleteFile(((PCloudFile) node).getId()).execute();
|
||||
.deleteFile(node.getId()).execute();
|
||||
}
|
||||
|
||||
idCache.remove(node);
|
||||
}
|
||||
|
||||
public String currentAccount() throws ApiError, IOException {
|
||||
|
@ -2,8 +2,12 @@ package org.cryptomator.data.cloud.pcloud;
|
||||
|
||||
import org.cryptomator.domain.CloudNode;
|
||||
|
||||
interface PCloudNode extends CloudNode {
|
||||
interface PCloudNode extends PCloudIdCloudNode {
|
||||
|
||||
@Override
|
||||
Long getId();
|
||||
|
||||
@Override
|
||||
PCloudFolder getParent();
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user