From 3ef6546961adf39c2ce508560376c2c49f28ec16 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 9 Oct 2021 13:32:12 -0300 Subject: [PATCH 01/46] Add new file mechanism --- .../filesystem/HybridFileParcelable.java | 3 + .../amaze/filemanager/utils/OnFileFound.kt | 3 +- .../filetypes/AmazeDeleteOnExitHook.java | 79 + .../filesystem/filetypes/AmazeFile.java | 1905 +++++++++++++++++ .../filesystem/filetypes/AmazeFileFilter.java | 51 + .../filesystem/filetypes/AmazeFileSystem.java | 358 ++++ .../filetypes/AmazeFilenameFilter.java | 55 + .../filesystem/filetypes/OnAmazeFileFound.kt | 29 + 8 files changed, 2481 insertions(+), 2 deletions(-) create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileFilter.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilenameFilter.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/OnAmazeFileFound.kt diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFileParcelable.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFileParcelable.java index a4db3e823c..002b7d6298 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFileParcelable.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFileParcelable.java @@ -37,9 +37,12 @@ import jcifs.smb.SmbException; import jcifs.smb.SmbFile; +import kotlin.Deprecated; + import net.schmizz.sshj.sftp.RemoteResourceInfo; import net.schmizz.sshj.xfer.FilePermission; +@Deprecated(message = "Use [AmazeFile]") public class HybridFileParcelable extends HybridFile implements Parcelable { private long date, size; diff --git a/app/src/main/java/com/amaze/filemanager/utils/OnFileFound.kt b/app/src/main/java/com/amaze/filemanager/utils/OnFileFound.kt index f593791338..181e3ca33a 100644 --- a/app/src/main/java/com/amaze/filemanager/utils/OnFileFound.kt +++ b/app/src/main/java/com/amaze/filemanager/utils/OnFileFound.kt @@ -24,9 +24,8 @@ import com.amaze.filemanager.filesystem.HybridFileParcelable /** * This allows the caller of a function to know when a file has ben found and deal with it ASAP - * - * @author Emmanuel on 21/9/2017, at 15:23. */ +@Deprecated(message = "Replaced with OnAmazeFileFound") interface OnFileFound { @Suppress("UndocumentedPublicFunction") fun onFileFound(file: HybridFileParcelable) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java new file mode 100644 index 0000000000..6bd5dd72d8 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.amaze.filemanager.file_operations.filesystem.filetypes; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; + +/** + * This class holds a set of filenames to be deleted on VM exit through a shutdown hook. + * A set is used both to prevent double-insertion of the same file as well as offer + * quick removal. + */ + +public class AmazeDeleteOnExitHook { + private static LinkedHashSet files = new LinkedHashSet<>(); + static { + // BEGIN Android-changed: Use Runtime.addShutdownHook() rather than SharedSecrets. + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + runHooks(); + } + }); + // END Android-changed: Use Runtime.addShutdownHook() rather than SharedSecrets. + } + + private AmazeDeleteOnExitHook() {} + + static synchronized void add(String file) { + if(files == null) { + // DeleteOnExitHook is running. Too late to add a file + throw new IllegalStateException("Shutdown in progress"); + } + + files.add(file); + } + + static void runHooks() { + LinkedHashSet theFiles; + + synchronized (AmazeDeleteOnExitHook.class) { + theFiles = files; + files = null; + } + + ArrayList toBeDeleted = new ArrayList<>(theFiles); + + // reverse the list to maintain previous jdk deletion order. + // Last in first deleted. + Collections.reverse(toBeDeleted); + for (String filename : toBeDeleted) { + (new File(filename)).delete(); + } + } +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java new file mode 100644 index 0000000000..c3f8b26600 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -0,0 +1,1905 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.amaze.filemanager.file_operations.filesystem.filetypes; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import kotlin.Deprecated; +import kotlin.NotImplementedError; +import kotlin.random.Random; + +// Android-added: Info about UTF-8 usage in filenames. +/** + * An abstract representation of file and directory pathnames. + * + *

User interfaces and operating systems use system-dependent pathname + * strings to name files and directories. This class presents an + * abstract, system-independent view of hierarchical pathnames. An + * abstract pathname has two components: + * + *

    + *
  1. An optional system-dependent prefix string, + * such as a disk-drive specifier, "/" for the UNIX root + * directory, or "\\\\" for a Microsoft Windows UNC pathname, and + *
  2. A sequence of zero or more string names. + *
+ * + * The first name in an abstract pathname may be a directory name or, in the + * case of Microsoft Windows UNC pathnames, a hostname. Each subsequent name + * in an abstract pathname denotes a directory; the last name may denote + * either a directory or a file. The empty abstract pathname has no + * prefix and an empty name sequence. + * + *

The conversion of a pathname string to or from an abstract pathname is + * inherently system-dependent. When an abstract pathname is converted into a + * pathname string, each name is separated from the next by a single copy of + * the default separator character. The default name-separator + * character is defined by the system property file.separator, and + * is made available in the public static fields {@link + * #separator} and {@link #separatorChar} of this class. + * When a pathname string is converted into an abstract pathname, the names + * within it may be separated by the default name-separator character or by any + * other name-separator character that is supported by the underlying system. + * + *

A pathname, whether abstract or in string form, may be either + * absolute or relative. An absolute pathname is complete in + * that no other information is required in order to locate the file that it + * denotes. A relative pathname, in contrast, must be interpreted in terms of + * information taken from some other pathname. By default the classes in the + * java.io package always resolve relative pathnames against the + * current user directory. This directory is named by the system property + * user.dir, and is typically the directory in which the Java + * virtual machine was invoked. + * + *

The parent of an abstract pathname may be obtained by invoking + * the {@link #getParent} method of this class and consists of the pathname's + * prefix and each name in the pathname's name sequence except for the last. + * Each directory's absolute pathname is an ancestor of any AmazeFile + * object with an absolute abstract pathname which begins with the directory's + * absolute pathname. For example, the directory denoted by the abstract + * pathname "/usr" is an ancestor of the directory denoted by the + * pathname "/usr/local/bin". + * + *

The prefix concept is used to handle root directories on UNIX platforms, + * and drive specifiers, root directories and UNC pathnames on Microsoft Windows platforms, + * as follows: + * + *

    + * + *
  • For UNIX platforms, the prefix of an absolute pathname is always + * "/". Relative pathnames have no prefix. The abstract pathname + * denoting the root directory has the prefix "/" and an empty + * name sequence. + * + *
  • For Microsoft Windows platforms, the prefix of a pathname that contains a drive + * specifier consists of the drive letter followed by ":" and + * possibly followed by "\\" if the pathname is absolute. The + * prefix of a UNC pathname is "\\\\"; the hostname and the share + * name are the first two names in the name sequence. A relative pathname that + * does not specify a drive has no prefix. + * + *
+ * + *

Instances of this class may or may not denote an actual file-system + * object such as a file or a directory. If it does denote such an object + * then that object resides in a partition. A partition is an + * operating system-specific portion of storage for a file system. A single + * storage device (e.g. a physical disk-drive, flash memory, CD-ROM) may + * contain multiple partitions. The object, if any, will reside on the + * partition named by some ancestor of the absolute + * form of this pathname. + * + *

A file system may implement restrictions to certain operations on the + * actual file-system object, such as reading, writing, and executing. These + * restrictions are collectively known as access permissions. The file + * system may have multiple sets of access permissions on a single object. + * For example, one set may apply to the object's owner, and another + * may apply to all other users. The access permissions on an object may + * cause some methods in this class to fail. + * + *

On Android strings are converted to UTF-8 byte sequences when sending filenames to + * the operating system, and byte sequences returned by the operating system (from the + * various {@code list} methods) are converted to strings by decoding them as UTF-8 + * byte sequences. + * + * @author unascribed + * @since JDK1.0 + */ +public class AmazeFile implements Parcelable, Comparable { + + public static final String TAG = AmazeFile.class.getSimpleName(); + + /** + * The FileSystem object representing the platform's local file system. + */ + private AmazeFileSystem fs; + + /** + * This abstract pathname's normalized pathname string. A normalized + * pathname string uses the default name-separator character and does not + * contain any duplicate or redundant separators. + */ + private final String path; + + /** + * Enum type that indicates the status of a file path. + */ + private static enum PathStatus { INVALID, CHECKED }; + + /** + * The flag indicating whether the file path is invalid. + */ + private transient PathStatus status = null; + + /** + * Check if the file has an invalid path. Currently, the inspection of + * a file path is very limited, and it only covers Nul character check. + * Returning true means the path is definitely invalid/garbage. But + * returning false does not guarantee that the path is valid. + * + * @return true if the file path is invalid. + */ + final boolean isInvalid() { + if (status == null) { + status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED + : PathStatus.INVALID; + } + return status == PathStatus.INVALID; + } + + /** + * The length of this abstract pathname's prefix, or zero if it has no + * prefix. + */ + private final transient int prefixLength; + + /** + * Returns the length of this abstract pathname's prefix. + * For use by FileSystem classes. + */ + int getPrefixLength() { + return prefixLength; + } + + /** + * The system-dependent default name-separator character. This field is + * initialized to contain the first character of the value of the system + * property file.separator. On UNIX systems the value of this + * field is '/'; on Microsoft Windows systems it is '\\'. + * + * @see java.lang.System#getProperty(java.lang.String) + */ + public final char separatorChar; + + /** + * The system-dependent default name-separator character, represented as a + * string for convenience. This string contains a single character, namely + * {@link #separatorChar}. + */ + public final String separator; + + /** + * The system-dependent path-separator character. This field is + * initialized to contain the first character of the value of the system + * property path.separator. This character is used to + * separate filenames in a sequence of files given as a path list. + * On UNIX systems, this character is ':'; on Microsoft Windows systems it + * is ';'. + * + * @see java.lang.System#getProperty(java.lang.String) + */ + public final char pathSeparatorChar; + + /** + * The system-dependent path-separator character, represented as a string + * for convenience. This string contains a single character, namely + * {@link #pathSeparatorChar}. + */ + public final String pathSeparator; + + + /* -- Constructors -- */ + + /** + * Internal constructor for already-normalized pathname strings. + */ + private AmazeFile(String pathname, int prefixLength) { + loadFilesystem(pathname); + separatorChar = fs.getSeparator(); + separator = "" + separatorChar; + pathSeparatorChar = fs.getPathSeparator(); + pathSeparator = "" + pathSeparatorChar; + this.path = pathname; + this.prefixLength = prefixLength; + } + + /** + * Internal constructor for already-normalized pathname strings. + * The parameter order is used to disambiguate this method from the + * public(AmazeFile, String) constructor. + */ + private AmazeFile(String child, AmazeFile parent) { + assert parent.path != null; + assert (!parent.path.equals("")); + loadFilesystem(parent.path); + separatorChar = fs.getSeparator(); + separator = "" + separatorChar; + pathSeparatorChar = fs.getPathSeparator(); + pathSeparator = "" + pathSeparatorChar; + this.path = fs.resolve(parent.path, child); + this.prefixLength = parent.prefixLength; + } + + /** + * Creates a new AmazeFile instance by converting the given + * pathname string into an abstract pathname. If the given string is + * the empty string, then the result is the empty abstract pathname. + * + * @param pathname A pathname string + * @throws NullPointerException + * If the pathname argument is null + */ + public AmazeFile(String pathname) { + if (pathname == null) { + throw new NullPointerException(); + } + loadFilesystem(pathname); + separatorChar = fs.getSeparator(); + separator = "" + separatorChar; + pathSeparatorChar = fs.getPathSeparator(); + pathSeparator = "" + pathSeparatorChar; + this.path = fs.normalize(pathname); + this.prefixLength = fs.prefixLength(this.path); + } + + /* Note: The two-argument File constructors do not interpret an empty + parent abstract pathname as the current user directory. An empty parent + instead causes the child to be resolved against the system-dependent + directory defined by the FileSystem.getDefaultParent method. On Unix + this default is "/", while on Microsoft Windows it is "\\". This is required for + compatibility with the original behavior of this class. */ + + /** + * Creates a new AmazeFile instance from a parent pathname string + * and a child pathname string. + * + *

If parent is null then the new + * AmazeFile instance is created as if by invoking the + * single-argument AmazeFile constructor on the given + * child pathname string. + * + *

Otherwise the parent pathname string is taken to denote + * a directory, and the child pathname string is taken to + * denote either a directory or a file. If the child pathname + * string is absolute then it is converted into a relative pathname in a + * system-dependent way. If parent is the empty string then + * the new AmazeFile instance is created by converting + * child into an abstract pathname and resolving the result + * against a system-dependent default directory. Otherwise each pathname + * string is converted into an abstract pathname and the child abstract + * pathname is resolved against the parent. + * + * @param parent The parent pathname string + * @param child The child pathname string + * @throws NullPointerException + * If child is null + */ + public AmazeFile(String parent, String child) { + if (child == null) { + throw new NullPointerException(); + } + // BEGIN Android-changed: b/25859957, app-compat; don't substitute empty parent. + if (parent != null && !parent.isEmpty()) { + loadFilesystem(parent); + separatorChar = fs.getSeparator(); + separator = "" + separatorChar; + pathSeparatorChar = fs.getPathSeparator(); + pathSeparator = "" + pathSeparatorChar; + this.path = fs.resolve(fs.normalize(parent), + fs.normalize(child)); + // END Android-changed: b/25859957, app-compat; don't substitute empty parent. + } else { + loadFilesystem(child); + separatorChar = fs.getSeparator(); + separator = "" + separatorChar; + pathSeparatorChar = fs.getPathSeparator(); + pathSeparator = "" + pathSeparatorChar; + this.path = fs.normalize(child); + } + this.prefixLength = fs.prefixLength(this.path); + } + + /** + * Creates a new AmazeFile instance from a parent abstract + * pathname and a child pathname string. + * + *

If parent is null then the new + * AmazeFile instance is created as if by invoking the + * single-argument AmazeFile constructor on the given + * child pathname string. + * + *

Otherwise the parent abstract pathname is taken to + * denote a directory, and the child pathname string is taken + * to denote either a directory or a file. If the child + * pathname string is absolute then it is converted into a relative + * pathname in a system-dependent way. If parent is the empty + * abstract pathname then the new AmazeFile instance is created by + * converting child into an abstract pathname and resolving + * the result against a system-dependent default directory. Otherwise each + * pathname string is converted into an abstract pathname and the child + * abstract pathname is resolved against the parent. + * + * @param parent The parent abstract pathname + * @param child The child pathname string + * @throws NullPointerException + * If child is null + */ + public AmazeFile(AmazeFile parent, String child) { + if (child == null) { + throw new NullPointerException(); + } + if (parent != null) { + loadFilesystem(parent.getPath()); + separatorChar = fs.getSeparator(); + separator = "" + separatorChar; + pathSeparatorChar = fs.getPathSeparator(); + pathSeparator = "" + pathSeparatorChar; + + if (parent.path.equals("")) { + this.path = fs.resolve(fs.getDefaultParent(), + fs.normalize(child)); + } else { + this.path = fs.resolve(parent.path, + fs.normalize(child)); + } + } else { + loadFilesystem(child); + separatorChar = fs.getSeparator(); + separator = "" + separatorChar; + pathSeparatorChar = fs.getPathSeparator(); + pathSeparator = "" + pathSeparatorChar; + this.path = fs.normalize(child); + } + this.prefixLength = fs.prefixLength(this.path); + } + + /** + * Creates a new AmazeFile instance by converting the given + * file: URI into an abstract pathname. + * + *

The exact form of a file: URI is system-dependent, hence + * the transformation performed by this constructor is also + * system-dependent. + * + *

For a given abstract pathname f it is guaranteed that + * + *

+ * new AmazeFile( f.{@link #toURI() toURI}()).equals( f.{@link #getAbsoluteFile() getAbsoluteFile}()) + *
+ * + * so long as the original abstract pathname, the URI, and the new abstract + * pathname are all created in (possibly different invocations of) the same + * Java virtual machine. This relationship typically does not hold, + * however, when a file: URI that is created in a virtual machine + * on one operating system is converted into an abstract pathname in a + * virtual machine on a different operating system. + * + * @param uri + * An absolute, hierarchical URI with a scheme equal to + * "file", a non-empty path component, and undefined + * authority, query, and fragment components + * + * @throws NullPointerException + * If uri is null + * + * @throws IllegalArgumentException + * If the preconditions on the parameter do not hold + * + * @see #toURI() + * @see java.net.URI + * @since 1.4 + */ + public AmazeFile(URI uri) { + + // Check our many preconditions + if (!uri.isAbsolute()) + throw new IllegalArgumentException("URI is not absolute"); + if (uri.isOpaque()) + throw new IllegalArgumentException("URI is not hierarchical"); + String scheme = uri.getScheme(); + if ((scheme == null) || !scheme.equalsIgnoreCase("file")) + throw new IllegalArgumentException("URI scheme is not \"file\""); + if (uri.getAuthority() != null) + throw new IllegalArgumentException("URI has an authority component"); + if (uri.getFragment() != null) + throw new IllegalArgumentException("URI has a fragment component"); + if (uri.getQuery() != null) + throw new IllegalArgumentException("URI has a query component"); + String p = uri.getPath(); + if (p.equals("")) + throw new IllegalArgumentException("URI path component is empty"); + + loadFilesystem(uri.toString()); + separatorChar = fs.getSeparator(); + separator = "" + separatorChar; + pathSeparatorChar = fs.getPathSeparator(); + pathSeparator = "" + pathSeparatorChar; + + // Okay, now initialize + p = fs.fromURIPath(p); + if (File.separatorChar != '/') { + p = p.replace('/', File.separatorChar); + } + this.path = fs.normalize(p); + this.prefixLength = fs.prefixLength(this.path); + } + + private void loadFilesystem(String path) { + if(SmbAmazeFileSystem.INSTANCE.isPathOfThisFilesystem(path)) { + fs = SmbAmazeFileSystem.INSTANCE; + } + } + + + /* -- Path-component accessors -- */ + + /** + * Returns the name of the file or directory denoted by this abstract + * pathname. This is just the last name in the pathname's name + * sequence. If the pathname's name sequence is empty, then the empty + * string is returned. + * + * @return The name of the file or directory denoted by this abstract + * pathname, or the empty string if this pathname's name sequence + * is empty + */ + public String getName() { + int index = path.lastIndexOf(separatorChar); + if (index < prefixLength) return path.substring(prefixLength); + return path.substring(index + 1); + } + + /** + * Returns the pathname string of this abstract pathname's parent, or + * null if this pathname does not name a parent directory. + * + *

The parent of an abstract pathname consists of the + * pathname's prefix, if any, and each name in the pathname's name + * sequence except for the last. If the name sequence is empty then + * the pathname does not name a parent directory. + * + * @return The pathname string of the parent directory named by this + * abstract pathname, or null if this pathname + * does not name a parent + */ + public String getParent() { + int index = path.lastIndexOf(separatorChar); + if (index < prefixLength) { + if ((prefixLength > 0) && (path.length() > prefixLength)) + return path.substring(0, prefixLength); + return null; + } + return path.substring(0, index); + } + + /** + * Returns the abstract pathname of this abstract pathname's parent, + * or null if this pathname does not name a parent + * directory. + * + *

The parent of an abstract pathname consists of the + * pathname's prefix, if any, and each name in the pathname's name + * sequence except for the last. If the name sequence is empty then + * the pathname does not name a parent directory. + * + * @return The abstract pathname of the parent directory named by this + * abstract pathname, or null if this pathname + * does not name a parent + * + * @since 1.2 + */ + public AmazeFile getParentFile() { + String p = this.getParent(); + if (p == null) return null; + return new AmazeFile(p, this.prefixLength); + } + + /** + * Converts this abstract pathname into a pathname string. The resulting + * string uses the {@link #separator default name-separator character} to + * separate the names in the name sequence. + * + * @return The string form of this abstract pathname + */ + public String getPath() { + return path; + } + + + /* -- Path operations -- */ + + // Android-changed: Android-specific path information + /** + * Tests whether this abstract pathname is absolute. The definition of + * absolute pathname is system dependent. On Android, absolute paths start with + * the character '/'. + * + * @return true if this abstract pathname is absolute, + * false otherwise + */ + public boolean isAbsolute() { + return fs.isAbsolute(this); + } + + // Android-changed: Android-specific path information + /** + * Returns the absolute path of this file. An absolute path is a path that starts at a root + * of the file system. On Android, there is only one root: {@code /}. + * + *

A common use for absolute paths is when passing paths to a {@code Process} as + * command-line arguments, to remove the requirement implied by relative paths, that the + * child must have the same working directory as its parent. + * + * @return The absolute pathname string denoting the same file or + * directory as this abstract pathname + * + * @see java.io.File#isAbsolute() + */ + public String getAbsolutePath() { + return fs.resolve(this); + } + + /** + * Returns the absolute form of this abstract pathname. Equivalent to + * new File(this.{@link #getAbsolutePath}). + * + * @return The absolute abstract pathname denoting the same file or + * directory as this abstract pathname + * + * + * @since 1.2 + */ + public AmazeFile getAbsoluteFile() { + String absPath = getAbsolutePath(); + return new AmazeFile(absPath, fs.prefixLength(absPath)); + } + + /** + * Returns the canonical pathname string of this abstract pathname. + * + *

A canonical pathname is both absolute and unique. The precise + * definition of canonical form is system-dependent. This method first + * converts this pathname to absolute form if necessary, as if by invoking the + * {@link #getAbsolutePath} method, and then maps it to its unique form in a + * system-dependent way. This typically involves removing redundant names + * such as "." and ".." from the pathname, resolving + * symbolic links (on UNIX platforms), and converting drive letters to a + * standard case (on Microsoft Windows platforms). + * + *

Every pathname that denotes an existing file or directory has a + * unique canonical form. Every pathname that denotes a nonexistent file + * or directory also has a unique canonical form. The canonical form of + * the pathname of a nonexistent file or directory may be different from + * the canonical form of the same pathname after the file or directory is + * created. Similarly, the canonical form of the pathname of an existing + * file or directory may be different from the canonical form of the same + * pathname after the file or directory is deleted. + * + * @return The canonical pathname string denoting the same file or + * directory as this abstract pathname + * + * @throws IOException + * If an I/O error occurs, which is possible because the + * construction of the canonical pathname may require + * filesystem queries + * + * @since JDK1.1 + * @see Path#toRealPath + */ + public String getCanonicalPath() throws IOException { + if (isInvalid()) { + throw new IOException("Invalid file path"); + } + return fs.canonicalize(fs.resolve(this)); + } + + /** + * Returns the canonical form of this abstract pathname. Equivalent to + * new File(this.{@link #getCanonicalPath}). + * + * @return The canonical pathname string denoting the same file or + * directory as this abstract pathname + * + * @throws IOException + * If an I/O error occurs, which is possible because the + * construction of the canonical pathname may require + * filesystem queries + * + * + * @since 1.2 + * @see Path#toRealPath + */ + public AmazeFile getCanonicalFile() throws IOException { + String canonPath = getCanonicalPath(); + return new AmazeFile(canonPath, fs.prefixLength(canonPath)); + } + + private static String slashify(String path, boolean isDirectory) { + String p = path; + if (File.separatorChar != '/') + p = p.replace(File.separatorChar, '/'); + if (!p.startsWith("/")) + p = "/" + p; + if (!p.endsWith("/") && isDirectory) + p = p + "/"; + return p; + } + + /** + * Constructs a file: URI that represents this abstract pathname. + * + *

The exact form of the URI is system-dependent. If it can be + * determined that the file denoted by this abstract pathname is a + * directory, then the resulting URI will end with a slash. + * + *

For a given abstract pathname f, it is guaranteed that + * + *

+ * new {@link #AmazeFile(java.net.URI) File}( f.toURI()).equals( f.{@link #getAbsoluteFile() getAbsoluteFile}()) + *
+ * + * so long as the original abstract pathname, the URI, and the new abstract + * pathname are all created in (possibly different invocations of) the same + * Java virtual machine. Due to the system-dependent nature of abstract + * pathnames, however, this relationship typically does not hold when a + * file: URI that is created in a virtual machine on one operating + * system is converted into an abstract pathname in a virtual machine on a + * different operating system. + * + *

Note that when this abstract pathname represents a UNC pathname then + * all components of the UNC (including the server name component) are encoded + * in the {@code URI} path. The authority component is undefined, meaning + * that it is represented as {@code null}. The {@link Path} class defines the + * {@link Path#toUri toUri} method to encode the server name in the authority + * component of the resulting {@code URI}. The {@link #toPath toPath} method + * may be used to obtain a {@code Path} representing this abstract pathname. + * + * @return An absolute, hierarchical URI with a scheme equal to + * "file", a path representing this abstract pathname, + * and undefined authority, query, and fragment components + * + * @see #AmazeFile(java.net.URI) + * @see java.net.URI + * @see java.net.URI#toURL() + * @since 1.4 + */ + @Deprecated(message = "Left for reference, do not use") + public URI toURI() { + try { + AmazeFile f = getAbsoluteFile(); + String sp = slashify(f.getPath(), f.isDirectory()); + if (sp.startsWith("//")) + sp = "//" + sp; + return new URI("file", null, sp, null); + } catch (URISyntaxException x) { + throw new Error(x); // Can't happen + } + } + + + /* -- Attribute accessors -- */ + + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on android + /** + * Tests whether the application can read the file denoted by this + * abstract pathname. + * + * @return true if and only if the file specified by this + * abstract pathname exists and can be read by the + * application; false otherwise + */ + public boolean canRead() { + if (isInvalid()) { + return false; + } + return fs.checkAccess(this, AmazeFileSystem.ACCESS_READ); + } + + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on android + /** + * Tests whether the application can modify the file denoted by this + * abstract pathname. + * + * @return true if and only if the file system actually + * contains a file denoted by this abstract pathname and + * the application is allowed to write to the file; + * false otherwise. + * + */ + public boolean canWrite() { + if (isInvalid()) { + return false; + } + return fs.checkAccess(this, AmazeFileSystem.ACCESS_WRITE); + } + + /** + * Tests whether the file or directory denoted by this abstract pathname + * exists. + * + * @return true if and only if the file or directory denoted + * by this abstract pathname exists; false otherwise + * + */ + public boolean exists() { + if (isInvalid()) { + return false; + } + + // Android-changed: b/25878034 work around SELinux stat64 denial. + return fs.checkAccess(this, AmazeFileSystem.ACCESS_CHECK_EXISTS); + } + + /** + * Tests whether the file denoted by this abstract pathname is a + * directory. + * + *

Where it is required to distinguish an I/O exception from the case + * that the file is not a directory, or where several attributes of the + * same file are required at the same time, then the {@link + * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) + * Files.readAttributes} method may be used. + * + * @return true if and only if the file denoted by this + * abstract pathname exists and is a directory; + * false otherwise + * + */ + public boolean isDirectory() { + if (isInvalid()) { + return false; + } + return ((fs.getBooleanAttributes(this) & AmazeFileSystem.BA_DIRECTORY) + != 0); + } + + /** + * Tests whether the file denoted by this abstract pathname is a normal + * file. A file is normal if it is not a directory and, in + * addition, satisfies other system-dependent criteria. Any non-directory + * file created by a Java application is guaranteed to be a normal file. + * + *

Where it is required to distinguish an I/O exception from the case + * that the file is not a normal file, or where several attributes of the + * same file are required at the same time, then the {@link + * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) + * Files.readAttributes} method may be used. + * + * @return true if and only if the file denoted by this + * abstract pathname exists and is a normal file; + * false otherwise + * + */ + public boolean isFile() { + if (isInvalid()) { + return false; + } + return ((fs.getBooleanAttributes(this) & AmazeFileSystem.BA_REGULAR) != 0); + } + + /** + * Tests whether the file named by this abstract pathname is a hidden + * file. The exact definition of hidden is system-dependent. On + * UNIX systems, a file is considered to be hidden if its name begins with + * a period character ('.'). On Microsoft Windows systems, a file is + * considered to be hidden if it has been marked as such in the filesystem. + * + * @return true if and only if the file denoted by this + * abstract pathname is hidden according to the conventions of the + * underlying platform + * + * @since 1.2 + */ + public boolean isHidden() { + if (isInvalid()) { + return false; + } + return ((fs.getBooleanAttributes(this) & AmazeFileSystem.BA_HIDDEN) != 0); + } + + /** + * Returns the time that the file denoted by this abstract pathname was + * last modified. + * + *

Where it is required to distinguish an I/O exception from the case + * where {@code 0L} is returned, or where several attributes of the + * same file are required at the same time, or where the time of last + * access or the creation time are required, then the {@link + * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) + * Files.readAttributes} method may be used. + * + * @return A long value representing the time the file was + * last modified, measured in milliseconds since the epoch + * (00:00:00 GMT, January 1, 1970), or 0L if the + * file does not exist or if an I/O error occurs + * + */ + public long lastModified() { + if (isInvalid()) { + return 0L; + } + return fs.getLastModifiedTime(this); + } + + /** + * Returns the length of the file denoted by this abstract pathname. + * The return value is unspecified if this pathname denotes a directory. + * + *

Where it is required to distinguish an I/O exception from the case + * that {@code 0L} is returned, or where several attributes of the same file + * are required at the same time, then the {@link + * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) + * Files.readAttributes} method may be used. + * + * @return The length, in bytes, of the file denoted by this abstract + * pathname, or 0L if the file does not exist. Some + * operating systems may return 0L for pathnames + * denoting system-dependent entities such as devices or pipes. + */ + public long length() throws IOException { + if (isInvalid()) { + return 0L; + } + return fs.getLength(this); + } + + + /* -- File operations -- */ + + /** + * Atomically creates a new, empty file named by this abstract pathname if + * and only if a file with this name does not yet exist. The check for the + * existence of the file and the creation of the file if it does not exist + * are a single operation that is atomic with respect to all other + * filesystem activities that might affect the file. + *

+ * Note: this method should not be used for file-locking, as + * the resulting protocol cannot be made to work reliably. The + * {@link java.nio.channels.FileLock FileLock} + * facility should be used instead. + * + * @return true if the named file does not exist and was + * successfully created; false if the named file + * already exists + * + * @throws IOException + * If an I/O error occurred + * + * @since 1.2 + */ + public boolean createNewFile() throws IOException { + if (isInvalid()) { + throw new IOException("Invalid file path"); + } + return fs.createFileExclusively(path); + } + + /** + * Deletes the file or directory denoted by this abstract pathname. If + * this pathname denotes a directory, then the directory must be empty in + * order to be deleted. + * + *

Note that the {@link java.nio.file.Files} class defines the {@link + * java.nio.file.Files#delete(Path) delete} method to throw an {@link IOException} + * when a file cannot be deleted. This is useful for error reporting and to + * diagnose why a file cannot be deleted. + * + * @return true if and only if the file or directory is + * successfully deleted; false otherwise + */ + public boolean delete() { + if (isInvalid()) { + return false; + } + return fs.delete(this); + } + + // Android-added: Additional information about Android behaviour. + /** + * Requests that the file or directory denoted by this abstract + * pathname be deleted when the virtual machine terminates. + * Files (or directories) are deleted in the reverse order that + * they are registered. Invoking this method to delete a file or + * directory that is already registered for deletion has no effect. + * Deletion will be attempted only for normal termination of the + * virtual machine, as defined by the Java Language Specification. + * + *

Once deletion has been requested, it is not possible to cancel the + * request. This method should therefore be used with care. + * + *

+ * Note: this method should not be used for file-locking, as + * the resulting protocol cannot be made to work reliably. The + * {@link java.nio.channels.FileLock FileLock} + * facility should be used instead. + * + *

Note that on Android, the application lifecycle does not include VM termination, + * so calling this method will not ensure that files are deleted. Instead, you should + * use the most appropriate out of: + *

    + *
  • Use a {@code finally} clause to manually invoke {@link #delete}. + *
  • Maintain your own set of files to delete, and process it at an appropriate point + * in your application's lifecycle. + *
  • Use the Unix trick of deleting the file as soon as all readers and writers have + * opened it. No new readers/writers will be able to access the file, but all existing + * ones will still have access until the last one closes the file. + *
+ * + * + * @see #delete + * + * @since 1.2 + */ + public void deleteOnExit() { + if (isInvalid()) { + return; + } + AmazeDeleteOnExitHook.add(path); + } + + /** + * Returns an array of strings naming the files and directories in the + * directory denoted by this abstract pathname. + * + *

If this abstract pathname does not denote a directory, then this + * method returns {@code null}. Otherwise an array of strings is + * returned, one for each file or directory in the directory. Names + * denoting the directory itself and the directory's parent directory are + * not included in the result. Each string is a file name rather than a + * complete path. + * + *

There is no guarantee that the name strings in the resulting array + * will appear in any specific order; they are not, in particular, + * guaranteed to appear in alphabetical order. + * + *

Note that the {@link java.nio.file.Files} class defines the {@link + * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream} method to + * open a directory and iterate over the names of the files in the directory. + * This may use less resources when working with very large directories, and + * may be more responsive when working with remote directories. + * + * @return An array of strings naming the files and directories in the + * directory denoted by this abstract pathname. The array will be + * empty if the directory is empty. Returns {@code null} if + * this abstract pathname does not denote a directory, or if an + * I/O error occurs. + */ + public String[] list() { + if (isInvalid()) { + return null; + } + return fs.list(this); + } + + /** + * Returns an array of strings naming the files and directories in the + * directory denoted by this abstract pathname that satisfy the specified + * filter. The behavior of this method is the same as that of the + * {@link #list()} method, except that the strings in the returned array + * must satisfy the filter. If the given {@code filter} is {@code null} + * then all names are accepted. Otherwise, a name satisfies the filter if + * and only if the value {@code true} results when the {@link + * AmazeFilenameFilter#accept AmazeFilenameFilter.accept(File, String)} method + * of the filter is invoked on this abstract pathname and the name of a + * file or directory in the directory that it denotes. + * + * @param filter + * A filename filter + * + * @return An array of strings naming the files and directories in the + * directory denoted by this abstract pathname that were accepted + * by the given {@code filter}. The array will be empty if the + * directory is empty or if no names were accepted by the filter. + * Returns {@code null} if this abstract pathname does not denote + * a directory, or if an I/O error occurs. + * + * @see java.nio.file.Files#newDirectoryStream(Path,String) + */ + public String[] list(AmazeFilenameFilter filter) { + String names[] = list(); + if ((names == null) || (filter == null)) { + return names; + } + List v = new ArrayList<>(); + for (int i = 0 ; i < names.length ; i++) { + if (filter.accept(this, names[i])) { + v.add(names[i]); + } + } + return v.toArray(new String[v.size()]); + } + + /** + * Returns an array of abstract pathnames denoting the files in the + * directory denoted by this abstract pathname. + * + *

If this abstract pathname does not denote a directory, then this + * method returns {@code null}. Otherwise an array of {@code File} objects + * is returned, one for each file or directory in the directory. Pathnames + * denoting the directory itself and the directory's parent directory are + * not included in the result. Each resulting abstract pathname is + * constructed from this abstract pathname using the {@link #File(File, + * String) File(File, String)} constructor. Therefore if this + * pathname is absolute then each resulting pathname is absolute; if this + * pathname is relative then each resulting pathname will be relative to + * the same directory. + * + *

There is no guarantee that the name strings in the resulting array + * will appear in any specific order; they are not, in particular, + * guaranteed to appear in alphabetical order. + * + *

Note that the {@link java.nio.file.Files} class defines the {@link + * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream} method + * to open a directory and iterate over the names of the files in the + * directory. This may use less resources when working with very large + * directories. + * + * @return An array of abstract pathnames denoting the files and + * directories in the directory denoted by this abstract pathname. + * The array will be empty if the directory is empty. Returns + * {@code null} if this abstract pathname does not denote a + * directory, or if an I/O error occurs. + * + * @since 1.2 + */ + public AmazeFile[] listFiles() { + String[] ss = list(); + if (ss == null) return null; + int n = ss.length; + AmazeFile[] fs = new AmazeFile[n]; + for (int i = 0; i < n; i++) { + fs[i] = new AmazeFile(ss[i], this); + } + return fs; + } + + /** + * Returns an array of abstract pathnames denoting the files and + * directories in the directory denoted by this abstract pathname that + * satisfy the specified filter. The behavior of this method is the same + * as that of the {@link #listFiles()} method, except that the pathnames in + * the returned array must satisfy the filter. If the given {@code filter} + * is {@code null} then all pathnames are accepted. Otherwise, a pathname + * satisfies the filter if and only if the value {@code true} results when + * the {@link AmazeFilenameFilter#accept + * AmazeFilenameFilter.accept(File, String)} method of the filter is + * invoked on this abstract pathname and the name of a file or directory in + * the directory that it denotes. + * + * @param filter + * A filename filter + * + * @return An array of abstract pathnames denoting the files and + * directories in the directory denoted by this abstract pathname. + * The array will be empty if the directory is empty. Returns + * {@code null} if this abstract pathname does not denote a + * directory, or if an I/O error occurs. + * + * @since 1.2 + * @see java.nio.file.Files#newDirectoryStream(Path,String) + */ + public AmazeFile[] listFiles(AmazeFilenameFilter filter) { + String ss[] = list(); + if (ss == null) return null; + ArrayList files = new ArrayList<>(); + for (String s : ss) + if ((filter == null) || filter.accept(this, s)) + files.add(new AmazeFile(s, this)); + return files.toArray(new AmazeFile[files.size()]); + } + + /** + * Returns an array of abstract pathnames denoting the files and + * directories in the directory denoted by this abstract pathname that + * satisfy the specified filter. The behavior of this method is the same + * as that of the {@link #listFiles()} method, except that the pathnames in + * the returned array must satisfy the filter. If the given {@code filter} + * is {@code null} then all pathnames are accepted. Otherwise, a pathname + * satisfies the filter if and only if the value {@code true} results when + * the {@link FileFilter#accept FileFilter.accept(File)} method of the + * filter is invoked on the pathname. + * + * @param filter + * A file filter + * + * @return An array of abstract pathnames denoting the files and + * directories in the directory denoted by this abstract pathname. + * The array will be empty if the directory is empty. Returns + * {@code null} if this abstract pathname does not denote a + * directory, or if an I/O error occurs. + * + * @since 1.2 + * @see java.nio.file.Files#newDirectoryStream(Path,java.nio.file.DirectoryStream.Filter) + */ + public AmazeFile[] listFiles(AmazeFileFilter filter) { + String ss[] = list(); + if (ss == null) return null; + ArrayList files = new ArrayList<>(); + for (String s : ss) { + AmazeFile f = new AmazeFile(s, this); + if ((filter == null) || filter.accept(f)) + files.add(f); + } + return files.toArray(new AmazeFile[files.size()]); + } + + public InputStream getInputStream() { + return fs.getInputStream(this); + } + + public OutputStream getOutputStream() { + return fs.getOutputStream(this); + } + + /** + * Creates the directory named by this abstract pathname. + * + * @return true if and only if the directory was + * created; false otherwise + * + */ + public boolean mkdir() { + if (isInvalid()) { + return false; + } + return fs.createDirectory(this); + } + + /** + * Creates the directory named by this abstract pathname, including any + * necessary but nonexistent parent directories. Note that if this + * operation fails it may have succeeded in creating some of the necessary + * parent directories. + * + * @return true if and only if the directory was created, + * along with all necessary parent directories; false + * otherwise + * + */ + public boolean mkdirs() { + if (exists()) { + return false; + } + if (mkdir()) { + return true; + } + AmazeFile canonFile = null; + try { + canonFile = getCanonicalFile(); + } catch (IOException e) { + return false; + } + + AmazeFile parent = canonFile.getParentFile(); + return (parent != null && (parent.mkdirs() || parent.exists()) && + canonFile.mkdir()); + } + + // Android-changed: Replaced generic platform info with Android specific one. + /** + * Renames the file denoted by this abstract pathname. + * + *

Many failures are possible. Some of the more likely failures include: + *

    + *
  • Write permission is required on the directories containing both the source and + * destination paths. + *
  • Search permission is required for all parents of both paths. + *
  • Both paths be on the same mount point. On Android, applications are most likely to hit + * this restriction when attempting to copy between internal storage and an SD card. + *
+ * + *

The return value should always be checked to make sure + * that the rename operation was successful. + * + *

Note that the {@link java.nio.file.Files} class defines the {@link + * java.nio.file.Files#move move} method to move or rename a file in a + * platform independent manner. + * + * @param dest The new abstract pathname for the named file + * + * @return true if and only if the renaming succeeded; + * false otherwise + * + * + * @throws NullPointerException + * If parameter dest is null + */ + public boolean renameTo(AmazeFile dest) { + if (dest == null) { + throw new NullPointerException(); + } + if (this.isInvalid() || dest.isInvalid()) { + return false; + } + return fs.rename(this, dest); + } + + /** + * Sets the last-modified time of the file or directory named by this + * abstract pathname. + * + *

All platforms support file-modification times to the nearest second, + * but some provide more precision. The argument will be truncated to fit + * the supported precision. If the operation succeeds and no intervening + * operations on the file take place, then the next invocation of the + * {@link #lastModified} method will return the (possibly + * truncated) time argument that was passed to this method. + * + * @param time The new last-modified time, measured in milliseconds since + * the epoch (00:00:00 GMT, January 1, 1970) + * + * @return true if and only if the operation succeeded; + * false otherwise + * + * @throws IllegalArgumentException If the argument is negative + * + * + * @since 1.2 + */ + public boolean setLastModified(long time) { + if (time < 0) throw new IllegalArgumentException("Negative time"); + if (isInvalid()) { + return false; + } + return fs.setLastModifiedTime(this, time); + } + + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * Marks the file or directory named by this abstract pathname so that + * only read operations are allowed. After invoking this method the file + * or directory will not change until it is either deleted or marked + * to allow write access. Whether or not a read-only file or + * directory may be deleted depends upon the underlying system. + * + * @return true if and only if the operation succeeded; + * false otherwise + * + * + * @since 1.2 + */ + public boolean setReadOnly() { + if (isInvalid()) { + return false; + } + return fs.setReadOnly(this); + } + + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * Sets the owner's or everybody's write permission for this abstract + * pathname. + * + *

The {@link java.nio.file.Files} class defines methods that operate on + * file attributes including file permissions. This may be used when finer + * manipulation of file permissions is required. + * + * @param writable + * If true, sets the access permission to allow write + * operations; if false to disallow write operations + * + * @param ownerOnly + * If true, the write permission applies only to the + * owner's write permission; otherwise, it applies to everybody. If + * the underlying file system can not distinguish the owner's write + * permission from that of others, then the permission will apply to + * everybody, regardless of this value. + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to change + * the access permissions of this abstract pathname. + * + * @since 1.6 + */ + public boolean setWritable(boolean writable, boolean ownerOnly) { + if (isInvalid()) { + return false; + } + return fs.setPermission(this, AmazeFileSystem.ACCESS_WRITE, writable, ownerOnly); + } + + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * A convenience method to set the owner's write permission for this abstract + * pathname. + * + *

An invocation of this method of the form file.setWritable(arg) + * behaves in exactly the same way as the invocation + * + *

+   *     file.setWritable(arg, true) 
+ * + * @param writable + * If true, sets the access permission to allow write + * operations; if false to disallow write operations + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to + * change the access permissions of this abstract pathname. + * + * @since 1.6 + */ + public boolean setWritable(boolean writable) { + return setWritable(writable, true); + } + + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * Sets the owner's or everybody's read permission for this abstract + * pathname. + * + *

The {@link java.nio.file.Files} class defines methods that operate on + * file attributes including file permissions. This may be used when finer + * manipulation of file permissions is required. + * + * @param readable + * If true, sets the access permission to allow read + * operations; if false to disallow read operations + * + * @param ownerOnly + * If true, the read permission applies only to the + * owner's read permission; otherwise, it applies to everybody. If + * the underlying file system can not distinguish the owner's read + * permission from that of others, then the permission will apply to + * everybody, regardless of this value. + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to + * change the access permissions of this abstract pathname. If + * readable is false and the underlying + * file system does not implement a read permission, then the + * operation will fail. + * + * @since 1.6 + */ + public boolean setReadable(boolean readable, boolean ownerOnly) { + if (isInvalid()) { + return false; + } + return fs.setPermission(this, AmazeFileSystem.ACCESS_READ, readable, ownerOnly); + } + + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * A convenience method to set the owner's read permission for this abstract + * pathname. + * + *

An invocation of this method of the form file.setReadable(arg) + * behaves in exactly the same way as the invocation + * + *

+   *     file.setReadable(arg, true) 
+ * + * @param readable + * If true, sets the access permission to allow read + * operations; if false to disallow read operations + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to + * change the access permissions of this abstract pathname. If + * readable is false and the underlying + * file system does not implement a read permission, then the + * operation will fail. + * + * @since 1.6 + */ + public boolean setReadable(boolean readable) { + return setReadable(readable, true); + } + + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * Sets the owner's or everybody's execute permission for this abstract + * pathname. + * + *

The {@link java.nio.file.Files} class defines methods that operate on + * file attributes including file permissions. This may be used when finer + * manipulation of file permissions is required. + * + * @param executable + * If true, sets the access permission to allow execute + * operations; if false to disallow execute operations + * + * @param ownerOnly + * If true, the execute permission applies only to the + * owner's execute permission; otherwise, it applies to everybody. + * If the underlying file system can not distinguish the owner's + * execute permission from that of others, then the permission will + * apply to everybody, regardless of this value. + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to + * change the access permissions of this abstract pathname. If + * executable is false and the underlying + * file system does not implement an execute permission, then the + * operation will fail. + * + * @since 1.6 + */ + public boolean setExecutable(boolean executable, boolean ownerOnly) { + if (isInvalid()) { + return false; + } + return fs.setPermission(this, AmazeFileSystem.ACCESS_EXECUTE, executable, ownerOnly); + } + + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * A convenience method to set the owner's execute permission for this + * abstract pathname. + * + *

An invocation of this method of the form file.setExcutable(arg) + * behaves in exactly the same way as the invocation + * + *

+   *     file.setExecutable(arg, true) 
+ * + * @param executable + * If true, sets the access permission to allow execute + * operations; if false to disallow execute operations + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to + * change the access permissions of this abstract pathname. If + * executable is false and the underlying + * file system does not implement an execute permission, then the + * operation will fail. + * + * @since 1.6 + */ + public boolean setExecutable(boolean executable) { + return setExecutable(executable, true); + } + + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * Tests whether the application can execute the file denoted by this + * abstract pathname. + * + * @return true if and only if the abstract pathname exists + * and the application is allowed to execute the file + * + * @since 1.6 + */ + public boolean canExecute() { + if (isInvalid()) { + return false; + } + return fs.checkAccess(this, AmazeFileSystem.ACCESS_EXECUTE); + } + + + /* -- Filesystem interface -- */ + + // Android-changed: Replaced generic platform info with Android specific one. + /** + * Returns the file system roots. On Android and other Unix systems, there is + * a single root, {@code /}. + */ + public AmazeFile[] listRoots() { + return fs.listRoots(); + } + + + /* -- Disk usage -- */ + + /** + * Returns the size of the partition named by this + * abstract pathname. + * + * @return The size, in bytes, of the partition or 0L if this + * abstract pathname does not name a partition + * If there is no way to determine, total space is -1 + * + * @since 1.6 + */ + public long getTotalSpace() { + if (isInvalid()) { + return 0L; + } + try { + return fs.getSpace(this, AmazeFileSystem.SPACE_TOTAL); + } catch(NotImplementedError e) { + Log.w(TAG, "Call to unimplemented fuction", e); + return -1; + } + } + + /** + * Returns the number of unallocated bytes in the partition named by this abstract path name. + * + *

The returned number of unallocated bytes is a hint, but not + * a guarantee, that it is possible to use most or any of these + * bytes. The number of unallocated bytes is most likely to be + * accurate immediately after this call. It is likely to be made + * inaccurate by any external I/O operations including those made + * on the system outside of this virtual machine. This method + * makes no guarantee that write operations to this file system + * will succeed. + * + * @return The number of unallocated bytes on the partition or 0L + * if the abstract pathname does not name a partition. This + * value will be less than or equal to the total file system size + * returned by {@link #getTotalSpace}. + * + * @since 1.6 + */ + public long getFreeSpace() { + if (isInvalid()) { + return 0L; + } + return fs.getSpace(this, AmazeFileSystem.SPACE_FREE); + } + + // Android-added: Replaced generic platform info with Android specific one. + /** + * Returns the number of bytes available to this virtual machine on the + * partition named by this abstract pathname. When + * possible, this method checks for write permissions and other operating + * system restrictions and will therefore usually provide a more accurate + * estimate of how much new data can actually be written than {@link + * #getFreeSpace}. + * + *

The returned number of available bytes is a hint, but not a + * guarantee, that it is possible to use most or any of these bytes. The + * number of unallocated bytes is most likely to be accurate immediately + * after this call. It is likely to be made inaccurate by any external + * I/O operations including those made on the system outside of this + * virtual machine. This method makes no guarantee that write operations + * to this file system will succeed. + * + *

On Android (and other Unix-based systems), this method returns the number of free bytes + * available to non-root users, regardless of whether you're actually running as root, + * and regardless of any quota or other restrictions that might apply to the user. + * (The {@code getFreeSpace} method returns the number of bytes potentially available to root.) + * + * @return The number of available bytes on the partition or 0L + * if the abstract pathname does not name a partition. On + * systems where this information is not available, this method + * will be equivalent to a call to {@link #getFreeSpace}. + * If there is no way to determine the current space left -1 is returned. + * + * @since 1.6 + */ + public long getUsableSpace() { + if (isInvalid()) { + return 0L; + } + try { + return fs.getSpace(this, AmazeFileSystem.SPACE_USABLE); + } catch (NotImplementedError e) { + Log.w(TAG, "Call to unimplemented fuction", e); + return -1; + } + } + + /* -- Temporary files -- */ + + private static class TempDirectory { + private TempDirectory() { } + + // Android-changed: Don't cache java.io.tmpdir value + // temporary directory location. + /* + private static final File tmpdir = new AmazeFile(AccessController + .doPrivileged(new GetPropertyAction("java.io.tmpdir"))); + static File location() { + return tmpdir; + } + */ + + // file name generation + // private static final SecureRandom random = new SecureRandom(); + static AmazeFile generateFile(String prefix, String suffix, AmazeFile dir, Random random) + throws IOException + { + // Android-changed: Use Math.randomIntInternal. This (pseudo) random number + // is initialized post-fork + + long n = random.nextLong(); + if (n == Long.MIN_VALUE) { + n = 0; // corner case + } else { + n = Math.abs(n); + } + + // Android-changed: Reject invalid file prefixes + // Use only the file name from the supplied prefix + // prefix = (new AmazeFile(prefix)).getName(); + + String name = prefix + Long.toString(n) + suffix; + AmazeFile f = new AmazeFile(dir, name); + if (!name.equals(f.getName()) || f.isInvalid()) { + if (System.getSecurityManager() != null) + throw new IOException("Unable to create temporary file"); + else + throw new IOException("Unable to create temporary file, " + f); + } + return f; + } + } + + /** + *

Creates a new empty file in the specified directory, using the + * given prefix and suffix strings to generate its name. If this method + * returns successfully then it is guaranteed that: + * + *

    + *
  1. The file denoted by the returned abstract pathname did not exist + * before this method was invoked, and + *
  2. Neither this method nor any of its variants will return the same + * abstract pathname again in the current invocation of the virtual + * machine. + *
+ * + * This method provides only part of a temporary-file facility. To arrange + * for a file created by this method to be deleted automatically, use the + * {@link #deleteOnExit} method. + * + *

The prefix argument must be at least three characters + * long. It is recommended that the prefix be a short, meaningful string + * such as "hjb" or "mail". The + * suffix argument may be null, in which case the + * suffix ".tmp" will be used. + * + *

To create the new file, the prefix and the suffix may first be + * adjusted to fit the limitations of the underlying platform. If the + * prefix is too long then it will be truncated, but its first three + * characters will always be preserved. If the suffix is too long then it + * too will be truncated, but if it begins with a period character + * ('.') then the period and the first three characters + * following it will always be preserved. Once these adjustments have been + * made the name of the new file will be generated by concatenating the + * prefix, five or more internally-generated characters, and the suffix. + * + *

If the directory argument is null then the + * system-dependent default temporary-file directory will be used. The + * default temporary-file directory is specified by the system property + * java.io.tmpdir. On UNIX systems the default value of this + * property is typically "/tmp" or "/var/tmp"; on + * Microsoft Windows systems it is typically "C:\\WINNT\\TEMP". A different + * value may be given to this system property when the Java virtual machine + * is invoked, but programmatic changes to this property are not guaranteed + * to have any effect upon the temporary directory used by this method. + * + * @param prefix The prefix string to be used in generating the file's + * name; must be at least three characters long + * + * @param suffix The suffix string to be used in generating the file's + * name; may be null, in which case the + * suffix ".tmp" will be used + * + * @param directory The directory in which the file is to be created, or + * null if the default temporary-file + * directory is to be used + * + * @return An abstract pathname denoting a newly-created empty file + * + * @throws IllegalArgumentException + * If the prefix argument contains fewer than three + * characters + * + * @throws IOException If a file could not be created + * + * @since 1.2 + */ + public AmazeFile createTempFile(String prefix, String suffix, + AmazeFile directory, Random random) + throws IOException + { + if (prefix.length() < 3) + throw new IllegalArgumentException("Prefix string too short"); + if (suffix == null) + suffix = ".tmp"; + + // Android-changed: Handle java.io.tmpdir changes. + AmazeFile tmpdir = (directory != null) ? directory + : new AmazeFile(System.getProperty("java.io.tmpdir", ".")); + AmazeFile f; + do { + f = TempDirectory.generateFile(prefix, suffix, tmpdir, random); + } while ((fs.getBooleanAttributes(f) & AmazeFileSystem.BA_EXISTS) != 0); + + if (!fs.createFileExclusively(f.getPath())) + throw new IOException("Unable to create temporary file"); + + return f; + } + + /** + * Creates an empty file in the default temporary-file directory, using + * the given prefix and suffix to generate its name. Invoking this method + * is equivalent to invoking {@link #createTempFile(java.lang.String, + * java.lang.String, java.io.File) + * createTempFile(prefix, suffix, null)}. + * + *

The {@link + * java.nio.file.Files#createTempFile(String,String,java.nio.file.attribute.FileAttribute[]) + * Files.createTempFile} method provides an alternative method to create an + * empty file in the temporary-file directory. Files created by that method + * may have more restrictive access permissions to files created by this + * method and so may be more suited to security-sensitive applications. + * + * @param prefix The prefix string to be used in generating the file's + * name; must be at least three characters long + * + * @param suffix The suffix string to be used in generating the file's + * name; may be null, in which case the + * suffix ".tmp" will be used + * + * @return An abstract pathname denoting a newly-created empty file + * + * @throws IllegalArgumentException + * If the prefix argument contains fewer than three + * characters + * + * @throws IOException If a file could not be created + * + * @since 1.2 + * @see java.nio.file.Files#createTempDirectory(String,FileAttribute[]) + */ + public AmazeFile createTempFile(String prefix, String suffix, Random random) + throws IOException + { + return createTempFile(prefix, suffix, null, random); + } + + /* -- Basic infrastructure -- */ + + /** + * Compares two abstract pathnames lexicographically. The ordering + * defined by this method depends upon the underlying system. On UNIX + * systems, alphabetic case is significant in comparing pathnames; on Microsoft Windows + * systems it is not. + * + * @param pathname The abstract pathname to be compared to this abstract + * pathname + * + * @return Zero if the argument is equal to this abstract pathname, a + * value less than zero if this abstract pathname is + * lexicographically less than the argument, or a value greater + * than zero if this abstract pathname is lexicographically + * greater than the argument + * + * @since 1.2 + */ + public int compareTo(AmazeFile pathname) { + return fs.compare(this, pathname); + } + + /** + * Tests this abstract pathname for equality with the given object. + * Returns true if and only if the argument is not + * null and is an abstract pathname that denotes the same file + * or directory as this abstract pathname. Whether or not two abstract + * pathnames are equal depends upon the underlying system. On UNIX + * systems, alphabetic case is significant in comparing pathnames; on Microsoft Windows + * systems it is not. + * + * @param obj The object to be compared with this abstract pathname + * + * @return true if and only if the objects are the same; + * false otherwise + */ + public boolean equals(Object obj) { + if (obj instanceof AmazeFile) { + return compareTo((AmazeFile)obj) == 0; + } + return false; + } + + /** + * Computes a hash code for this abstract pathname. Because equality of + * abstract pathnames is inherently system-dependent, so is the computation + * of their hash codes. On UNIX systems, the hash code of an abstract + * pathname is equal to the exclusive or of the hash code + * of its pathname string and the decimal value + * 1234321. On Microsoft Windows systems, the hash + * code is equal to the exclusive or of the hash code of + * its pathname string converted to lower case and the decimal + * value 1234321. Locale is not taken into account on + * lowercasing the pathname string. + * + * @return A hash code for this abstract pathname + */ + public int hashCode() { + return fs.hashCode(this); + } + + /** + * Returns the pathname string of this abstract pathname. This is just the + * string returned by the {@link #getPath} method. + * + * @return The string form of this abstract pathname + */ + @NonNull + public String toString() { + return getPath(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(getPath()); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public AmazeFile createFromParcel(Parcel in) { + return new AmazeFile(in.readString()); + } + + public AmazeFile[] newArray(int size) { + return new AmazeFile[size]; + } + }; +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileFilter.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileFilter.java new file mode 100644 index 0000000000..3edf717acd --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileFilter.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.amaze.filemanager.file_operations.filesystem.filetypes; + +import java.io.File; + +/** + * A filter for abstract pathnames. + * + *

Instances of this interface may be passed to the {@link + * File#listFiles(java.io.FileFilter) listFiles(FileFilter)} method + * of the {@link java.io.File} class. + * + * @since 1.2 + */ +@FunctionalInterface +public interface AmazeFileFilter { + + /** + * Tests whether or not the specified abstract pathname should be + * included in a pathname list. + * + * @param pathname The abstract pathname to be tested + * @return true if and only if pathname + * should be included + */ + boolean accept(AmazeFile pathname); +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java new file mode 100644 index 0000000000..63af050eac --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.amaze.filemanager.file_operations.filesystem.filetypes; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.annotation.Native; + +public abstract class AmazeFileSystem implements Closeable { + + /* -- Normalization and construction -- */ + + /** + * Is the path of this filesystem? + */ + public abstract boolean isPathOfThisFilesystem(String path); + + /** + * Return the local filesystem's name-separator character. + */ + public abstract char getSeparator(); + + /** + * Return the local filesystem's path-separator character. + */ + public abstract char getPathSeparator(); + + /** + * Convert the given pathname string to normal form. If the string is + * already in normal form then it is simply returned. + */ + public abstract String normalize(String path); + + /** + * Compute the length of this pathname string's prefix. The pathname + * string must be in normal form. + */ + public abstract int prefixLength(String path); + + /** + * Resolve the child pathname string against the parent. + * Both strings must be in normal form, and the result + * will be in normal form. + */ + public abstract String resolve(String parent, String child); + + /** + * Return the parent pathname string to be used when the parent-directory + * argument in one of the two-argument File constructors is the empty + * pathname. + */ + public abstract String getDefaultParent(); + + /** + * Post-process the given URI path string if necessary. This is used on + * win32, e.g., to transform "/c:/foo" into "c:/foo". The path string + * still has slash separators; code in the File class will translate them + * after this method returns. + */ + public abstract String fromURIPath(String path); + + + /* -- Path operations -- */ + + /** + * Tell whether or not the given abstract pathname is absolute. + */ + public abstract boolean isAbsolute(AmazeFile f); + + /** + * Resolve the given abstract pathname into absolute form. Invoked by the + * getAbsolutePath and getCanonicalPath methods in the F class. + */ + public abstract String resolve(AmazeFile f); + + public abstract String canonicalize(String path) throws IOException; + + + /* -- Attribute accessors -- */ + + /* Constants for simple boolean attributes */ + @Native + public static final int BA_EXISTS = 0x01; + @Native public static final int BA_REGULAR = 0x02; + @Native public static final int BA_DIRECTORY = 0x04; + @Native public static final int BA_HIDDEN = 0x08; + + /** + * Return the simple boolean attributes for the file or directory denoted + * by the given abstract pathname, or zero if it does not exist or some + * other I/O error occurs. + */ + public abstract int getBooleanAttributes(AmazeFile f); + + @Native public static final int ACCESS_READ = 0x04; + @Native public static final int ACCESS_WRITE = 0x02; + @Native public static final int ACCESS_EXECUTE = 0x01; + // Android-added: b/25878034, to support F.exists() reimplementation. + public static final int ACCESS_CHECK_EXISTS = 0x08; + + /** + * Check whether the file or directory denoted by the given abstract + * pathname may be accessed by this process. The second argument specifies + * which access, ACCESS_READ, ACCESS_WRITE or ACCESS_EXECUTE, to check. + * Return false if access is denied or an I/O error occurs + */ + public abstract boolean checkAccess(AmazeFile f, int access); + /** + * Set on or off the access permission (to owner only or to all) to the file + * or directory denoted by the given abstract pathname, based on the parameters + * enable, access and oweronly. + */ + public abstract boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly); + + /** + * Return the time at which the file or directory denoted by the given + * abstract pathname was last modified, or zero if it does not exist or + * some other I/O error occurs. + */ + public abstract long getLastModifiedTime(AmazeFile f); + + /** + * Return the length in bytes of the file denoted by the given abstract + * pathname, or zero if it does not exist, is a directory, or some other + * I/O error occurs. + */ + public abstract long getLength(AmazeFile f) throws IOException; + + + /* -- File operations -- */ + + /** + * Create a new empty file with the given pathname. Return + * true if the file was created and false if a + * file or directory with the given pathname already exists. Throw an + * IOException if an I/O error occurs. + */ + public abstract boolean createFileExclusively(String pathname) + throws IOException; + + /** + * Delete the file or directory denoted by the given abstract pathname, + * returning true if and only if the operation succeeds. + */ + public abstract boolean delete(AmazeFile f); + + /** + * List the elements of the directory denoted by the given abstract + * pathname. Return an array of strings naming the elements of the + * directory if successful; otherwise, return null. + */ + public abstract String[] list(AmazeFile f); + + public abstract InputStream getInputStream(AmazeFile f); + + public abstract OutputStream getOutputStream(AmazeFile f); + + /** + * Create a new directory denoted by the given abstract pathname, + * returning true if and only if the operation succeeds. + */ + public abstract boolean createDirectory(AmazeFile f); + + /** + * Rename the file or directory denoted by the first abstract pathname to + * the second abstract pathname, returning true if and only if + * the operation succeeds. + */ + public abstract boolean rename(AmazeFile f1, AmazeFile f2); + + /** + * Set the last-modified time of the file or directory denoted by the + * given abstract pathname, returning true if and only if the + * operation succeeds. + */ + public abstract boolean setLastModifiedTime(AmazeFile f, long time); + + /** + * Mark the file or directory denoted by the given abstract pathname as + * read-only, returning true if and only if the operation + * succeeds. + */ + public abstract boolean setReadOnly(AmazeFile f); + + + /* -- Filesystem interface -- */ + + /** + * List the available filesystem roots. + */ + public abstract AmazeFile[] listRoots(); + + /* -- Disk usage -- */ + @Native public static final int SPACE_TOTAL = 0; + @Native public static final int SPACE_FREE = 1; + @Native public static final int SPACE_USABLE = 2; + + public abstract long getSpace(AmazeFile f, int t); + + /* -- Basic infrastructure -- */ + + /** + * Compare two abstract pathnames lexicographically. + */ + public abstract int compare(AmazeFile f1, AmazeFile f2); + + /** + * Compute the hash code of an abstract pathname. + */ + public abstract int hashCode(AmazeFile f); + + // Flags for enabling/disabling performance optimizations for file + // name canonicalization + // Android-changed: Disabled caches for security reasons (b/62301183) + //static boolean useCanonCaches = true; + //static boolean useCanonPrefixCache = true; + static boolean useCanonCaches = false; + static boolean useCanonPrefixCache = false; + + private static boolean getBooleanProperty(String prop, boolean defaultVal) { + String val = System.getProperty(prop); + if (val == null) return defaultVal; + if (val.equalsIgnoreCase("true")) { + return true; + } else { + return false; + } + } + + static { + useCanonCaches = getBooleanProperty("sun.io.useCanonCaches", + useCanonCaches); + useCanonPrefixCache = getBooleanProperty("sun.io.useCanonPrefixCache", + useCanonPrefixCache); + } + + /* + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + * A normal Unix pathname does not contain consecutive slashes and does not end + * with a slash. The empty string and "/" are special cases that are also + * considered normal. + */ + public static String simpleUnixNormalize(String pathname) { + int n = pathname.length(); + char[] normalized = pathname.toCharArray(); + int index = 0; + char prevChar = 0; + for (int i = 0; i < n; i++) { + char current = normalized[i]; + // Remove duplicate slashes. + if (!(current == '/' && prevChar == '/')) { + normalized[index++] = current; + } + + prevChar = current; + } + + // Omit the trailing slash, except when pathname == "/". + if (prevChar == '/' && n > 1) { + index--; + } + + return (index != n) ? new String(normalized, 0, index) : pathname; + } + + /* + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + // Invariant: Both |parent| and |child| are normalized paths. + public static String basicUnixResolve(String parent, String child) { + if (child.isEmpty() || child.equals("/")) { + return parent; + } + + if (child.charAt(0) == '/') { + if (parent.equals("/")) return child; + return parent + child; + } + + if (parent.equals("/")) return parent + child; + return parent + '/' + child; + } + + public static int basicUnixHashCode(String path) { + return path.hashCode() ^ 1234321; + } + +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilenameFilter.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilenameFilter.java new file mode 100644 index 0000000000..1251c4ec9e --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilenameFilter.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.amaze.filemanager.file_operations.filesystem.filetypes; + +// Android-changed: Removed @see tag (target does not exist on Android): +// @see java.awt.FileDialog#setFilenameFilter(java.io.FilenameFilter) + +/** + * Instances of classes that implement this interface are used to + * filter filenames. These instances are used to filter directory + * listings in the list method of class + * File, and by the Abstract Window Toolkit's file + * dialog component. + * + * @author Arthur van Hoff + * @author Jonathan Payne + * @see java.io.File + * @see java.io.File#list(java.io.FilenameFilter) + * @since JDK1.0 + */ +@FunctionalInterface +public interface AmazeFilenameFilter { + /** + * Tests if a specified file should be included in a file list. + * + * @param dir the directory in which the file was found. + * @param name the name of the file. + * @return true if and only if the name should be + * included in the file list; false otherwise. + */ + boolean accept(AmazeFile dir, String name); +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/OnAmazeFileFound.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/OnAmazeFileFound.kt new file mode 100644 index 0000000000..b1f4508d41 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/OnAmazeFileFound.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes + +/** + * This allows the caller of a function to know when a file has ben found and deal with it ASAP + */ +interface OnAmazeFileFound { + @Suppress("UndocumentedPublicFunction") + fun onFileFound(file: AmazeFile) +} From 85f4f50196b56fde1dec1403f7eb3b04d9fced0f Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Wed, 22 Dec 2021 19:22:30 -0300 Subject: [PATCH 02/46] Update SMB file handling to new system --- app/build.gradle | 5 +- .../asynctasks/LoadFilesListTask.java | 8 +- .../filemanager/filesystem/HybridFile.java | 220 +++------- .../filesystem/HybridFileParcelable.java | 1 - .../filesystem/MakeDirectoryOperation.kt | 9 +- .../filemanager/filesystem/Operations.java | 52 +-- .../filesystem/cloud/CloudUtil.java | 9 +- .../filesystem/files/FileUtils.java | 6 +- .../filesystem/smb/CifsContexts.kt | 1 + .../ui/fragments/MainFragment.java | 19 +- .../com/amaze/filemanager/utils/SmbUtil.java | 4 + build.gradle | 2 + file_operations/build.gradle | 7 + .../filesystem/filetypes/smb/CifsContexts.kt | 101 +++++ .../filetypes/smb/SmbAmazeFileSystem.java | 394 ++++++++++++++++++ .../filesystem/smbstreamer/StreamSource.java | 7 +- .../filesystem/smbstreamer/Streamer.java | 8 +- .../smbstreamer/StreamSourceTest.java | 7 +- 18 files changed, 609 insertions(+), 251 deletions(-) create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/CifsContexts.kt create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java diff --git a/app/build.gradle b/app/build.gradle index d2bda34d3d..ee3b234b40 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -234,11 +234,12 @@ dependencies { implementation 'org.tukaani:xz:1.8' - implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' + //ReactiveX + implementation "io.reactivex.rxjava2:rxandroid:$reactiveXAndroidVersion" // Because RxAndroid releases are few and far between, it is recommended you also // explicitly depend on RxJava's latest version for bug fixes and new features. // (see https://github.com/ReactiveX/RxJava/releases for latest 3.x.x version) - implementation group: 'io.reactivex.rxjava2', name: 'rxjava', version: '2.2.9' + implementation "io.reactivex.rxjava2:rxjava:$reactiveXVersion" implementation project(':commons_compress_7z') implementation project(':file_operations') diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java index d710836985..98b5a6c8a8 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java @@ -24,6 +24,7 @@ import static android.os.Build.VERSION_CODES.Q; import java.io.File; +import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Calendar; @@ -38,6 +39,7 @@ import com.amaze.filemanager.database.UtilsHandler; import com.amaze.filemanager.file_operations.exceptions.CloudPluginException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.filesystem.HybridFile; import com.amaze.filemanager.filesystem.HybridFileParcelable; import com.amaze.filemanager.filesystem.RootHelper; @@ -69,8 +71,6 @@ import androidx.core.util.Pair; import jcifs.smb.SmbAuthException; -import jcifs.smb.SmbException; -import jcifs.smb.SmbFile; public class LoadFilesListTask extends AsyncTask>> { @@ -141,7 +141,7 @@ public LoadFilesListTask( hFile.setPath(hFile.getPath() + "/"); } try { - SmbFile[] smbFile = hFile.getSmbFile(5000).listFiles(); + AmazeFile[] smbFile = new AmazeFile(hFile.getPath()).listFiles(); list = mainFragment.addToSmb(smbFile, path, showHiddenFiles); openmode = OpenMode.SMB; } catch (SmbAuthException e) { @@ -150,7 +150,7 @@ public LoadFilesListTask( } e.printStackTrace(); return null; - } catch (SmbException | NullPointerException e) { + } catch (NullPointerException | IOException e) { Log.w(getClass().getSimpleName(), "Failed to load smb files for path: " + path, e); mainFragment.reauthenticateSmb(); return null; diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 0405e62ccc..dc78a3b771 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -22,6 +22,7 @@ import static com.amaze.filemanager.filesystem.smb.CifsContexts.SMB_URI_PREFIX; import static com.amaze.filemanager.filesystem.ssh.SshConnectionPool.SSH_URI_PREFIX; +import static com.amaze.filemanager.utils.SmbUtil.create; import java.io.File; import java.io.FileInputStream; @@ -41,6 +42,7 @@ import com.amaze.filemanager.file_operations.exceptions.CloudPluginException; import com.amaze.filemanager.file_operations.exceptions.ShellNotRunningException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.root.NativeOperations; import com.amaze.filemanager.filesystem.cloud.CloudUtil; import com.amaze.filemanager.filesystem.files.FileUtils; @@ -55,7 +57,6 @@ import com.amaze.filemanager.utils.DataUtils; import com.amaze.filemanager.utils.OTGUtil; import com.amaze.filemanager.utils.OnFileFound; -import com.amaze.filemanager.utils.SmbUtil; import com.amaze.filemanager.utils.Utils; import com.cloudrail.si.interfaces.CloudStorage; import com.cloudrail.si.types.SpaceAllocation; @@ -71,8 +72,6 @@ import androidx.documentfile.provider.DocumentFile; import androidx.preference.PreferenceManager; -import io.reactivex.Single; -import io.reactivex.schedulers.Schedulers; import jcifs.smb.SmbException; import jcifs.smb.SmbFile; import net.schmizz.sshj.SSHClient; @@ -83,7 +82,12 @@ import net.schmizz.sshj.sftp.SFTPClient; import net.schmizz.sshj.sftp.SFTPException; -/** Hybrid file for handeling all types of files */ +/** + * Hybrid file for handeling all types of files + * + *

This is deprecated, please use AmazeFile instead + */ +@Deprecated public class HybridFile { protected static final String TAG = HybridFile.class.getSimpleName(); @@ -261,16 +265,7 @@ public Long execute(@NonNull SFTPClient client) throws IOException { return returnValue == null ? 0L : returnValue; case SMB: - SmbFile smbFile = getSmbFile(); - if (smbFile != null) { - try { - return smbFile.lastModified(); - } catch (SmbException e) { - Log.e(TAG, "Error getting last modified time for SMB [" + path + "]", e); - return 0; - } - } - break; + return new AmazeFile(path).lastModified(); case FILE: return getFile().lastModified(); case DOCUMENT_FILE: @@ -289,14 +284,11 @@ public long length(Context context) { case SFTP: return ((HybridFileParcelable) this).getSize(); case SMB: - SmbFile smbFile = getSmbFile(); - if (smbFile != null) - try { - s = smbFile.length(); - } catch (SmbException e) { - e.printStackTrace(); - } - return s; + try { + return new AmazeFile(path).length(); + } catch (IOException e) { + Log.e(TAG, "Error getting length for SMB file", e); + } case FILE: s = getFile().length(); return s; @@ -352,9 +344,7 @@ public String getSimpleName() { String name = null; switch (mode) { case SMB: - SmbFile smbFile = getSmbFile(); - if (smbFile != null) return smbFile.getName(); - break; + return new AmazeFile(path).getName(); default: StringBuilder builder = new StringBuilder(path); name = builder.substring(builder.lastIndexOf("/") + 1, builder.length()); @@ -365,11 +355,7 @@ public String getSimpleName() { public String getName(Context context) { switch (mode) { case SMB: - SmbFile smbFile = getSmbFile(); - if (smbFile != null) { - return smbFile.getName(); - } - return null; + return new AmazeFile(path).getName(); case FILE: case ROOT: return getFile().getName(); @@ -401,26 +387,6 @@ public String getName(Context context) { } } - public SmbFile getSmbFile(int timeout) { - try { - SmbFile smbFile = SmbUtil.create(path); - smbFile.setConnectTimeout(timeout); - return smbFile; - } catch (MalformedURLException e) { - e.printStackTrace(); - return null; - } - } - - public SmbFile getSmbFile() { - try { - return SmbUtil.create(path); - } catch (MalformedURLException e) { - e.printStackTrace(); - return null; - } - } - public boolean isCustomPath() { return path.equals("0") || path.equals("1") @@ -435,11 +401,7 @@ public boolean isCustomPath() { public String getParent(Context context) { switch (mode) { case SMB: - SmbFile smbFile = getSmbFile(); - if (smbFile != null) { - return smbFile.getParent(); - } - return ""; + return new AmazeFile(path).getParent(); case FILE: case ROOT: return getFile().getParent(); @@ -475,14 +437,7 @@ public boolean isDirectory() { case SFTP: return isDirectory(AppConfig.getInstance()); case SMB: - SmbFile smbFile = getSmbFile(); - try { - isDirectory = smbFile != null && smbFile.isDirectory(); - } catch (SmbException e) { - e.printStackTrace(); - isDirectory = false; - } - break; + return new AmazeFile(path).isDirectory(); case FILE: isDirectory = getFile().isDirectory(); break; @@ -534,17 +489,7 @@ public Boolean execute(SFTPClient client) { //noinspection SimplifiableConditionalExpression return returnValue == null ? false : returnValue; case SMB: - try { - isDirectory = - Single.fromCallable(() -> getSmbFile().isDirectory()) - .subscribeOn(Schedulers.io()) - .blockingGet(); - } catch (Exception e) { - isDirectory = false; - if (e.getCause() != null) e.getCause().printStackTrace(); - else e.printStackTrace(); - } - break; + return new AmazeFile(path).isDirectory(); case FILE: isDirectory = getFile().isDirectory(); break; @@ -600,9 +545,7 @@ public long folderSize() { case SFTP: return folderSize(AppConfig.getInstance()); case SMB: - SmbFile smbFile = getSmbFile(); - size = smbFile != null ? FileUtils.folderSize(getSmbFile()) : 0; - break; + return FileUtils.folderSize(new AmazeFile(getPath())); case FILE: size = FileUtils.folderSize(getFile(), null); break; @@ -638,9 +581,7 @@ public Long execute(SFTPClient client) throws IOException { return returnValue == null ? 0L : returnValue; case SMB: - SmbFile smbFile = getSmbFile(); - size = (smbFile != null) ? FileUtils.folderSize(smbFile) : 0L; - break; + return FileUtils.folderSize(new AmazeFile(getPath())); case FILE: size = FileUtils.folderSize(getFile(), null); break; @@ -679,14 +620,7 @@ public long getUsableSpace() { long size = 0L; switch (mode) { case SMB: - try { - SmbFile smbFile = getSmbFile(); - size = smbFile != null ? smbFile.getDiskFreeSpace() : 0L; - } catch (SmbException e) { - size = 0L; - e.printStackTrace(); - } - break; + return new AmazeFile(path).getUsableSpace(); case FILE: case ROOT: size = getFile().getUsableSpace(); @@ -747,14 +681,7 @@ public long getTotal(Context context) { long size = 0l; switch (mode) { case SMB: - // TODO: Find total storage space of SMB when JCIFS adds support - try { - SmbFile smbFile = getSmbFile(); - size = smbFile != null ? smbFile.getDiskFreeSpace() : 0L; - } catch (SmbException e) { - e.printStackTrace(); - } - break; + return new AmazeFile(path).getTotalSpace(); case FILE: case ROOT: size = getFile().getTotalSpace(); @@ -847,21 +774,19 @@ public Boolean execute(SFTPClient client) { break; case SMB: try { - SmbFile smbFile = getSmbFile(); - if (smbFile != null) { - for (SmbFile smbFile1 : smbFile.listFiles()) { - HybridFileParcelable baseFile; - try { - SmbFile sf = new SmbFile(smbFile1.getURL(), smbFile.getContext()); - baseFile = new HybridFileParcelable(sf); - } catch (MalformedURLException shouldNeverHappen) { - shouldNeverHappen.printStackTrace(); - baseFile = new HybridFileParcelable(smbFile1); - } - onFileFound.onFileFound(baseFile); + SmbFile smbFile = create(getPath()); + for (SmbFile smbFile1 : smbFile.listFiles()) { + HybridFileParcelable baseFile; + try { + SmbFile sf = new SmbFile(smbFile1.getURL(), smbFile.getContext()); + baseFile = new HybridFileParcelable(sf); + } catch (MalformedURLException shouldNeverHappen) { + shouldNeverHappen.printStackTrace(); + baseFile = new HybridFileParcelable(smbFile1); } + onFileFound.onFileFound(baseFile); } - } catch (SmbException e) { + } catch (MalformedURLException | SmbException e) { e.printStackTrace(); } break; @@ -931,19 +856,17 @@ public ArrayList execute(SFTPClient client) { }); break; case SMB: - try { - SmbFile smbFile = getSmbFile(); - if (smbFile != null) { - for (SmbFile smbFile1 : smbFile.listFiles()) { - HybridFileParcelable baseFile = new HybridFileParcelable(smbFile1); - arrayList.add(baseFile); - } + ArrayList result = new ArrayList<>(); + for (AmazeFile smbFile1 : new AmazeFile(getPath()).listFiles()) { + try { + HybridFileParcelable baseFile = new HybridFileParcelable(create(smbFile1.getPath())); + result.add(baseFile); + } catch (SmbException | MalformedURLException e) { + Log.e(TAG, "Error getting an SMB file", e); + return null; } - } catch (SmbException e) { - arrayList.clear(); - e.printStackTrace(); } - break; + return result; case OTG: arrayList = OTGUtil.getDocumentFilesList(path, context); break; @@ -1022,12 +945,7 @@ public void close() throws IOException { } }); } else if (isSmb()) { - try { - inputStream = getSmbFile().getInputStream(); - } catch (IOException e) { - inputStream = null; - e.printStackTrace(); - } + return new AmazeFile(getPath()).getInputStream(); } else { try { inputStream = new FileInputStream(path); @@ -1066,13 +984,7 @@ public void close() throws IOException { }); break; case SMB: - try { - inputStream = getSmbFile().getInputStream(); - } catch (IOException e) { - inputStream = null; - e.printStackTrace(); - } - break; + return new AmazeFile(getPath()).getInputStream(); case DOCUMENT_FILE: ContentResolver contentResolver = context.getContentResolver(); DocumentFile documentSourceFile = getDocumentFile(false); @@ -1156,13 +1068,7 @@ public void close() throws IOException { } }); case SMB: - try { - outputStream = getSmbFile().getOutputStream(); - } catch (IOException e) { - outputStream = null; - e.printStackTrace(); - } - break; + return new AmazeFile(path).getOutputStream(); case DOCUMENT_FILE: ContentResolver contentResolver = context.getContentResolver(); DocumentFile documentSourceFile = getDocumentFile(true); @@ -1217,13 +1123,7 @@ public Boolean execute(SFTPClient client) throws IOException { //noinspection SimplifiableConditionalExpression exists = executionReturn == null ? false : executionReturn; } else if (isSmb()) { - try { - SmbFile smbFile = getSmbFile(2000); - exists = smbFile != null && smbFile.exists(); - } catch (SmbException e) { - e.printStackTrace(); - exists = false; - } + return new AmazeFile(path).exists(); } else if (isDropBoxFile()) { CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX); exists = cloudStorageDropbox.exists(CloudUtil.stripPath(OpenMode.DROPBOX, path)); @@ -1284,18 +1184,7 @@ public boolean isSimpleFile() { public boolean setLastModified(final long date) { if (isSmb()) { - try { - SmbFile smbFile = getSmbFile(); - if (smbFile != null) { - smbFile.setLastModified(date); - return true; - } else { - return false; - } - } catch (SmbException e) { - e.printStackTrace(); - return false; - } + return new AmazeFile(path).setLastModified(date); } File f = getFile(); return f.setLastModified(date); @@ -1317,11 +1206,7 @@ public Void execute(@NonNull SFTPClient client) { } }); } else if (isSmb()) { - try { - getSmbFile().mkdirs(); - } catch (SmbException e) { - e.printStackTrace(); - } + new AmazeFile(path).mkdirs(); } else if (isOtgFile()) { if (!exists(context)) { DocumentFile parentDirectory = OTGUtil.getDocumentFile(getParent(context), context, true); @@ -1389,12 +1274,7 @@ public Boolean execute(@NonNull SFTPClient client) throws IOException { }); return retval != null && retval; } else if (isSmb()) { - try { - getSmbFile().delete(); - } catch (SmbException e) { - Log.e(TAG, "Error delete SMB file", e); - throw e; - } + return new AmazeFile(path).delete(); } else { if (isRoot() && rootmode) { setMode(OpenMode.ROOT); diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFileParcelable.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFileParcelable.java index 002b7d6298..15119df503 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFileParcelable.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFileParcelable.java @@ -38,7 +38,6 @@ import jcifs.smb.SmbException; import jcifs.smb.SmbFile; import kotlin.Deprecated; - import net.schmizz.sshj.sftp.RemoteResourceInfo; import net.schmizz.sshj.xfer.FilePermission; diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt b/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt index 95bc45c078..272e639de6 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt @@ -23,6 +23,7 @@ package com.amaze.filemanager.filesystem import android.content.Context import android.os.Build import com.amaze.filemanager.file_operations.filesystem.OpenMode +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.utils.OTGUtil import jcifs.smb.SmbException import java.io.File @@ -76,13 +77,7 @@ object MakeDirectoryOperation { var isSuccessful = true when (file.mode) { OpenMode.SMB -> - try { - val smbFile = file.smbFile - smbFile.mkdirs() - } catch (e: SmbException) { - e.printStackTrace() - isSuccessful = false - } + return AmazeFile(file.path).mkdirs() OpenMode.OTG -> { val documentFile = OTGUtil.getDocumentFile(file.getPath(), context, true) isSuccessful = documentFile != null diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java index 081eb14fd8..4d55d2ae72 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java @@ -27,14 +27,13 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.net.MalformedURLException; -import java.net.URL; import java.util.ArrayList; import java.util.concurrent.Executor; import com.amaze.filemanager.R; import com.amaze.filemanager.file_operations.exceptions.ShellNotRunningException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.filesystem.cloud.CloudUtil; import com.amaze.filemanager.filesystem.files.FileUtils; import com.amaze.filemanager.filesystem.root.MakeDirectoryCommand; @@ -57,8 +56,6 @@ import androidx.arch.core.util.Function; import androidx.documentfile.provider.DocumentFile; -import jcifs.smb.SmbException; -import jcifs.smb.SmbFile; import net.schmizz.sshj.sftp.SFTPClient; public class Operations { @@ -149,13 +146,7 @@ protected Void doInBackground(Void... params) { return null; } if (file.isSmb()) { - try { - file.getSmbFile(2000).mkdirs(); - } catch (SmbException e) { - e.printStackTrace(); - errorCallBack.done(file, false); - return null; - } + new AmazeFile(file.getPath()).mkdirs(); errorCallBack.done(file, file.exists()); return null; } else if (file.isOtgFile()) { @@ -304,8 +295,8 @@ protected Void doInBackground(Void... params) { } if (file.isSmb()) { try { - file.getSmbFile(2000).createNewFile(); - } catch (SmbException e) { + new AmazeFile(file.getPath()).createNewFile(); + } catch (IOException e) { e.printStackTrace(); errorCallBack.done(file, false); return null; @@ -456,34 +447,15 @@ protected Void doInBackground(Void... params) { } if (oldFile.isSmb()) { - try { - SmbFile smbFile = oldFile.getSmbFile(); - // FIXME: smbFile1 should be created from SmbUtil too so it can be mocked - SmbFile smbFile1 = new SmbFile(new URL(newFile.getPath()), smbFile.getContext()); - if (newFile.exists()) { - errorCallBack.exists(newFile); - return null; - } - smbFile.renameTo(smbFile1); - if (!smbFile.exists() && smbFile1.exists()) errorCallBack.done(newFile, true); - } catch (SmbException | MalformedURLException e) { - String errmsg = - context.getString( - R.string.cannot_rename_file, - HybridFile.parseAndFormatUriForDisplay(oldFile.getPath()), - e.getMessage()); - try { - ArrayList failedOps = new ArrayList<>(); - failedOps.add(new HybridFileParcelable(oldFile.getSmbFile())); - context.sendBroadcast( - new Intent(TAG_INTENT_FILTER_GENERAL) - .putParcelableArrayListExtra(TAG_INTENT_FILTER_FAILED_OPS, failedOps)); - } catch (SmbException exceptionThrownDuringBuildParcelable) { - Log.e( - TAG, "Error creating HybridFileParcelable", exceptionThrownDuringBuildParcelable); - } - Log.e(TAG, errmsg, e); + AmazeFile smbFile = new AmazeFile(oldFile.getPath()); + // FIXME: smbFile1 should be created from SmbUtil too so it can be mocked + AmazeFile smbFile1 = new AmazeFile(newFile.getPath()); + if (newFile.exists()) { + errorCallBack.exists(newFile); + return null; } + smbFile.renameTo(smbFile1); + if (!smbFile.exists() && smbFile1.exists()) errorCallBack.done(newFile, true); return null; } else if (oldFile.isSftp()) { SshClientUtils.execute( diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java index 4aabb07f8d..f2e185ac94 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java @@ -34,6 +34,7 @@ import com.amaze.filemanager.file_operations.exceptions.CloudPluginException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; import com.amaze.filemanager.file_operations.filesystem.cloud.CloudStreamer; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.filesystem.HybridFile; import com.amaze.filemanager.filesystem.HybridFileParcelable; import com.amaze.filemanager.filesystem.ssh.SFtpClientTemplate; @@ -277,13 +278,7 @@ public void close() throws IOException { }); break; case SMB: - try { - inputStream = hybridFile.getSmbFile().getInputStream(); - } catch (IOException e) { - inputStream = null; - e.printStackTrace(); - } - break; + return new AmazeFile(hybridFile.getPath()).getInputStream(); case OTG: ContentResolver contentResolver = context.getContentResolver(); DocumentFile documentSourceFile = diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java index a41ab695e7..943f32eece 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java @@ -51,6 +51,7 @@ import com.amaze.filemanager.adapters.data.LayoutElementParcelable; import com.amaze.filemanager.application.AppConfig; import com.amaze.filemanager.file_operations.filesystem.OpenMode; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.filesystem.ExternalSdCardOperation; import com.amaze.filemanager.filesystem.HybridFile; import com.amaze.filemanager.filesystem.HybridFileParcelable; @@ -89,7 +90,6 @@ import java.util.LinkedList; import java.util.concurrent.atomic.AtomicLong; -import jcifs.smb.SmbFile; import kotlin.collections.ArraysKt; /** Functions that deal with files */ @@ -126,10 +126,10 @@ public static long folderSize(HybridFile directory, OnProgressUpdate updat else return directory.folderSize(AppConfig.getInstance()); } - public static long folderSize(SmbFile directory) { + public static long folderSize(AmazeFile directory) { long length = 0; try { - for (SmbFile file : directory.listFiles()) { + for (AmazeFile file : directory.listFiles()) { if (file.isFile()) length += file.length(); else length += folderSize(file); diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/smb/CifsContexts.kt b/app/src/main/java/com/amaze/filemanager/filesystem/smb/CifsContexts.kt index 229cdb38b1..4062815447 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/smb/CifsContexts.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/smb/CifsContexts.kt @@ -32,6 +32,7 @@ import jcifs.context.SingletonContext import java.util.* import java.util.concurrent.ConcurrentHashMap +@Deprecated("Replaced with [com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts]") object CifsContexts { const val SMB_URI_PREFIX = "smb://" diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java index 885a0cd138..f3f848742d 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java @@ -31,6 +31,7 @@ import static com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants.PREFERENCE_SHOW_THUMB; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -51,6 +52,7 @@ import com.amaze.filemanager.database.SortHandler; import com.amaze.filemanager.database.models.explorer.Tab; import com.amaze.filemanager.file_operations.filesystem.OpenMode; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.smbstreamer.Streamer; import com.amaze.filemanager.filesystem.CustomFileObserver; import com.amaze.filemanager.filesystem.FileProperties; @@ -133,9 +135,6 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import jcifs.smb.SmbException; -import jcifs.smb.SmbFile; - public class MainFragment extends Fragment implements BottomBarButtonPath, ViewTreeObserver.OnGlobalLayoutListener, @@ -1028,7 +1027,7 @@ public void goBack() { && !mainFragmentViewModel .getSmbPath() .equals(mainFragmentViewModel.getCurrentPath())) { - StringBuilder path = new StringBuilder(currentFile.getSmbFile().getParent()); + StringBuilder path = new StringBuilder(currentFile.getParent(requireContext())); if (mainFragmentViewModel.getCurrentPath().indexOf('?') > 0) path.append( mainFragmentViewModel @@ -1176,7 +1175,7 @@ public void goBackItemClick() { && !mainFragmentViewModel .getCurrentPath() .equals(mainFragmentViewModel.getSmbPath())) { - StringBuilder path = new StringBuilder(currentFile.getSmbFile().getParent()); + StringBuilder path = new StringBuilder(currentFile.getParent(requireContext())); if (mainFragmentViewModel.getCurrentPath().indexOf('?') > 0) path.append( mainFragmentViewModel @@ -1225,14 +1224,18 @@ public void onPause() { } public ArrayList addToSmb( - @NonNull SmbFile[] mFile, @NonNull String path, boolean showHiddenFiles) throws SmbException { + @NonNull AmazeFile[] mFile, @NonNull String path, boolean showHiddenFiles) + throws IOException { ArrayList smbFileList = new ArrayList<>(); String extraParams = Uri.parse(path).getQuery(); if (mainFragmentViewModel.getSearchHelper().size() > 500) { mainFragmentViewModel.getSearchHelper().clear(); } - for (SmbFile aMFile : mFile) { + for (AmazeFile aMFile : mFile) { + if (!aMFile.isDirectory() && !aMFile.isFile()) { + continue; + } if ((DataUtils.getInstance().isFileHidden(aMFile.getPath()) || aMFile.isHidden()) && !showHiddenFiles) { continue; @@ -1492,7 +1495,7 @@ public void run() { } */ - s.setStreamSrc(baseFile.getSmbFile(), baseFile.getSize()); + s.setStreamSrc(new AmazeFile(baseFile.getPath()), baseFile.getSize()); activity.runOnUiThread( () -> { try { diff --git a/app/src/main/java/com/amaze/filemanager/utils/SmbUtil.java b/app/src/main/java/com/amaze/filemanager/utils/SmbUtil.java index b50d0e0806..389f46594b 100644 --- a/app/src/main/java/com/amaze/filemanager/utils/SmbUtil.java +++ b/app/src/main/java/com/amaze/filemanager/utils/SmbUtil.java @@ -102,6 +102,8 @@ public static String getSmbEncryptedPath(Context context, String path) return buffer.toString(); } + /** Use AmazeFile(path) instead */ + @Deprecated public static SmbFile create(String path) throws MalformedURLException { Uri uri = Uri.parse(path); boolean disableIpcSigningCheck = @@ -122,7 +124,9 @@ public static SmbFile create(String path) throws MalformedURLException { * @param userInfo authentication string, must be already URL decoded. {@link Uri} shall do this * for you already * @return {@link NtlmPasswordAuthenticator} instance + *

Use AmazeFile(path) instead */ + @Deprecated protected static @NonNull NtlmPasswordAuthenticator createFrom(@Nullable String userInfo) { if (!TextUtils.isEmpty(userInfo)) { String dom = null; diff --git a/build.gradle b/build.gradle index 3cc388969f..763e164bdb 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,8 @@ buildscript { commonsCompressVersion = "1.20" libsuVersion = "3.2.1" mockkVersion = "1.12.2" + reactiveXVersion = "2.2.9" + reactiveXAndroidVersion = "2.1.1" } repositories { google() diff --git a/file_operations/build.gradle b/file_operations/build.gradle index e5e84f5039..306201e797 100644 --- a/file_operations/build.gradle +++ b/file_operations/build.gradle @@ -78,6 +78,13 @@ dependencies { implementation 'androidx.multidex:multidex:2.0.1'//Multiple dex files + //ReactiveX + implementation "io.reactivex.rxjava2:rxandroid:$reactiveXAndroidVersion" + // Because RxAndroid releases are few and far between, it is recommended you also + // explicitly depend on RxJava's latest version for bug fixes and new features. + // (see https://github.com/ReactiveX/RxJava/releases for latest 3.x.x version) + implementation "io.reactivex.rxjava2:rxjava:$reactiveXVersion" + //TODO some libs are not needed //For tests diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/CifsContexts.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/CifsContexts.kt new file mode 100644 index 0000000000..b4b36375eb --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/CifsContexts.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.smb + +import android.net.Uri +import android.text.TextUtils +import android.util.Log +import io.reactivex.Single +import io.reactivex.schedulers.Schedulers +import jcifs.CIFSException +import jcifs.config.PropertyConfiguration +import jcifs.context.BaseContext +import jcifs.context.SingletonContext +import java.util.* +import java.util.concurrent.ConcurrentHashMap + +object CifsContexts { + + const val SMB_URI_PREFIX = "smb://" + + private val TAG = CifsContexts::class.java.simpleName + + private val defaultProperties: Properties = Properties().apply { + setProperty("jcifs.resolveOrder", "BCAST") + setProperty("jcifs.smb.client.responseTimeout", "30000") + setProperty("jcifs.netbios.retryTimeout", "5000") + setProperty("jcifs.netbios.cachePolicy", "-1") + } + + private val contexts: MutableMap = ConcurrentHashMap() + + @JvmStatic + fun clearBaseContexts() { + contexts.forEach { + try { + it.value.close() + } catch (e: CIFSException) { + Log.w(TAG, "Error closing SMB connection", e) + } + } + contexts.clear() + } + + @JvmStatic + fun createWithDisableIpcSigningCheck( + basePath: String, + disableIpcSigningCheck: Boolean + ): BaseContext { + return if (disableIpcSigningCheck) { + val extraProperties = Properties() + extraProperties["jcifs.smb.client.ipcSigningEnforced"] = "false" + create(basePath, extraProperties) + } else { + create(basePath, null) + } + } + + @JvmStatic + fun create(basePath: String, extraProperties: Properties?): BaseContext { + val basePathKey: String = Uri.parse(basePath).run { + val prefix = "$scheme://$authority" + val suffix = if (TextUtils.isEmpty(query)) "" else "?$query" + "$prefix$suffix" + } + return if (contexts.containsKey(basePathKey)) { + contexts.getValue(basePathKey) + } else { + val context = Single.fromCallable { + try { + val p = Properties(defaultProperties) + if (extraProperties != null) p.putAll(extraProperties) + BaseContext(PropertyConfiguration(p)) + } catch (e: CIFSException) { + Log.e(TAG, "Error initialize jcifs BaseContext, returning default", e) + SingletonContext.getInstance() + } + }.subscribeOn(Schedulers.io()) + .blockingGet() + contexts[basePathKey] = context + context + } + } +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java new file mode 100644 index 0000000000..690bf91c66 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -0,0 +1,394 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.smb; + +import static com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.SMB_URI_PREFIX; +import static com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.clearBaseContexts; + +import android.net.Uri; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jcifs.SmbConstants; +import jcifs.smb.NtlmPasswordAuthenticator; +import jcifs.smb.SmbException; +import jcifs.smb.SmbFile; +import kotlin.NotImplementedError; + +/** + * Root is + * "smb://:@" + * or "smb://" + * or "smb://:@/?disableIpcSigningCheck=true" + * or "smb:///?disableIpcSigningCheck=true" + * Relative paths are not supported + */ +public class SmbAmazeFileSystem extends AmazeFileSystem { + public static final String TAG = SmbAmazeFileSystem.class.getSimpleName(); + + public static final String PREFIX = SMB_URI_PREFIX; + public static final char SEPARATOR = '/'; + public static final String PARAM_DISABLE_IPC_SIGNING_CHECK = "disableIpcSigningCheck"; + + private static final Pattern IPv4_PATTERN = Pattern.compile("[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+"); + private static final Pattern METADATA_PATTERN = Pattern.compile("([?][a-zA-Z]+=(\")?[a-zA-Z]+(\")?)+"); + + private SmbAmazeFileSystem() { } + + public static final SmbAmazeFileSystem INSTANCE = new SmbAmazeFileSystem(); + + @Override + public boolean isPathOfThisFilesystem(String path) { + return path.startsWith(SmbAmazeFileSystem.PREFIX); + } + + @Override + public char getSeparator() { + return SEPARATOR; + } + + @Override + public char getPathSeparator() { + return 0; + } + + @Override + public String normalize(String pathname) { + try { + String canonical = canonicalize(pathname); + return canonical.substring(0, canonical.length()-1); + } catch (MalformedURLException e) { + Log.e(TAG, "Error getting canonical path for SMB file", e); + return null; + } + } + + @Override + public int prefixLength(String path) { + if (path.length() == 0) { + return 0; + } + + Matcher matcherMetadata = METADATA_PATTERN.matcher(path); + if(matcherMetadata.find()) { + return matcherMetadata.end(); + } + + Matcher matcher = IPv4_PATTERN.matcher(path); + matcher.find(); + return matcher.end(); + } + + @Override + public String resolve(String parent, String child) { + final String prefix = parent.substring(0, prefixLength(parent)); + final String simplePathParent = parent.substring(prefixLength(parent)); + final String simplePathChild = child.substring(prefixLength(child)); + + return prefix + basicUnixResolve(simplePathParent, simplePathChild); + } + + /** + * This makes no sense for SMB + */ + @Override + public String getDefaultParent() { + throw new IllegalStateException("There is no default SMB path"); + } + + @Override + public String fromURIPath(String path) { + throw new NotImplementedError(); + } + + @Override + public boolean isAbsolute(AmazeFile f) { + return f.getPath().startsWith(PREFIX); + } + + @Override + public String resolve(AmazeFile f) { + if (isAbsolute(f)) { + return f.getPath(); + } + + throw new IllegalArgumentException("Relative paths are not supported"); + } + + @Override + public String canonicalize(String path) throws MalformedURLException { + return create(path).getCanonicalPath(); + } + + @Override + public int getBooleanAttributes(AmazeFile f) { + try { + SmbFile smbFile = create(f.getPath()); + int r = 0; + + if (smbFile.exists()) { + r |= BA_EXISTS; + + if (smbFile.getType() == SmbConstants.TYPE_FILESYSTEM) { + r |= BA_REGULAR; + } + + if (smbFile.isDirectory()) { + r |= BA_DIRECTORY; + } + + if (smbFile.isHidden()) { + r |= BA_HIDDEN; + } + } + + return r; + } catch (MalformedURLException | SmbException e) { + Log.e(TAG, "Failed to get attributes for SMB file", e); + return 0; + } + } + + @Override + public boolean checkAccess(AmazeFile f, int access) { + try { + switch (access) { + case ACCESS_EXECUTE: + throw new NotImplementedError(); + case ACCESS_WRITE: + return create(f.getPath()).canWrite(); + case ACCESS_READ: + return create(f.getPath()).canRead(); + case ACCESS_CHECK_EXISTS: + SmbFile file = create(f.getPath()); + file.setConnectTimeout(2000); + file.exists(); + default: + throw new IllegalStateException(); + } + } catch (MalformedURLException | SmbException e) { + Log.e(TAG, "Error getting SMB file to check access", e); + return false; + } + } + + @Override + public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { + throw new NotImplementedError(); + } + + @Override + public long getLastModifiedTime(AmazeFile f) { + try { + return create(f.getPath()).getLastModified(); + } catch (MalformedURLException e) { + Log.e(TAG, "Error getting SMB file to get last modified time", e); + return 0; + } + } + + @Override + public long getLength(AmazeFile f) throws SmbException, MalformedURLException { + return create(f.getPath()).length(); + } + + private static SmbFile create(String path) throws MalformedURLException { + if(!path.endsWith(SEPARATOR + "")) { + path = path + SEPARATOR; + } + Uri uri = Uri.parse(path); + boolean disableIpcSigningCheck = + Boolean.parseBoolean(uri.getQueryParameter(PARAM_DISABLE_IPC_SIGNING_CHECK)); + String userInfo = uri.getUserInfo(); + return new SmbFile( + path.indexOf('?') < 0 ? path : path.substring(0, path.indexOf('?')), + CifsContexts.createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) + .withCredentials(createFrom(userInfo))); + } + + /** + * Create {@link NtlmPasswordAuthenticator} from given userInfo parameter. + * + *

Logic borrowed directly from jcifs-ng's own code. They should make that protected + * constructor public... + * + * @param userInfo authentication string, must be already URL decoded. {@link Uri} shall do this + * for you already + * @return {@link NtlmPasswordAuthenticator} instance + */ + private static @NonNull + NtlmPasswordAuthenticator createFrom(@Nullable String userInfo) { + if (!TextUtils.isEmpty(userInfo)) { + String dom = null; + String user = null; + String pass = null; + int i; + int u; + int end = userInfo.length(); + for (i = 0, u = 0; i < end; i++) { + char c = userInfo.charAt(i); + if (c == ';') { + dom = userInfo.substring(0, i); + u = i + 1; + } else if (c == ':') { + pass = userInfo.substring(i + 1); + break; + } + } + user = userInfo.substring(u, i); + return new NtlmPasswordAuthenticator(dom, user, pass); + } else { + return new NtlmPasswordAuthenticator(); + } + } + + @Override + public boolean createFileExclusively(String pathname) throws IOException { + create(pathname).mkdirs(); + return true; + } + + @Override + public boolean delete(AmazeFile f) { + try { + create(f.getPath()).delete(); + return true; + } catch (SmbException | MalformedURLException e) { + Log.e(TAG, "Error deleting SMB file", e); + return false; + } + } + + @Override + public String[] list(AmazeFile f) { + String[] list; + try { + list = create(f.getPath()).list(); + } catch (SmbException | MalformedURLException e) { + Log.e(TAG, "Error listing SMB files", e); + return null; + } + final String prefix = f.getPath().substring(0, prefixLength(f.getPath())); + + for (int i = 0; i < list.length; i++) { + list[i] = SmbAmazeFileSystem.INSTANCE.normalize(prefix + getSeparator() + list[i]); + } + + return list; + } + + @Override + public InputStream getInputStream(AmazeFile f) { + try { + return create(f.getPath()).getInputStream(); + } catch (IOException e) { + Log.e(TAG, "Error creating SMB output stream", e); + return null; + } + } + + @Override + public OutputStream getOutputStream(AmazeFile f) { + try { + return create(f.getPath()).getOutputStream(); + } catch (IOException e) { + Log.e(TAG, "Error creating SMB output stream", e); + return null; + } + } + + @Override + public boolean createDirectory(AmazeFile f) { + try { + create(f.getPath()).mkdir(); + return true; + } catch (SmbException | MalformedURLException e) { + Log.e(TAG, "Error creating SMB directory", e); + return false; + } + } + + @Override + public boolean rename(AmazeFile f1, AmazeFile f2) { + try { + create(f1.getPath()).renameTo(create(f2.getPath())); + return true; + } catch (SmbException | MalformedURLException e) { + Log.e(TAG, "Error getting SMB files for a rename", e); + return false; + } + } + + @Override + public boolean setLastModifiedTime(AmazeFile f, long time) { + try { + create(f.getPath()).setLastModified(time); + return true; + } catch (SmbException | MalformedURLException e) { + Log.e(TAG, "Error getting SMB file to set modified time", e); + return false; + } + } + + @Override + public boolean setReadOnly(AmazeFile f) { + try { + create(f.getPath()).setReadOnly(); + return true; + } catch (SmbException | MalformedURLException e) { + Log.e(TAG, "Error getting SMB file to set read only", e); + return false; + } + } + + @Override + public AmazeFile[] listRoots() { + throw new NotImplementedError(); + } + + @Override + public long getSpace(AmazeFile f, int t) { + switch (t) { + case SPACE_TOTAL: + // TODO: Find total storage space of SMB when JCIFS adds support + throw new NotImplementedError(); + case SPACE_FREE: + try { + return create(f.getPath()).getDiskFreeSpace(); + } catch (SmbException | MalformedURLException e) { + Log.e(TAG, "Error getting SMB file to read free volume space", e); + } + case SPACE_USABLE: + // TODO: Find total storage space of SMB when JCIFS adds support + throw new NotImplementedError(); + default: + throw new IllegalStateException(); + } + } + + @Override + public int compare(AmazeFile f1, AmazeFile f2) { + return f1.getPath().compareTo(f2.getPath()); + } + + @Override + public int hashCode(AmazeFile f) { + return basicUnixHashCode(f.getPath()); + } + + @Override + public void close() throws IOException { + clearBaseContexts(); + } +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java index 37d9c6492b..79e602f336 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.InputStream; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.streams.RandomAccessStream; import android.webkit.MimeTypeMap; @@ -35,10 +36,10 @@ public class StreamSource extends RandomAccessStream { protected String mime; protected long fp; protected String name; - protected SmbFile file; + protected AmazeFile file; InputStream input; - public StreamSource(SmbFile file, long l) { + public StreamSource(AmazeFile file, long l) { super(l); fp = 0; @@ -114,7 +115,7 @@ public String getName() { return name; } - public SmbFile getFile() { + public AmazeFile getFile() { return file; } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/Streamer.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/Streamer.java index 3166ca3937..b57a309486 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/Streamer.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/Streamer.java @@ -27,6 +27,8 @@ import android.util.Log; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; + import jcifs.smb.SmbFile; /** Created by Arpit on 06-07-2015. */ @@ -34,7 +36,7 @@ public class Streamer extends StreamServer { public static final int PORT = 7871; public static final String URL = "http://127.0.0.1:" + PORT; - private SmbFile file; + private AmazeFile file; long length = 0; // protected List extras; //those can be subtitles // private InputStream stream; @@ -65,7 +67,7 @@ public static boolean isStreamMedia(SmbFile file) { return pattern.matcher(file.getName()).matches(); } - public void setStreamSrc(SmbFile file, long len) { + public void setStreamSrc(AmazeFile file, long len) { this.file = file; // this.extras = extraFiles; this.length = len; @@ -81,7 +83,7 @@ public void stop() { public Response serve( String uri, String method, Properties header, Properties parms, Properties files) { Response res; - SmbFile sourceFile = null; + AmazeFile sourceFile = null; String name = getNameFromPath(uri); if (file != null && file.getName().equals(name)) sourceFile = file; /*else if(extras!=null){ diff --git a/file_operations/src/test/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSourceTest.java b/file_operations/src/test/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSourceTest.java index 0ec6d4027d..203805614f 100644 --- a/file_operations/src/test/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSourceTest.java +++ b/file_operations/src/test/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSourceTest.java @@ -39,6 +39,7 @@ import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.shadows.ShadowMultiDex; import com.amaze.filemanager.file_operations.shadows.jcifs.smb.ShadowSmbFile; @@ -54,7 +55,7 @@ shadows = {ShadowMultiDex.class, ShadowSmbFile.class}, sdk = {JELLY_BEAN, KITKAT, P}) public class StreamSourceTest { - private SmbFile file; + private AmazeFile file; private StreamSource ss; private byte[] text; @@ -73,7 +74,7 @@ public void tearDown() { if (ss != null) ss.close(); } - private SmbFile createFile() throws IOException { + private AmazeFile createFile() throws IOException { File testFile = new File(Environment.getExternalStorageDirectory(), "Test.txt"); testFile.createNewFile(); @@ -82,7 +83,7 @@ private SmbFile createFile() throws IOException { is.flush(); is.close(); - SmbFile file = new SmbFile("smb://127.0.0.1/Test.txt"); + AmazeFile file = new AmazeFile("smb://127.0.0.1/Test.txt"); ShadowSmbFile shadowSmbFile = Shadow.extract(file); shadowSmbFile.setFile(testFile); From a373c5ec8e3cd8246b3071a7bcdac1f4d5f4c861 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Mon, 11 Oct 2021 12:24:54 -0300 Subject: [PATCH 03/46] Apply spotless and add spotless for file_operations module --- .../filesystem/MakeDirectoryOperation.kt | 1 - .../filesystem/smb/CifsContexts.kt | 6 +- .../filetypes/AmazeDeleteOnExitHook.java | 54 +- .../filesystem/filetypes/AmazeFile.java | 1740 +++++++---------- .../filesystem/filetypes/AmazeFileFilter.java | 45 +- .../filesystem/filetypes/AmazeFileSystem.java | 188 +- .../filetypes/AmazeFilenameFilter.java | 57 +- .../filetypes/smb/SmbAmazeFileSystem.java | 78 +- .../filesystem/smbstreamer/StreamSource.java | 2 - .../filesystem/smbstreamer/Streamer.java | 4 +- .../smbstreamer/StreamSourceTest.java | 2 - 11 files changed, 917 insertions(+), 1260 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt b/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt index 272e639de6..7601ff4b11 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt @@ -25,7 +25,6 @@ import android.os.Build import com.amaze.filemanager.file_operations.filesystem.OpenMode import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.utils.OTGUtil -import jcifs.smb.SmbException import java.io.File import java.io.IOException diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/smb/CifsContexts.kt b/app/src/main/java/com/amaze/filemanager/filesystem/smb/CifsContexts.kt index 4062815447..7ece3dbdbc 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/smb/CifsContexts.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/smb/CifsContexts.kt @@ -32,7 +32,11 @@ import jcifs.context.SingletonContext import java.util.* import java.util.concurrent.ConcurrentHashMap -@Deprecated("Replaced with [com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts]") +@Deprecated( + "" + + "Replaced with " + + "[com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts]" +) object CifsContexts { const val SMB_URI_PREFIX = "smb://" diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java index 6bd5dd72d8..924f1e1d31 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java @@ -1,26 +1,21 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (C) 2014-2010 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * This file is part of Amaze File Manager. * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Amaze File Manager 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. * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * 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. * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ package com.amaze.filemanager.file_operations.filesystem.filetypes; @@ -31,27 +26,28 @@ import java.util.LinkedHashSet; /** - * This class holds a set of filenames to be deleted on VM exit through a shutdown hook. - * A set is used both to prevent double-insertion of the same file as well as offer - * quick removal. + * This class holds a set of filenames to be deleted on VM exit through a shutdown hook. A set is + * used both to prevent double-insertion of the same file as well as offer quick removal. */ - public class AmazeDeleteOnExitHook { private static LinkedHashSet files = new LinkedHashSet<>(); + static { // BEGIN Android-changed: Use Runtime.addShutdownHook() rather than SharedSecrets. - Runtime.getRuntime().addShutdownHook(new Thread() { - public void run() { - runHooks(); - } - }); + Runtime.getRuntime() + .addShutdownHook( + new Thread() { + public void run() { + runHooks(); + } + }); // END Android-changed: Use Runtime.addShutdownHook() rather than SharedSecrets. } private AmazeDeleteOnExitHook() {} static synchronized void add(String file) { - if(files == null) { + if (files == null) { // DeleteOnExitHook is running. Too late to add a file throw new IllegalStateException("Shutdown in progress"); } @@ -76,4 +72,4 @@ static void runHooks() { (new File(filename)).delete(); } } -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index c3f8b26600..3fa2f9e7f3 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -1,53 +1,44 @@ /* - * Copyright (C) 2014 The Android Open Source Project - * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (C) 2014-2014 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * This file is part of Amaze File Manager. * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Amaze File Manager 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. * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * 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. * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ package com.amaze.filemanager.file_operations.filesystem.filetypes; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -import androidx.annotation.NonNull; - -import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; - import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.Serializable; -import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import androidx.annotation.NonNull; + import kotlin.Deprecated; import kotlin.NotImplementedError; import kotlin.random.Random; @@ -56,197 +47,162 @@ /** * An abstract representation of file and directory pathnames. * - *

User interfaces and operating systems use system-dependent pathname - * strings to name files and directories. This class presents an - * abstract, system-independent view of hierarchical pathnames. An - * abstract pathname has two components: + *

User interfaces and operating systems use system-dependent pathname strings to name + * files and directories. This class presents an abstract, system-independent view of hierarchical + * pathnames. An abstract pathname has two components: * *

    - *
  1. An optional system-dependent prefix string, - * such as a disk-drive specifier, "/" for the UNIX root - * directory, or "\\\\" for a Microsoft Windows UNC pathname, and - *
  2. A sequence of zero or more string names. + *
  3. An optional system-dependent prefix string, such as a disk-drive specifier, + * "/" for the UNIX root directory, or "\\\\" for a Microsoft + * Windows UNC pathname, and + *
  4. A sequence of zero or more string names. *
* - * The first name in an abstract pathname may be a directory name or, in the - * case of Microsoft Windows UNC pathnames, a hostname. Each subsequent name - * in an abstract pathname denotes a directory; the last name may denote - * either a directory or a file. The empty abstract pathname has no - * prefix and an empty name sequence. + * The first name in an abstract pathname may be a directory name or, in the case of Microsoft + * Windows UNC pathnames, a hostname. Each subsequent name in an abstract pathname denotes a + * directory; the last name may denote either a directory or a file. The empty abstract + * pathname has no prefix and an empty name sequence. * - *

The conversion of a pathname string to or from an abstract pathname is - * inherently system-dependent. When an abstract pathname is converted into a - * pathname string, each name is separated from the next by a single copy of - * the default separator character. The default name-separator - * character is defined by the system property file.separator, and - * is made available in the public static fields {@link - * #separator} and {@link #separatorChar} of this class. - * When a pathname string is converted into an abstract pathname, the names - * within it may be separated by the default name-separator character or by any - * other name-separator character that is supported by the underlying system. + *

The conversion of a pathname string to or from an abstract pathname is inherently + * system-dependent. When an abstract pathname is converted into a pathname string, each name is + * separated from the next by a single copy of the default separator character. The default + * name-separator character is defined by the system property file.separator, and is + * made available in the public static fields {@link + * #separator} and {@link #separatorChar} of this class. When a pathname string + * is converted into an abstract pathname, the names within it may be separated by the default + * name-separator character or by any other name-separator character that is supported by the + * underlying system. * - *

A pathname, whether abstract or in string form, may be either - * absolute or relative. An absolute pathname is complete in - * that no other information is required in order to locate the file that it - * denotes. A relative pathname, in contrast, must be interpreted in terms of - * information taken from some other pathname. By default the classes in the - * java.io package always resolve relative pathnames against the - * current user directory. This directory is named by the system property - * user.dir, and is typically the directory in which the Java - * virtual machine was invoked. + *

A pathname, whether abstract or in string form, may be either absolute or + * relative. An absolute pathname is complete in that no other information is required in + * order to locate the file that it denotes. A relative pathname, in contrast, must be interpreted + * in terms of information taken from some other pathname. By default the classes in the + * java.io package always resolve relative pathnames against the current user directory. This + * directory is named by the system property user.dir, and is typically the directory + * in which the Java virtual machine was invoked. * - *

The parent of an abstract pathname may be obtained by invoking - * the {@link #getParent} method of this class and consists of the pathname's - * prefix and each name in the pathname's name sequence except for the last. - * Each directory's absolute pathname is an ancestor of any AmazeFile - * object with an absolute abstract pathname which begins with the directory's - * absolute pathname. For example, the directory denoted by the abstract - * pathname "/usr" is an ancestor of the directory denoted by the - * pathname "/usr/local/bin". + *

The parent of an abstract pathname may be obtained by invoking the {@link #getParent} + * method of this class and consists of the pathname's prefix and each name in the pathname's name + * sequence except for the last. Each directory's absolute pathname is an ancestor of any + * AmazeFile object with an absolute abstract pathname which begins with the directory's + * absolute pathname. For example, the directory denoted by the abstract pathname "/usr" is + * an ancestor of the directory denoted by the pathname "/usr/local/bin". * - *

The prefix concept is used to handle root directories on UNIX platforms, - * and drive specifiers, root directories and UNC pathnames on Microsoft Windows platforms, - * as follows: + *

The prefix concept is used to handle root directories on UNIX platforms, and drive specifiers, + * root directories and UNC pathnames on Microsoft Windows platforms, as follows: * *

    - * - *
  • For UNIX platforms, the prefix of an absolute pathname is always - * "/". Relative pathnames have no prefix. The abstract pathname - * denoting the root directory has the prefix "/" and an empty - * name sequence. - * - *
  • For Microsoft Windows platforms, the prefix of a pathname that contains a drive - * specifier consists of the drive letter followed by ":" and - * possibly followed by "\\" if the pathname is absolute. The - * prefix of a UNC pathname is "\\\\"; the hostname and the share - * name are the first two names in the name sequence. A relative pathname that - * does not specify a drive has no prefix. - * + *
  • For UNIX platforms, the prefix of an absolute pathname is always "/". Relative + * pathnames have no prefix. The abstract pathname denoting the root directory has the prefix + * "/" and an empty name sequence. + *
  • For Microsoft Windows platforms, the prefix of a pathname that contains a drive specifier + * consists of the drive letter followed by ":" and possibly followed by + * "\\" if the pathname is absolute. The prefix of a UNC pathname is "\\\\" + * ; the hostname and the share name are the first two names in the name sequence. A + * relative pathname that does not specify a drive has no prefix. *
* - *

Instances of this class may or may not denote an actual file-system - * object such as a file or a directory. If it does denote such an object - * then that object resides in a partition. A partition is an - * operating system-specific portion of storage for a file system. A single - * storage device (e.g. a physical disk-drive, flash memory, CD-ROM) may - * contain multiple partitions. The object, if any, will reside on the - * partition named by some ancestor of the absolute - * form of this pathname. + *

Instances of this class may or may not denote an actual file-system object such as a file or a + * directory. If it does denote such an object then that object resides in a partition. A + * partition is an operating system-specific portion of storage for a file system. A single storage + * device (e.g. a physical disk-drive, flash memory, CD-ROM) may contain multiple partitions. The + * object, if any, will reside on the partition named by some ancestor of the + * absolute form of this pathname. * - *

A file system may implement restrictions to certain operations on the - * actual file-system object, such as reading, writing, and executing. These - * restrictions are collectively known as access permissions. The file - * system may have multiple sets of access permissions on a single object. - * For example, one set may apply to the object's owner, and another - * may apply to all other users. The access permissions on an object may - * cause some methods in this class to fail. + *

A file system may implement restrictions to certain operations on the actual file-system + * object, such as reading, writing, and executing. These restrictions are collectively known as + * access permissions. The file system may have multiple sets of access permissions on a + * single object. For example, one set may apply to the object's owner, and another may apply + * to all other users. The access permissions on an object may cause some methods in this class to + * fail. * - *

On Android strings are converted to UTF-8 byte sequences when sending filenames to - * the operating system, and byte sequences returned by the operating system (from the - * various {@code list} methods) are converted to strings by decoding them as UTF-8 - * byte sequences. + *

On Android strings are converted to UTF-8 byte sequences when sending filenames to the + * operating system, and byte sequences returned by the operating system (from the various {@code + * list} methods) are converted to strings by decoding them as UTF-8 byte sequences. * - * @author unascribed - * @since JDK1.0 + * @author unascribed + * @since JDK1.0 */ public class AmazeFile implements Parcelable, Comparable { public static final String TAG = AmazeFile.class.getSimpleName(); - /** - * The FileSystem object representing the platform's local file system. - */ + /** The FileSystem object representing the platform's local file system. */ private AmazeFileSystem fs; /** - * This abstract pathname's normalized pathname string. A normalized - * pathname string uses the default name-separator character and does not - * contain any duplicate or redundant separators. + * This abstract pathname's normalized pathname string. A normalized pathname string uses the + * default name-separator character and does not contain any duplicate or redundant separators. */ private final String path; - /** - * Enum type that indicates the status of a file path. - */ - private static enum PathStatus { INVALID, CHECKED }; + /** Enum type that indicates the status of a file path. */ + private static enum PathStatus { + INVALID, + CHECKED + }; - /** - * The flag indicating whether the file path is invalid. - */ + /** The flag indicating whether the file path is invalid. */ private transient PathStatus status = null; /** - * Check if the file has an invalid path. Currently, the inspection of - * a file path is very limited, and it only covers Nul character check. - * Returning true means the path is definitely invalid/garbage. But - * returning false does not guarantee that the path is valid. + * Check if the file has an invalid path. Currently, the inspection of a file path is very + * limited, and it only covers Nul character check. Returning true means the path is definitely + * invalid/garbage. But returning false does not guarantee that the path is valid. * * @return true if the file path is invalid. */ final boolean isInvalid() { if (status == null) { - status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED - : PathStatus.INVALID; + status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED : PathStatus.INVALID; } return status == PathStatus.INVALID; } - /** - * The length of this abstract pathname's prefix, or zero if it has no - * prefix. - */ + /** The length of this abstract pathname's prefix, or zero if it has no prefix. */ private final transient int prefixLength; - /** - * Returns the length of this abstract pathname's prefix. - * For use by FileSystem classes. - */ + /** Returns the length of this abstract pathname's prefix. For use by FileSystem classes. */ int getPrefixLength() { return prefixLength; } /** - * The system-dependent default name-separator character. This field is - * initialized to contain the first character of the value of the system - * property file.separator. On UNIX systems the value of this - * field is '/'; on Microsoft Windows systems it is '\\'. + * The system-dependent default name-separator character. This field is initialized to contain the + * first character of the value of the system property file.separator. On UNIX + * systems the value of this field is '/'; on Microsoft Windows systems it is + * '\\'. * - * @see java.lang.System#getProperty(java.lang.String) + * @see java.lang.System#getProperty(java.lang.String) */ public final char separatorChar; /** - * The system-dependent default name-separator character, represented as a - * string for convenience. This string contains a single character, namely - * {@link #separatorChar}. + * The system-dependent default name-separator character, represented as a string for convenience. + * This string contains a single character, namely {@link #separatorChar}. */ public final String separator; /** - * The system-dependent path-separator character. This field is - * initialized to contain the first character of the value of the system - * property path.separator. This character is used to - * separate filenames in a sequence of files given as a path list. - * On UNIX systems, this character is ':'; on Microsoft Windows systems it - * is ';'. - * - * @see java.lang.System#getProperty(java.lang.String) + * The system-dependent path-separator character. This field is initialized to contain the first + * character of the value of the system property path.separator. This character is + * used to separate filenames in a sequence of files given as a path list. On UNIX + * systems, this character is ':'; on Microsoft Windows systems it is ';' + * . + * + * @see java.lang.System#getProperty(java.lang.String) */ public final char pathSeparatorChar; /** - * The system-dependent path-separator character, represented as a string - * for convenience. This string contains a single character, namely - * {@link #pathSeparatorChar}. + * The system-dependent path-separator character, represented as a string for convenience. This + * string contains a single character, namely {@link #pathSeparatorChar}. */ public final String pathSeparator; - /* -- Constructors -- */ - /** - * Internal constructor for already-normalized pathname strings. - */ + /** Internal constructor for already-normalized pathname strings. */ private AmazeFile(String pathname, int prefixLength) { loadFilesystem(pathname); separatorChar = fs.getSeparator(); @@ -258,9 +214,8 @@ private AmazeFile(String pathname, int prefixLength) { } /** - * Internal constructor for already-normalized pathname strings. - * The parameter order is used to disambiguate this method from the - * public(AmazeFile, String) constructor. + * Internal constructor for already-normalized pathname strings. The parameter order is used to + * disambiguate this method from the public(AmazeFile, String) constructor. */ private AmazeFile(String child, AmazeFile parent) { assert parent.path != null; @@ -275,13 +230,12 @@ private AmazeFile(String child, AmazeFile parent) { } /** - * Creates a new AmazeFile instance by converting the given - * pathname string into an abstract pathname. If the given string is - * the empty string, then the result is the empty abstract pathname. + * Creates a new AmazeFile instance by converting the given pathname string into an + * abstract pathname. If the given string is the empty string, then the result is the empty + * abstract pathname. * - * @param pathname A pathname string - * @throws NullPointerException - * If the pathname argument is null + * @param pathname A pathname string + * @throws NullPointerException If the pathname argument is null */ public AmazeFile(String pathname) { if (pathname == null) { @@ -296,37 +250,33 @@ public AmazeFile(String pathname) { this.prefixLength = fs.prefixLength(this.path); } - /* Note: The two-argument File constructors do not interpret an empty - parent abstract pathname as the current user directory. An empty parent - instead causes the child to be resolved against the system-dependent - directory defined by the FileSystem.getDefaultParent method. On Unix - this default is "/", while on Microsoft Windows it is "\\". This is required for - compatibility with the original behavior of this class. */ + /* Note: The two-argument File constructors do not interpret an empty + parent abstract pathname as the current user directory. An empty parent + instead causes the child to be resolved against the system-dependent + directory defined by the FileSystem.getDefaultParent method. On Unix + this default is "/", while on Microsoft Windows it is "\\". This is required for + compatibility with the original behavior of this class. */ /** - * Creates a new AmazeFile instance from a parent pathname string - * and a child pathname string. + * Creates a new AmazeFile instance from a parent pathname string and a child + * pathname string. * - *

If parent is null then the new - * AmazeFile instance is created as if by invoking the - * single-argument AmazeFile constructor on the given + *

If parent is null then the new AmazeFile instance is + * created as if by invoking the single-argument AmazeFile constructor on the given * child pathname string. * - *

Otherwise the parent pathname string is taken to denote - * a directory, and the child pathname string is taken to - * denote either a directory or a file. If the child pathname - * string is absolute then it is converted into a relative pathname in a - * system-dependent way. If parent is the empty string then - * the new AmazeFile instance is created by converting - * child into an abstract pathname and resolving the result - * against a system-dependent default directory. Otherwise each pathname - * string is converted into an abstract pathname and the child abstract - * pathname is resolved against the parent. - * - * @param parent The parent pathname string - * @param child The child pathname string - * @throws NullPointerException - * If child is null + *

Otherwise the parent pathname string is taken to denote a directory, and the + * child pathname string is taken to denote either a directory or a file. If the + * child pathname string is absolute then it is converted into a relative pathname in + * a system-dependent way. If parent is the empty string then the new AmazeFile + * instance is created by converting child into an abstract pathname and + * resolving the result against a system-dependent default directory. Otherwise each pathname + * string is converted into an abstract pathname and the child abstract pathname is resolved + * against the parent. + * + * @param parent The parent pathname string + * @param child The child pathname string + * @throws NullPointerException If child is null */ public AmazeFile(String parent, String child) { if (child == null) { @@ -339,8 +289,7 @@ public AmazeFile(String parent, String child) { separator = "" + separatorChar; pathSeparatorChar = fs.getPathSeparator(); pathSeparator = "" + pathSeparatorChar; - this.path = fs.resolve(fs.normalize(parent), - fs.normalize(child)); + this.path = fs.resolve(fs.normalize(parent), fs.normalize(child)); // END Android-changed: b/25859957, app-compat; don't substitute empty parent. } else { loadFilesystem(child); @@ -354,29 +303,25 @@ public AmazeFile(String parent, String child) { } /** - * Creates a new AmazeFile instance from a parent abstract - * pathname and a child pathname string. + * Creates a new AmazeFile instance from a parent abstract pathname and a child + * pathname string. * - *

If parent is null then the new - * AmazeFile instance is created as if by invoking the - * single-argument AmazeFile constructor on the given + *

If parent is null then the new AmazeFile instance is + * created as if by invoking the single-argument AmazeFile constructor on the given * child pathname string. * - *

Otherwise the parent abstract pathname is taken to - * denote a directory, and the child pathname string is taken - * to denote either a directory or a file. If the child - * pathname string is absolute then it is converted into a relative - * pathname in a system-dependent way. If parent is the empty - * abstract pathname then the new AmazeFile instance is created by - * converting child into an abstract pathname and resolving - * the result against a system-dependent default directory. Otherwise each - * pathname string is converted into an abstract pathname and the child - * abstract pathname is resolved against the parent. - * - * @param parent The parent abstract pathname - * @param child The child pathname string - * @throws NullPointerException - * If child is null + *

Otherwise the parent abstract pathname is taken to denote a directory, and the + * child pathname string is taken to denote either a directory or a file. If the + * child pathname string is absolute then it is converted into a relative pathname in + * a system-dependent way. If parent is the empty abstract pathname then the new + * AmazeFile instance is created by converting child into an abstract + * pathname and resolving the result against a system-dependent default directory. Otherwise each + * pathname string is converted into an abstract pathname and the child abstract pathname is + * resolved against the parent. + * + * @param parent The parent abstract pathname + * @param child The child pathname string + * @throws NullPointerException If child is null */ public AmazeFile(AmazeFile parent, String child) { if (child == null) { @@ -390,11 +335,9 @@ public AmazeFile(AmazeFile parent, String child) { pathSeparator = "" + pathSeparatorChar; if (parent.path.equals("")) { - this.path = fs.resolve(fs.getDefaultParent(), - fs.normalize(child)); + this.path = fs.resolve(fs.getDefaultParent(), fs.normalize(child)); } else { - this.path = fs.resolve(parent.path, - fs.normalize(child)); + this.path = fs.resolve(parent.path, fs.normalize(child)); } } else { loadFilesystem(child); @@ -408,37 +351,31 @@ public AmazeFile(AmazeFile parent, String child) { } /** - * Creates a new AmazeFile instance by converting the given - * file: URI into an abstract pathname. - * - *

The exact form of a file: URI is system-dependent, hence - * the transformation performed by this constructor is also - * system-dependent. + * Creates a new AmazeFile instance by converting the given file: URI into an + * abstract pathname. * - *

For a given abstract pathname f it is guaranteed that + *

The exact form of a file: URI is system-dependent, hence the transformation + * performed by this constructor is also system-dependent. * - *

- * new AmazeFile( f.{@link #toURI() toURI}()).equals( f.{@link #getAbsoluteFile() getAbsoluteFile}()) - *
+ *

For a given abstract pathname f it is guaranteed that * - * so long as the original abstract pathname, the URI, and the new abstract - * pathname are all created in (possibly different invocations of) the same - * Java virtual machine. This relationship typically does not hold, - * however, when a file: URI that is created in a virtual machine - * on one operating system is converted into an abstract pathname in a - * virtual machine on a different operating system. + *

* - * @param uri - * An absolute, hierarchical URI with a scheme equal to - * "file", a non-empty path component, and undefined - * authority, query, and fragment components + * new AmazeFile( f.{@link #toURI() + * toURI}()).equals( f.{@link #getAbsoluteFile() getAbsoluteFile}()) * - * @throws NullPointerException - * If uri is null + *
* - * @throws IllegalArgumentException - * If the preconditions on the parameter do not hold + * so long as the original abstract pathname, the URI, and the new abstract pathname are all + * created in (possibly different invocations of) the same Java virtual machine. This relationship + * typically does not hold, however, when a file: URI that is created in a virtual + * machine on one operating system is converted into an abstract pathname in a virtual machine on + * a different operating system. * + * @param uri An absolute, hierarchical URI with a scheme equal to "file", a non-empty + * path component, and undefined authority, query, and fragment components + * @throws NullPointerException If uri is null + * @throws IllegalArgumentException If the preconditions on the parameter do not hold * @see #toURI() * @see java.net.URI * @since 1.4 @@ -446,10 +383,8 @@ public AmazeFile(AmazeFile parent, String child) { public AmazeFile(URI uri) { // Check our many preconditions - if (!uri.isAbsolute()) - throw new IllegalArgumentException("URI is not absolute"); - if (uri.isOpaque()) - throw new IllegalArgumentException("URI is not hierarchical"); + if (!uri.isAbsolute()) throw new IllegalArgumentException("URI is not absolute"); + if (uri.isOpaque()) throw new IllegalArgumentException("URI is not hierarchical"); String scheme = uri.getScheme(); if ((scheme == null) || !scheme.equalsIgnoreCase("file")) throw new IllegalArgumentException("URI scheme is not \"file\""); @@ -457,11 +392,9 @@ public AmazeFile(URI uri) { throw new IllegalArgumentException("URI has an authority component"); if (uri.getFragment() != null) throw new IllegalArgumentException("URI has a fragment component"); - if (uri.getQuery() != null) - throw new IllegalArgumentException("URI has a query component"); + if (uri.getQuery() != null) throw new IllegalArgumentException("URI has a query component"); String p = uri.getPath(); - if (p.equals("")) - throw new IllegalArgumentException("URI path component is empty"); + if (p.equals("")) throw new IllegalArgumentException("URI path component is empty"); loadFilesystem(uri.toString()); separatorChar = fs.getSeparator(); @@ -479,23 +412,20 @@ public AmazeFile(URI uri) { } private void loadFilesystem(String path) { - if(SmbAmazeFileSystem.INSTANCE.isPathOfThisFilesystem(path)) { + if (SmbAmazeFileSystem.INSTANCE.isPathOfThisFilesystem(path)) { fs = SmbAmazeFileSystem.INSTANCE; } } - /* -- Path-component accessors -- */ /** - * Returns the name of the file or directory denoted by this abstract - * pathname. This is just the last name in the pathname's name - * sequence. If the pathname's name sequence is empty, then the empty - * string is returned. - * - * @return The name of the file or directory denoted by this abstract - * pathname, or the empty string if this pathname's name sequence - * is empty + * Returns the name of the file or directory denoted by this abstract pathname. This is just the + * last name in the pathname's name sequence. If the pathname's name sequence is empty, then the + * empty string is returned. + * + * @return The name of the file or directory denoted by this abstract pathname, or the empty + * string if this pathname's name sequence is empty */ public String getName() { int index = path.lastIndexOf(separatorChar); @@ -504,17 +434,15 @@ public String getName() { } /** - * Returns the pathname string of this abstract pathname's parent, or - * null if this pathname does not name a parent directory. + * Returns the pathname string of this abstract pathname's parent, or null if this + * pathname does not name a parent directory. * - *

The parent of an abstract pathname consists of the - * pathname's prefix, if any, and each name in the pathname's name - * sequence except for the last. If the name sequence is empty then - * the pathname does not name a parent directory. + *

The parent of an abstract pathname consists of the pathname's prefix, if any, and + * each name in the pathname's name sequence except for the last. If the name sequence is empty + * then the pathname does not name a parent directory. * - * @return The pathname string of the parent directory named by this - * abstract pathname, or null if this pathname - * does not name a parent + * @return The pathname string of the parent directory named by this abstract pathname, or + * null if this pathname does not name a parent */ public String getParent() { int index = path.lastIndexOf(separatorChar); @@ -527,19 +455,15 @@ public String getParent() { } /** - * Returns the abstract pathname of this abstract pathname's parent, - * or null if this pathname does not name a parent - * directory. - * - *

The parent of an abstract pathname consists of the - * pathname's prefix, if any, and each name in the pathname's name - * sequence except for the last. If the name sequence is empty then - * the pathname does not name a parent directory. + * Returns the abstract pathname of this abstract pathname's parent, or null if this + * pathname does not name a parent directory. * - * @return The abstract pathname of the parent directory named by this - * abstract pathname, or null if this pathname - * does not name a parent + *

The parent of an abstract pathname consists of the pathname's prefix, if any, and + * each name in the pathname's name sequence except for the last. If the name sequence is empty + * then the pathname does not name a parent directory. * + * @return The abstract pathname of the parent directory named by this abstract pathname, or + * null if this pathname does not name a parent * @since 1.2 */ public AmazeFile getParentFile() { @@ -549,27 +473,23 @@ public AmazeFile getParentFile() { } /** - * Converts this abstract pathname into a pathname string. The resulting - * string uses the {@link #separator default name-separator character} to - * separate the names in the name sequence. + * Converts this abstract pathname into a pathname string. The resulting string uses the {@link + * #separator default name-separator character} to separate the names in the name sequence. * - * @return The string form of this abstract pathname + * @return The string form of this abstract pathname */ public String getPath() { return path; } - /* -- Path operations -- */ // Android-changed: Android-specific path information /** - * Tests whether this abstract pathname is absolute. The definition of - * absolute pathname is system dependent. On Android, absolute paths start with - * the character '/'. + * Tests whether this abstract pathname is absolute. The definition of absolute pathname is system + * dependent. On Android, absolute paths start with the character '/'. * - * @return true if this abstract pathname is absolute, - * false otherwise + * @return true if this abstract pathname is absolute, false otherwise */ public boolean isAbsolute() { return fs.isAbsolute(this); @@ -577,30 +497,27 @@ public boolean isAbsolute() { // Android-changed: Android-specific path information /** - * Returns the absolute path of this file. An absolute path is a path that starts at a root - * of the file system. On Android, there is only one root: {@code /}. - * - *

A common use for absolute paths is when passing paths to a {@code Process} as - * command-line arguments, to remove the requirement implied by relative paths, that the - * child must have the same working directory as its parent. + * Returns the absolute path of this file. An absolute path is a path that starts at a root of the + * file system. On Android, there is only one root: {@code /}. * - * @return The absolute pathname string denoting the same file or - * directory as this abstract pathname + *

A common use for absolute paths is when passing paths to a {@code Process} as command-line + * arguments, to remove the requirement implied by relative paths, that the child must have the + * same working directory as its parent. * - * @see java.io.File#isAbsolute() + * @return The absolute pathname string denoting the same file or directory as this abstract + * pathname + * @see java.io.File#isAbsolute() */ public String getAbsolutePath() { return fs.resolve(this); } /** - * Returns the absolute form of this abstract pathname. Equivalent to - * new File(this.{@link #getAbsolutePath}). - * - * @return The absolute abstract pathname denoting the same file or - * directory as this abstract pathname - * + * Returns the absolute form of this abstract pathname. Equivalent to + * new File(this.{@link #getAbsolutePath}). * + * @return The absolute abstract pathname denoting the same file or directory as this abstract + * pathname * @since 1.2 */ public AmazeFile getAbsoluteFile() { @@ -611,34 +528,26 @@ public AmazeFile getAbsoluteFile() { /** * Returns the canonical pathname string of this abstract pathname. * - *

A canonical pathname is both absolute and unique. The precise - * definition of canonical form is system-dependent. This method first - * converts this pathname to absolute form if necessary, as if by invoking the - * {@link #getAbsolutePath} method, and then maps it to its unique form in a - * system-dependent way. This typically involves removing redundant names - * such as "." and ".." from the pathname, resolving - * symbolic links (on UNIX platforms), and converting drive letters to a - * standard case (on Microsoft Windows platforms). - * - *

Every pathname that denotes an existing file or directory has a - * unique canonical form. Every pathname that denotes a nonexistent file - * or directory also has a unique canonical form. The canonical form of - * the pathname of a nonexistent file or directory may be different from - * the canonical form of the same pathname after the file or directory is - * created. Similarly, the canonical form of the pathname of an existing - * file or directory may be different from the canonical form of the same - * pathname after the file or directory is deleted. - * - * @return The canonical pathname string denoting the same file or - * directory as this abstract pathname - * - * @throws IOException - * If an I/O error occurs, which is possible because the - * construction of the canonical pathname may require - * filesystem queries - * - * @since JDK1.1 - * @see Path#toRealPath + *

A canonical pathname is both absolute and unique. The precise definition of canonical form + * is system-dependent. This method first converts this pathname to absolute form if necessary, as + * if by invoking the {@link #getAbsolutePath} method, and then maps it to its unique form in a + * system-dependent way. This typically involves removing redundant names such as "." and + * ".." from the pathname, resolving symbolic links (on UNIX platforms), and converting + * drive letters to a standard case (on Microsoft Windows platforms). + * + *

Every pathname that denotes an existing file or directory has a unique canonical form. Every + * pathname that denotes a nonexistent file or directory also has a unique canonical form. The + * canonical form of the pathname of a nonexistent file or directory may be different from the + * canonical form of the same pathname after the file or directory is created. Similarly, the + * canonical form of the pathname of an existing file or directory may be different from the + * canonical form of the same pathname after the file or directory is deleted. + * + * @return The canonical pathname string denoting the same file or directory as this abstract + * pathname + * @throws IOException If an I/O error occurs, which is possible because the construction of the + * canonical pathname may require filesystem queries + * @since JDK1.1 + * @see Path#toRealPath */ public String getCanonicalPath() throws IOException { if (isInvalid()) { @@ -648,20 +557,15 @@ public String getCanonicalPath() throws IOException { } /** - * Returns the canonical form of this abstract pathname. Equivalent to - * new File(this.{@link #getCanonicalPath}). - * - * @return The canonical pathname string denoting the same file or - * directory as this abstract pathname - * - * @throws IOException - * If an I/O error occurs, which is possible because the - * construction of the canonical pathname may require - * filesystem queries - * + * Returns the canonical form of this abstract pathname. Equivalent to + * new File(this.{@link #getCanonicalPath}). * + * @return The canonical pathname string denoting the same file or directory as this abstract + * pathname + * @throws IOException If an I/O error occurs, which is possible because the construction of the + * canonical pathname may require filesystem queries * @since 1.2 - * @see Path#toRealPath + * @see Path#toRealPath */ public AmazeFile getCanonicalFile() throws IOException { String canonPath = getCanonicalPath(); @@ -670,48 +574,44 @@ public AmazeFile getCanonicalFile() throws IOException { private static String slashify(String path, boolean isDirectory) { String p = path; - if (File.separatorChar != '/') - p = p.replace(File.separatorChar, '/'); - if (!p.startsWith("/")) - p = "/" + p; - if (!p.endsWith("/") && isDirectory) - p = p + "/"; + if (File.separatorChar != '/') p = p.replace(File.separatorChar, '/'); + if (!p.startsWith("/")) p = "/" + p; + if (!p.endsWith("/") && isDirectory) p = p + "/"; return p; } /** * Constructs a file: URI that represents this abstract pathname. * - *

The exact form of the URI is system-dependent. If it can be - * determined that the file denoted by this abstract pathname is a - * directory, then the resulting URI will end with a slash. + *

The exact form of the URI is system-dependent. If it can be determined that the file denoted + * by this abstract pathname is a directory, then the resulting URI will end with a slash. + * + *

For a given abstract pathname f, it is guaranteed that * - *

For a given abstract pathname f, it is guaranteed that + *

* - *
- * new {@link #AmazeFile(java.net.URI) File}( f.toURI()).equals( f.{@link #getAbsoluteFile() getAbsoluteFile}()) - *
+ * new {@link #AmazeFile(java.net.URI) + * File}( f.toURI()).equals( f.{@link #getAbsoluteFile() + * getAbsoluteFile}()) * - * so long as the original abstract pathname, the URI, and the new abstract - * pathname are all created in (possibly different invocations of) the same - * Java virtual machine. Due to the system-dependent nature of abstract - * pathnames, however, this relationship typically does not hold when a - * file: URI that is created in a virtual machine on one operating - * system is converted into an abstract pathname in a virtual machine on a - * different operating system. + *
* - *

Note that when this abstract pathname represents a UNC pathname then - * all components of the UNC (including the server name component) are encoded - * in the {@code URI} path. The authority component is undefined, meaning - * that it is represented as {@code null}. The {@link Path} class defines the - * {@link Path#toUri toUri} method to encode the server name in the authority - * component of the resulting {@code URI}. The {@link #toPath toPath} method - * may be used to obtain a {@code Path} representing this abstract pathname. + * so long as the original abstract pathname, the URI, and the new abstract pathname are all + * created in (possibly different invocations of) the same Java virtual machine. Due to the + * system-dependent nature of abstract pathnames, however, this relationship typically does not + * hold when a file: URI that is created in a virtual machine on one operating system is + * converted into an abstract pathname in a virtual machine on a different operating system. * - * @return An absolute, hierarchical URI with a scheme equal to - * "file", a path representing this abstract pathname, - * and undefined authority, query, and fragment components + *

Note that when this abstract pathname represents a UNC pathname then all components of the + * UNC (including the server name component) are encoded in the {@code URI} path. The authority + * component is undefined, meaning that it is represented as {@code null}. The {@link Path} class + * defines the {@link Path#toUri toUri} method to encode the server name in the authority + * component of the resulting {@code URI}. The {@link #toPath toPath} method may be used to obtain + * a {@code Path} representing this abstract pathname. * + * @return An absolute, hierarchical URI with a scheme equal to "file", a path + * representing this abstract pathname, and undefined authority, query, and fragment + * components * @see #AmazeFile(java.net.URI) * @see java.net.URI * @see java.net.URI#toURL() @@ -722,26 +622,22 @@ public URI toURI() { try { AmazeFile f = getAbsoluteFile(); String sp = slashify(f.getPath(), f.isDirectory()); - if (sp.startsWith("//")) - sp = "//" + sp; + if (sp.startsWith("//")) sp = "//" + sp; return new URI("file", null, sp, null); } catch (URISyntaxException x) { - throw new Error(x); // Can't happen + throw new Error(x); // Can't happen } } - /* -- Attribute accessors -- */ // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on android /** - * Tests whether the application can read the file denoted by this - * abstract pathname. + * Tests whether the application can read the file denoted by this abstract pathname. * - * @return true if and only if the file specified by this - * abstract pathname exists and can be read by the - * application; false otherwise + * @return true if and only if the file specified by this abstract pathname exists + * and can be read by the application; false otherwise */ public boolean canRead() { if (isInvalid()) { @@ -753,14 +649,11 @@ public boolean canRead() { // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on android /** - * Tests whether the application can modify the file denoted by this - * abstract pathname. - * - * @return true if and only if the file system actually - * contains a file denoted by this abstract pathname and - * the application is allowed to write to the file; - * false otherwise. + * Tests whether the application can modify the file denoted by this abstract pathname. * + * @return true if and only if the file system actually contains a file denoted by + * this abstract pathname and the application is allowed to write to the file; + * false otherwise. */ public boolean canWrite() { if (isInvalid()) { @@ -770,12 +663,10 @@ public boolean canWrite() { } /** - * Tests whether the file or directory denoted by this abstract pathname - * exists. - * - * @return true if and only if the file or directory denoted - * by this abstract pathname exists; false otherwise + * Tests whether the file or directory denoted by this abstract pathname exists. * + * @return true if and only if the file or directory denoted by this abstract + * pathname exists; false otherwise */ public boolean exists() { if (isInvalid()) { @@ -787,44 +678,36 @@ public boolean exists() { } /** - * Tests whether the file denoted by this abstract pathname is a - * directory. + * Tests whether the file denoted by this abstract pathname is a directory. * - *

Where it is required to distinguish an I/O exception from the case - * that the file is not a directory, or where several attributes of the - * same file are required at the same time, then the {@link - * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) - * Files.readAttributes} method may be used. - * - * @return true if and only if the file denoted by this - * abstract pathname exists and is a directory; - * false otherwise + *

Where it is required to distinguish an I/O exception from the case that the file is not a + * directory, or where several attributes of the same file are required at the same time, then the + * {@link java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) Files.readAttributes} method + * may be used. * + * @return true if and only if the file denoted by this abstract pathname exists + * and is a directory; false otherwise */ public boolean isDirectory() { if (isInvalid()) { return false; } - return ((fs.getBooleanAttributes(this) & AmazeFileSystem.BA_DIRECTORY) - != 0); + return ((fs.getBooleanAttributes(this) & AmazeFileSystem.BA_DIRECTORY) != 0); } /** - * Tests whether the file denoted by this abstract pathname is a normal - * file. A file is normal if it is not a directory and, in - * addition, satisfies other system-dependent criteria. Any non-directory - * file created by a Java application is guaranteed to be a normal file. - * - *

Where it is required to distinguish an I/O exception from the case - * that the file is not a normal file, or where several attributes of the - * same file are required at the same time, then the {@link - * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) - * Files.readAttributes} method may be used. + * Tests whether the file denoted by this abstract pathname is a normal file. A file is + * normal if it is not a directory and, in addition, satisfies other system-dependent + * criteria. Any non-directory file created by a Java application is guaranteed to be a normal + * file. * - * @return true if and only if the file denoted by this - * abstract pathname exists and is a normal file; - * false otherwise + *

Where it is required to distinguish an I/O exception from the case that the file is not a + * normal file, or where several attributes of the same file are required at the same time, then + * the {@link java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) Files.readAttributes} + * method may be used. * + * @return true if and only if the file denoted by this abstract pathname exists + * and is a normal file; false otherwise */ public boolean isFile() { if (isInvalid()) { @@ -834,16 +717,13 @@ public boolean isFile() { } /** - * Tests whether the file named by this abstract pathname is a hidden - * file. The exact definition of hidden is system-dependent. On - * UNIX systems, a file is considered to be hidden if its name begins with - * a period character ('.'). On Microsoft Windows systems, a file is - * considered to be hidden if it has been marked as such in the filesystem. - * - * @return true if and only if the file denoted by this - * abstract pathname is hidden according to the conventions of the - * underlying platform + * Tests whether the file named by this abstract pathname is a hidden file. The exact definition + * of hidden is system-dependent. On UNIX systems, a file is considered to be hidden if + * its name begins with a period character ('.'). On Microsoft Windows systems, a + * file is considered to be hidden if it has been marked as such in the filesystem. * + * @return true if and only if the file denoted by this abstract pathname is hidden + * according to the conventions of the underlying platform * @since 1.2 */ public boolean isHidden() { @@ -854,21 +734,17 @@ public boolean isHidden() { } /** - * Returns the time that the file denoted by this abstract pathname was - * last modified. - * - *

Where it is required to distinguish an I/O exception from the case - * where {@code 0L} is returned, or where several attributes of the - * same file are required at the same time, or where the time of last - * access or the creation time are required, then the {@link - * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) - * Files.readAttributes} method may be used. + * Returns the time that the file denoted by this abstract pathname was last modified. * - * @return A long value representing the time the file was - * last modified, measured in milliseconds since the epoch - * (00:00:00 GMT, January 1, 1970), or 0L if the - * file does not exist or if an I/O error occurs + *

Where it is required to distinguish an I/O exception from the case where {@code 0L} is + * returned, or where several attributes of the same file are required at the same time, or where + * the time of last access or the creation time are required, then the {@link + * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) Files.readAttributes} method may be + * used. * + * @return A long value representing the time the file was last modified, measured in + * milliseconds since the epoch (00:00:00 GMT, January 1, 1970), or 0L if the + * file does not exist or if an I/O error occurs */ public long lastModified() { if (isInvalid()) { @@ -878,19 +754,17 @@ public long lastModified() { } /** - * Returns the length of the file denoted by this abstract pathname. - * The return value is unspecified if this pathname denotes a directory. - * - *

Where it is required to distinguish an I/O exception from the case - * that {@code 0L} is returned, or where several attributes of the same file - * are required at the same time, then the {@link - * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) - * Files.readAttributes} method may be used. - * - * @return The length, in bytes, of the file denoted by this abstract - * pathname, or 0L if the file does not exist. Some - * operating systems may return 0L for pathnames - * denoting system-dependent entities such as devices or pipes. + * Returns the length of the file denoted by this abstract pathname. The return value is + * unspecified if this pathname denotes a directory. + * + *

Where it is required to distinguish an I/O exception from the case that {@code 0L} is + * returned, or where several attributes of the same file are required at the same time, then the + * {@link java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) Files.readAttributes} method + * may be used. + * + * @return The length, in bytes, of the file denoted by this abstract pathname, or 0L + * if the file does not exist. Some operating systems may return 0L for pathnames + * denoting system-dependent entities such as devices or pipes. */ public long length() throws IOException { if (isInvalid()) { @@ -899,28 +773,21 @@ public long length() throws IOException { return fs.getLength(this); } - /* -- File operations -- */ /** - * Atomically creates a new, empty file named by this abstract pathname if - * and only if a file with this name does not yet exist. The check for the - * existence of the file and the creation of the file if it does not exist - * are a single operation that is atomic with respect to all other + * Atomically creates a new, empty file named by this abstract pathname if and only if a file with + * this name does not yet exist. The check for the existence of the file and the creation of the + * file if it does not exist are a single operation that is atomic with respect to all other * filesystem activities that might affect the file. - *

- * Note: this method should not be used for file-locking, as - * the resulting protocol cannot be made to work reliably. The - * {@link java.nio.channels.FileLock FileLock} - * facility should be used instead. - * - * @return true if the named file does not exist and was - * successfully created; false if the named file - * already exists * - * @throws IOException - * If an I/O error occurred + *

Note: this method should not be used for file-locking, as the resulting protocol + * cannot be made to work reliably. The {@link java.nio.channels.FileLock FileLock} facility + * should be used instead. * + * @return true if the named file does not exist and was successfully created; + * false if the named file already exists + * @throws IOException If an I/O error occurred * @since 1.2 */ public boolean createNewFile() throws IOException { @@ -931,17 +798,16 @@ public boolean createNewFile() throws IOException { } /** - * Deletes the file or directory denoted by this abstract pathname. If - * this pathname denotes a directory, then the directory must be empty in - * order to be deleted. + * Deletes the file or directory denoted by this abstract pathname. If this pathname denotes a + * directory, then the directory must be empty in order to be deleted. * - *

Note that the {@link java.nio.file.Files} class defines the {@link - * java.nio.file.Files#delete(Path) delete} method to throw an {@link IOException} - * when a file cannot be deleted. This is useful for error reporting and to - * diagnose why a file cannot be deleted. + *

Note that the {@link java.nio.file.Files} class defines the {@link + * java.nio.file.Files#delete(Path) delete} method to throw an {@link IOException} when a file + * cannot be deleted. This is useful for error reporting and to diagnose why a file cannot be + * deleted. * - * @return true if and only if the file or directory is - * successfully deleted; false otherwise + * @return true if and only if the file or directory is successfully deleted; + * false otherwise */ public boolean delete() { if (isInvalid()) { @@ -952,38 +818,33 @@ public boolean delete() { // Android-added: Additional information about Android behaviour. /** - * Requests that the file or directory denoted by this abstract - * pathname be deleted when the virtual machine terminates. - * Files (or directories) are deleted in the reverse order that - * they are registered. Invoking this method to delete a file or - * directory that is already registered for deletion has no effect. - * Deletion will be attempted only for normal termination of the + * Requests that the file or directory denoted by this abstract pathname be deleted when the + * virtual machine terminates. Files (or directories) are deleted in the reverse order that they + * are registered. Invoking this method to delete a file or directory that is already registered + * for deletion has no effect. Deletion will be attempted only for normal termination of the * virtual machine, as defined by the Java Language Specification. * - *

Once deletion has been requested, it is not possible to cancel the - * request. This method should therefore be used with care. + *

Once deletion has been requested, it is not possible to cancel the request. This method + * should therefore be used with care. * - *

- * Note: this method should not be used for file-locking, as - * the resulting protocol cannot be made to work reliably. The - * {@link java.nio.channels.FileLock FileLock} - * facility should be used instead. + *

Note: this method should not be used for file-locking, as the resulting protocol + * cannot be made to work reliably. The {@link java.nio.channels.FileLock FileLock} facility + * should be used instead. + * + *

Note that on Android, the application lifecycle does not include VM termination, so + * calling this method will not ensure that files are deleted. Instead, you should use the + * most appropriate out of: * - *

Note that on Android, the application lifecycle does not include VM termination, - * so calling this method will not ensure that files are deleted. Instead, you should - * use the most appropriate out of: *

    - *
  • Use a {@code finally} clause to manually invoke {@link #delete}. - *
  • Maintain your own set of files to delete, and process it at an appropriate point - * in your application's lifecycle. - *
  • Use the Unix trick of deleting the file as soon as all readers and writers have - * opened it. No new readers/writers will be able to access the file, but all existing - * ones will still have access until the last one closes the file. + *
  • Use a {@code finally} clause to manually invoke {@link #delete}. + *
  • Maintain your own set of files to delete, and process it at an appropriate point in your + * application's lifecycle. + *
  • Use the Unix trick of deleting the file as soon as all readers and writers have opened + * it. No new readers/writers will be able to access the file, but all existing ones will + * still have access until the last one closes the file. *
* - * * @see #delete - * * @since 1.2 */ public void deleteOnExit() { @@ -994,31 +855,25 @@ public void deleteOnExit() { } /** - * Returns an array of strings naming the files and directories in the - * directory denoted by this abstract pathname. - * - *

If this abstract pathname does not denote a directory, then this - * method returns {@code null}. Otherwise an array of strings is - * returned, one for each file or directory in the directory. Names - * denoting the directory itself and the directory's parent directory are - * not included in the result. Each string is a file name rather than a - * complete path. - * - *

There is no guarantee that the name strings in the resulting array - * will appear in any specific order; they are not, in particular, - * guaranteed to appear in alphabetical order. - * - *

Note that the {@link java.nio.file.Files} class defines the {@link - * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream} method to - * open a directory and iterate over the names of the files in the directory. - * This may use less resources when working with very large directories, and - * may be more responsive when working with remote directories. - * - * @return An array of strings naming the files and directories in the - * directory denoted by this abstract pathname. The array will be - * empty if the directory is empty. Returns {@code null} if - * this abstract pathname does not denote a directory, or if an - * I/O error occurs. + * Returns an array of strings naming the files and directories in the directory denoted by this + * abstract pathname. + * + *

If this abstract pathname does not denote a directory, then this method returns {@code + * null}. Otherwise an array of strings is returned, one for each file or directory in the + * directory. Names denoting the directory itself and the directory's parent directory are not + * included in the result. Each string is a file name rather than a complete path. + * + *

There is no guarantee that the name strings in the resulting array will appear in any + * specific order; they are not, in particular, guaranteed to appear in alphabetical order. + * + *

Note that the {@link java.nio.file.Files} class defines the {@link + * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream} method to open a directory and + * iterate over the names of the files in the directory. This may use less resources when working + * with very large directories, and may be more responsive when working with remote directories. + * + * @return An array of strings naming the files and directories in the directory denoted by this + * abstract pathname. The array will be empty if the directory is empty. Returns {@code null} + * if this abstract pathname does not denote a directory, or if an I/O error occurs. */ public String[] list() { if (isInvalid()) { @@ -1028,27 +883,20 @@ public String[] list() { } /** - * Returns an array of strings naming the files and directories in the - * directory denoted by this abstract pathname that satisfy the specified - * filter. The behavior of this method is the same as that of the - * {@link #list()} method, except that the strings in the returned array - * must satisfy the filter. If the given {@code filter} is {@code null} - * then all names are accepted. Otherwise, a name satisfies the filter if - * and only if the value {@code true} results when the {@link - * AmazeFilenameFilter#accept AmazeFilenameFilter.accept(File, String)} method - * of the filter is invoked on this abstract pathname and the name of a - * file or directory in the directory that it denotes. - * - * @param filter - * A filename filter - * - * @return An array of strings naming the files and directories in the - * directory denoted by this abstract pathname that were accepted - * by the given {@code filter}. The array will be empty if the - * directory is empty or if no names were accepted by the filter. - * Returns {@code null} if this abstract pathname does not denote - * a directory, or if an I/O error occurs. - * + * Returns an array of strings naming the files and directories in the directory denoted by this + * abstract pathname that satisfy the specified filter. The behavior of this method is the same as + * that of the {@link #list()} method, except that the strings in the returned array must satisfy + * the filter. If the given {@code filter} is {@code null} then all names are accepted. Otherwise, + * a name satisfies the filter if and only if the value {@code true} results when the {@link + * AmazeFilenameFilter#accept AmazeFilenameFilter.accept(File, String)} method of the filter + * is invoked on this abstract pathname and the name of a file or directory in the directory that + * it denotes. + * + * @param filter A filename filter + * @return An array of strings naming the files and directories in the directory denoted by this + * abstract pathname that were accepted by the given {@code filter}. The array will be empty + * if the directory is empty or if no names were accepted by the filter. Returns {@code null} + * if this abstract pathname does not denote a directory, or if an I/O error occurs. * @see java.nio.file.Files#newDirectoryStream(Path,String) */ public String[] list(AmazeFilenameFilter filter) { @@ -1057,7 +905,7 @@ public String[] list(AmazeFilenameFilter filter) { return names; } List v = new ArrayList<>(); - for (int i = 0 ; i < names.length ; i++) { + for (int i = 0; i < names.length; i++) { if (filter.accept(this, names[i])) { v.add(names[i]); } @@ -1066,37 +914,30 @@ public String[] list(AmazeFilenameFilter filter) { } /** - * Returns an array of abstract pathnames denoting the files in the - * directory denoted by this abstract pathname. - * - *

If this abstract pathname does not denote a directory, then this - * method returns {@code null}. Otherwise an array of {@code File} objects - * is returned, one for each file or directory in the directory. Pathnames - * denoting the directory itself and the directory's parent directory are - * not included in the result. Each resulting abstract pathname is - * constructed from this abstract pathname using the {@link #File(File, - * String) File(File, String)} constructor. Therefore if this - * pathname is absolute then each resulting pathname is absolute; if this - * pathname is relative then each resulting pathname will be relative to - * the same directory. - * - *

There is no guarantee that the name strings in the resulting array - * will appear in any specific order; they are not, in particular, - * guaranteed to appear in alphabetical order. - * - *

Note that the {@link java.nio.file.Files} class defines the {@link - * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream} method - * to open a directory and iterate over the names of the files in the - * directory. This may use less resources when working with very large - * directories. - * - * @return An array of abstract pathnames denoting the files and - * directories in the directory denoted by this abstract pathname. - * The array will be empty if the directory is empty. Returns - * {@code null} if this abstract pathname does not denote a - * directory, or if an I/O error occurs. - * - * @since 1.2 + * Returns an array of abstract pathnames denoting the files in the directory denoted by this + * abstract pathname. + * + *

If this abstract pathname does not denote a directory, then this method returns {@code + * null}. Otherwise an array of {@code File} objects is returned, one for each file or directory + * in the directory. Pathnames denoting the directory itself and the directory's parent directory + * are not included in the result. Each resulting abstract pathname is constructed from this + * abstract pathname using the {@link #File(File, String) File(File, String)} constructor. + * Therefore if this pathname is absolute then each resulting pathname is absolute; if this + * pathname is relative then each resulting pathname will be relative to the same directory. + * + *

There is no guarantee that the name strings in the resulting array will appear in any + * specific order; they are not, in particular, guaranteed to appear in alphabetical order. + * + *

Note that the {@link java.nio.file.Files} class defines the {@link + * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream} method to open a directory and + * iterate over the names of the files in the directory. This may use less resources when working + * with very large directories. + * + * @return An array of abstract pathnames denoting the files and directories in the directory + * denoted by this abstract pathname. The array will be empty if the directory is empty. + * Returns {@code null} if this abstract pathname does not denote a directory, or if an I/O + * error occurs. + * @since 1.2 */ public AmazeFile[] listFiles() { String[] ss = list(); @@ -1110,28 +951,21 @@ public AmazeFile[] listFiles() { } /** - * Returns an array of abstract pathnames denoting the files and - * directories in the directory denoted by this abstract pathname that - * satisfy the specified filter. The behavior of this method is the same - * as that of the {@link #listFiles()} method, except that the pathnames in - * the returned array must satisfy the filter. If the given {@code filter} - * is {@code null} then all pathnames are accepted. Otherwise, a pathname - * satisfies the filter if and only if the value {@code true} results when - * the {@link AmazeFilenameFilter#accept - * AmazeFilenameFilter.accept(File, String)} method of the filter is - * invoked on this abstract pathname and the name of a file or directory in - * the directory that it denotes. - * - * @param filter - * A filename filter - * - * @return An array of abstract pathnames denoting the files and - * directories in the directory denoted by this abstract pathname. - * The array will be empty if the directory is empty. Returns - * {@code null} if this abstract pathname does not denote a - * directory, or if an I/O error occurs. - * - * @since 1.2 + * Returns an array of abstract pathnames denoting the files and directories in the directory + * denoted by this abstract pathname that satisfy the specified filter. The behavior of this + * method is the same as that of the {@link #listFiles()} method, except that the pathnames in the + * returned array must satisfy the filter. If the given {@code filter} is {@code null} then all + * pathnames are accepted. Otherwise, a pathname satisfies the filter if and only if the value + * {@code true} results when the {@link AmazeFilenameFilter#accept + * AmazeFilenameFilter.accept(File, String)} method of the filter is invoked on this abstract + * pathname and the name of a file or directory in the directory that it denotes. + * + * @param filter A filename filter + * @return An array of abstract pathnames denoting the files and directories in the directory + * denoted by this abstract pathname. The array will be empty if the directory is empty. + * Returns {@code null} if this abstract pathname does not denote a directory, or if an I/O + * error occurs. + * @since 1.2 * @see java.nio.file.Files#newDirectoryStream(Path,String) */ public AmazeFile[] listFiles(AmazeFilenameFilter filter) { @@ -1139,32 +973,25 @@ public AmazeFile[] listFiles(AmazeFilenameFilter filter) { if (ss == null) return null; ArrayList files = new ArrayList<>(); for (String s : ss) - if ((filter == null) || filter.accept(this, s)) - files.add(new AmazeFile(s, this)); + if ((filter == null) || filter.accept(this, s)) files.add(new AmazeFile(s, this)); return files.toArray(new AmazeFile[files.size()]); } /** - * Returns an array of abstract pathnames denoting the files and - * directories in the directory denoted by this abstract pathname that - * satisfy the specified filter. The behavior of this method is the same - * as that of the {@link #listFiles()} method, except that the pathnames in - * the returned array must satisfy the filter. If the given {@code filter} - * is {@code null} then all pathnames are accepted. Otherwise, a pathname - * satisfies the filter if and only if the value {@code true} results when - * the {@link FileFilter#accept FileFilter.accept(File)} method of the + * Returns an array of abstract pathnames denoting the files and directories in the directory + * denoted by this abstract pathname that satisfy the specified filter. The behavior of this + * method is the same as that of the {@link #listFiles()} method, except that the pathnames in the + * returned array must satisfy the filter. If the given {@code filter} is {@code null} then all + * pathnames are accepted. Otherwise, a pathname satisfies the filter if and only if the value + * {@code true} results when the {@link FileFilter#accept FileFilter.accept(File)} method of the * filter is invoked on the pathname. * - * @param filter - * A file filter - * - * @return An array of abstract pathnames denoting the files and - * directories in the directory denoted by this abstract pathname. - * The array will be empty if the directory is empty. Returns - * {@code null} if this abstract pathname does not denote a - * directory, or if an I/O error occurs. - * - * @since 1.2 + * @param filter A file filter + * @return An array of abstract pathnames denoting the files and directories in the directory + * denoted by this abstract pathname. The array will be empty if the directory is empty. + * Returns {@code null} if this abstract pathname does not denote a directory, or if an I/O + * error occurs. + * @since 1.2 * @see java.nio.file.Files#newDirectoryStream(Path,java.nio.file.DirectoryStream.Filter) */ public AmazeFile[] listFiles(AmazeFileFilter filter) { @@ -1173,8 +1000,7 @@ public AmazeFile[] listFiles(AmazeFileFilter filter) { ArrayList files = new ArrayList<>(); for (String s : ss) { AmazeFile f = new AmazeFile(s, this); - if ((filter == null) || filter.accept(f)) - files.add(f); + if ((filter == null) || filter.accept(f)) files.add(f); } return files.toArray(new AmazeFile[files.size()]); } @@ -1190,9 +1016,8 @@ public OutputStream getOutputStream() { /** * Creates the directory named by this abstract pathname. * - * @return true if and only if the directory was - * created; false otherwise - * + * @return true if and only if the directory was created; false + * otherwise */ public boolean mkdir() { if (isInvalid()) { @@ -1202,15 +1027,12 @@ public boolean mkdir() { } /** - * Creates the directory named by this abstract pathname, including any - * necessary but nonexistent parent directories. Note that if this - * operation fails it may have succeeded in creating some of the necessary - * parent directories. - * - * @return true if and only if the directory was created, - * along with all necessary parent directories; false - * otherwise + * Creates the directory named by this abstract pathname, including any necessary but nonexistent + * parent directories. Note that if this operation fails it may have succeeded in creating some of + * the necessary parent directories. * + * @return true if and only if the directory was created, along with all necessary + * parent directories; false otherwise */ public boolean mkdirs() { if (exists()) { @@ -1227,8 +1049,7 @@ public boolean mkdirs() { } AmazeFile parent = canonFile.getParentFile(); - return (parent != null && (parent.mkdirs() || parent.exists()) && - canonFile.mkdir()); + return (parent != null && (parent.mkdirs() || parent.exists()) && canonFile.mkdir()); } // Android-changed: Replaced generic platform info with Android specific one. @@ -1236,29 +1057,24 @@ public boolean mkdirs() { * Renames the file denoted by this abstract pathname. * *

Many failures are possible. Some of the more likely failures include: + * *

    - *
  • Write permission is required on the directories containing both the source and - * destination paths. - *
  • Search permission is required for all parents of both paths. - *
  • Both paths be on the same mount point. On Android, applications are most likely to hit - * this restriction when attempting to copy between internal storage and an SD card. + *
  • Write permission is required on the directories containing both the source and + * destination paths. + *
  • Search permission is required for all parents of both paths. + *
  • Both paths be on the same mount point. On Android, applications are most likely to hit + * this restriction when attempting to copy between internal storage and an SD card. *
* - *

The return value should always be checked to make sure - * that the rename operation was successful. + *

The return value should always be checked to make sure that the rename operation was + * successful. * - *

Note that the {@link java.nio.file.Files} class defines the {@link - * java.nio.file.Files#move move} method to move or rename a file in a - * platform independent manner. + *

Note that the {@link java.nio.file.Files} class defines the {@link java.nio.file.Files#move + * move} method to move or rename a file in a platform independent manner. * - * @param dest The new abstract pathname for the named file - * - * @return true if and only if the renaming succeeded; - * false otherwise - * - * - * @throws NullPointerException - * If parameter dest is null + * @param dest The new abstract pathname for the named file + * @return true if and only if the renaming succeeded; false otherwise + * @throws NullPointerException If parameter dest is null */ public boolean renameTo(AmazeFile dest) { if (dest == null) { @@ -1271,25 +1087,18 @@ public boolean renameTo(AmazeFile dest) { } /** - * Sets the last-modified time of the file or directory named by this - * abstract pathname. - * - *

All platforms support file-modification times to the nearest second, - * but some provide more precision. The argument will be truncated to fit - * the supported precision. If the operation succeeds and no intervening - * operations on the file take place, then the next invocation of the - * {@link #lastModified} method will return the (possibly - * truncated) time argument that was passed to this method. - * - * @param time The new last-modified time, measured in milliseconds since - * the epoch (00:00:00 GMT, January 1, 1970) - * - * @return true if and only if the operation succeeded; - * false otherwise - * - * @throws IllegalArgumentException If the argument is negative + * Sets the last-modified time of the file or directory named by this abstract pathname. * + *

All platforms support file-modification times to the nearest second, but some provide more + * precision. The argument will be truncated to fit the supported precision. If the operation + * succeeds and no intervening operations on the file take place, then the next invocation of the + * {@link #lastModified} method will return the (possibly truncated) time + * argument that was passed to this method. * + * @param time The new last-modified time, measured in milliseconds since the epoch (00:00:00 GMT, + * January 1, 1970) + * @return true if and only if the operation succeeded; false otherwise + * @throws IllegalArgumentException If the argument is negative * @since 1.2 */ public boolean setLastModified(long time) { @@ -1303,16 +1112,12 @@ public boolean setLastModified(long time) { // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. /** - * Marks the file or directory named by this abstract pathname so that - * only read operations are allowed. After invoking this method the file - * or directory will not change until it is either deleted or marked - * to allow write access. Whether or not a read-only file or - * directory may be deleted depends upon the underlying system. - * - * @return true if and only if the operation succeeded; - * false otherwise - * + * Marks the file or directory named by this abstract pathname so that only read operations are + * allowed. After invoking this method the file or directory will not change until it is either + * deleted or marked to allow write access. Whether or not a read-only file or directory may be + * deleted depends upon the underlying system. * + * @return true if and only if the operation succeeded; false otherwise * @since 1.2 */ public boolean setReadOnly() { @@ -1325,28 +1130,21 @@ public boolean setReadOnly() { // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. /** - * Sets the owner's or everybody's write permission for this abstract - * pathname. - * - *

The {@link java.nio.file.Files} class defines methods that operate on - * file attributes including file permissions. This may be used when finer - * manipulation of file permissions is required. - * - * @param writable - * If true, sets the access permission to allow write - * operations; if false to disallow write operations - * - * @param ownerOnly - * If true, the write permission applies only to the - * owner's write permission; otherwise, it applies to everybody. If - * the underlying file system can not distinguish the owner's write - * permission from that of others, then the permission will apply to - * everybody, regardless of this value. - * - * @return true if and only if the operation succeeded. The - * operation will fail if the user does not have permission to change - * the access permissions of this abstract pathname. - * + * Sets the owner's or everybody's write permission for this abstract pathname. + * + *

The {@link java.nio.file.Files} class defines methods that operate on file attributes + * including file permissions. This may be used when finer manipulation of file permissions is + * required. + * + * @param writable If true, sets the access permission to allow write operations; if + * false to disallow write operations + * @param ownerOnly If true, the write permission applies only to the owner's write + * permission; otherwise, it applies to everybody. If the underlying file system can not + * distinguish the owner's write permission from that of others, then the permission will + * apply to everybody, regardless of this value. + * @return true if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. * @since 1.6 */ public boolean setWritable(boolean writable, boolean ownerOnly) { @@ -1359,23 +1157,19 @@ public boolean setWritable(boolean writable, boolean ownerOnly) { // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. /** - * A convenience method to set the owner's write permission for this abstract - * pathname. + * A convenience method to set the owner's write permission for this abstract pathname. * - *

An invocation of this method of the form file.setWritable(arg) - * behaves in exactly the same way as the invocation + *

An invocation of this method of the form file.setWritable(arg) behaves in exactly + * the same way as the invocation * *

    *     file.setWritable(arg, true) 
* - * @param writable - * If true, sets the access permission to allow write - * operations; if false to disallow write operations - * - * @return true if and only if the operation succeeded. The - * operation will fail if the user does not have permission to - * change the access permissions of this abstract pathname. - * + * @param writable If true, sets the access permission to allow write operations; if + * false to disallow write operations + * @return true if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. * @since 1.6 */ public boolean setWritable(boolean writable) { @@ -1385,31 +1179,22 @@ public boolean setWritable(boolean writable) { // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. /** - * Sets the owner's or everybody's read permission for this abstract - * pathname. - * - *

The {@link java.nio.file.Files} class defines methods that operate on - * file attributes including file permissions. This may be used when finer - * manipulation of file permissions is required. - * - * @param readable - * If true, sets the access permission to allow read - * operations; if false to disallow read operations - * - * @param ownerOnly - * If true, the read permission applies only to the - * owner's read permission; otherwise, it applies to everybody. If - * the underlying file system can not distinguish the owner's read - * permission from that of others, then the permission will apply to - * everybody, regardless of this value. - * - * @return true if and only if the operation succeeded. The - * operation will fail if the user does not have permission to - * change the access permissions of this abstract pathname. If - * readable is false and the underlying - * file system does not implement a read permission, then the - * operation will fail. - * + * Sets the owner's or everybody's read permission for this abstract pathname. + * + *

The {@link java.nio.file.Files} class defines methods that operate on file attributes + * including file permissions. This may be used when finer manipulation of file permissions is + * required. + * + * @param readable If true, sets the access permission to allow read operations; if + * false to disallow read operations + * @param ownerOnly If true, the read permission applies only to the owner's read + * permission; otherwise, it applies to everybody. If the underlying file system can not + * distinguish the owner's read permission from that of others, then the permission will apply + * to everybody, regardless of this value. + * @return true if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. If readable is false and the underlying file system + * does not implement a read permission, then the operation will fail. * @since 1.6 */ public boolean setReadable(boolean readable, boolean ownerOnly) { @@ -1422,26 +1207,20 @@ public boolean setReadable(boolean readable, boolean ownerOnly) { // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. /** - * A convenience method to set the owner's read permission for this abstract - * pathname. + * A convenience method to set the owner's read permission for this abstract pathname. * - *

An invocation of this method of the form file.setReadable(arg) - * behaves in exactly the same way as the invocation + *

An invocation of this method of the form file.setReadable(arg) behaves in exactly + * the same way as the invocation * *

    *     file.setReadable(arg, true) 
* - * @param readable - * If true, sets the access permission to allow read - * operations; if false to disallow read operations - * - * @return true if and only if the operation succeeded. The - * operation will fail if the user does not have permission to - * change the access permissions of this abstract pathname. If - * readable is false and the underlying - * file system does not implement a read permission, then the - * operation will fail. - * + * @param readable If true, sets the access permission to allow read operations; if + * false to disallow read operations + * @return true if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. If readable is false and the underlying file system + * does not implement a read permission, then the operation will fail. * @since 1.6 */ public boolean setReadable(boolean readable) { @@ -1451,31 +1230,22 @@ public boolean setReadable(boolean readable) { // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. /** - * Sets the owner's or everybody's execute permission for this abstract - * pathname. - * - *

The {@link java.nio.file.Files} class defines methods that operate on - * file attributes including file permissions. This may be used when finer - * manipulation of file permissions is required. - * - * @param executable - * If true, sets the access permission to allow execute - * operations; if false to disallow execute operations - * - * @param ownerOnly - * If true, the execute permission applies only to the - * owner's execute permission; otherwise, it applies to everybody. - * If the underlying file system can not distinguish the owner's - * execute permission from that of others, then the permission will - * apply to everybody, regardless of this value. - * - * @return true if and only if the operation succeeded. The - * operation will fail if the user does not have permission to - * change the access permissions of this abstract pathname. If - * executable is false and the underlying - * file system does not implement an execute permission, then the - * operation will fail. - * + * Sets the owner's or everybody's execute permission for this abstract pathname. + * + *

The {@link java.nio.file.Files} class defines methods that operate on file attributes + * including file permissions. This may be used when finer manipulation of file permissions is + * required. + * + * @param executable If true, sets the access permission to allow execute operations; + * if false to disallow execute operations + * @param ownerOnly If true, the execute permission applies only to the owner's + * execute permission; otherwise, it applies to everybody. If the underlying file system can + * not distinguish the owner's execute permission from that of others, then the permission + * will apply to everybody, regardless of this value. + * @return true if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. If executable is false and the underlying file system + * does not implement an execute permission, then the operation will fail. * @since 1.6 */ public boolean setExecutable(boolean executable, boolean ownerOnly) { @@ -1488,26 +1258,20 @@ public boolean setExecutable(boolean executable, boolean ownerOnly) { // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. /** - * A convenience method to set the owner's execute permission for this - * abstract pathname. + * A convenience method to set the owner's execute permission for this abstract pathname. * - *

An invocation of this method of the form file.setExcutable(arg) - * behaves in exactly the same way as the invocation + *

An invocation of this method of the form file.setExcutable(arg) behaves in exactly + * the same way as the invocation * *

    *     file.setExecutable(arg, true) 
* - * @param executable - * If true, sets the access permission to allow execute - * operations; if false to disallow execute operations - * - * @return true if and only if the operation succeeded. The - * operation will fail if the user does not have permission to - * change the access permissions of this abstract pathname. If - * executable is false and the underlying - * file system does not implement an execute permission, then the - * operation will fail. - * + * @param executable If true, sets the access permission to allow execute operations; + * if false to disallow execute operations + * @return true if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. If executable is false and the underlying file system + * does not implement an execute permission, then the operation will fail. * @since 1.6 */ public boolean setExecutable(boolean executable) { @@ -1517,12 +1281,10 @@ public boolean setExecutable(boolean executable) { // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. /** - * Tests whether the application can execute the file denoted by this - * abstract pathname. - * - * @return true if and only if the abstract pathname exists - * and the application is allowed to execute the file + * Tests whether the application can execute the file denoted by this abstract pathname. * + * @return true if and only if the abstract pathname exists and the + * application is allowed to execute the file * @since 1.6 */ public boolean canExecute() { @@ -1532,30 +1294,25 @@ public boolean canExecute() { return fs.checkAccess(this, AmazeFileSystem.ACCESS_EXECUTE); } - /* -- Filesystem interface -- */ // Android-changed: Replaced generic platform info with Android specific one. /** - * Returns the file system roots. On Android and other Unix systems, there is - * a single root, {@code /}. + * Returns the file system roots. On Android and other Unix systems, there is a single root, + * {@code /}. */ public AmazeFile[] listRoots() { return fs.listRoots(); } - /* -- Disk usage -- */ /** - * Returns the size of the partition named by this - * abstract pathname. - * - * @return The size, in bytes, of the partition or 0L if this - * abstract pathname does not name a partition - * If there is no way to determine, total space is -1 + * Returns the size of the partition named by this abstract pathname. * - * @since 1.6 + * @return The size, in bytes, of the partition or 0L if this abstract pathname does not + * name a partition If there is no way to determine, total space is -1 + * @since 1.6 */ public long getTotalSpace() { if (isInvalid()) { @@ -1563,31 +1320,26 @@ public long getTotalSpace() { } try { return fs.getSpace(this, AmazeFileSystem.SPACE_TOTAL); - } catch(NotImplementedError e) { + } catch (NotImplementedError e) { Log.w(TAG, "Call to unimplemented fuction", e); return -1; } } /** - * Returns the number of unallocated bytes in the partition named by this abstract path name. - * - *

The returned number of unallocated bytes is a hint, but not - * a guarantee, that it is possible to use most or any of these - * bytes. The number of unallocated bytes is most likely to be - * accurate immediately after this call. It is likely to be made - * inaccurate by any external I/O operations including those made - * on the system outside of this virtual machine. This method - * makes no guarantee that write operations to this file system - * will succeed. - * - * @return The number of unallocated bytes on the partition or 0L - * if the abstract pathname does not name a partition. This - * value will be less than or equal to the total file system size - * returned by {@link #getTotalSpace}. - * - * @since 1.6 + * Returns the number of unallocated bytes in the partition named by this + * abstract path name. + * + *

The returned number of unallocated bytes is a hint, but not a guarantee, that it is possible + * to use most or any of these bytes. The number of unallocated bytes is most likely to be + * accurate immediately after this call. It is likely to be made inaccurate by any external I/O + * operations including those made on the system outside of this virtual machine. This method + * makes no guarantee that write operations to this file system will succeed. + * + * @return The number of unallocated bytes on the partition or 0L if the abstract + * pathname does not name a partition. This value will be less than or equal to the total file + * system size returned by {@link #getTotalSpace}. + * @since 1.6 */ public long getFreeSpace() { if (isInvalid()) { @@ -1598,33 +1350,27 @@ public long getFreeSpace() { // Android-added: Replaced generic platform info with Android specific one. /** - * Returns the number of bytes available to this virtual machine on the - * partition named by this abstract pathname. When - * possible, this method checks for write permissions and other operating - * system restrictions and will therefore usually provide a more accurate - * estimate of how much new data can actually be written than {@link - * #getFreeSpace}. - * - *

The returned number of available bytes is a hint, but not a - * guarantee, that it is possible to use most or any of these bytes. The - * number of unallocated bytes is most likely to be accurate immediately - * after this call. It is likely to be made inaccurate by any external - * I/O operations including those made on the system outside of this - * virtual machine. This method makes no guarantee that write operations - * to this file system will succeed. - * - *

On Android (and other Unix-based systems), this method returns the number of free bytes - * available to non-root users, regardless of whether you're actually running as root, - * and regardless of any quota or other restrictions that might apply to the user. - * (The {@code getFreeSpace} method returns the number of bytes potentially available to root.) - * - * @return The number of available bytes on the partition or 0L - * if the abstract pathname does not name a partition. On - * systems where this information is not available, this method - * will be equivalent to a call to {@link #getFreeSpace}. - * If there is no way to determine the current space left -1 is returned. - * - * @since 1.6 + * Returns the number of bytes available to this virtual machine on the partition named by this abstract pathname. When possible, this method checks for + * write permissions and other operating system restrictions and will therefore usually provide a + * more accurate estimate of how much new data can actually be written than {@link #getFreeSpace}. + * + *

The returned number of available bytes is a hint, but not a guarantee, that it is possible + * to use most or any of these bytes. The number of unallocated bytes is most likely to be + * accurate immediately after this call. It is likely to be made inaccurate by any external I/O + * operations including those made on the system outside of this virtual machine. This method + * makes no guarantee that write operations to this file system will succeed. + * + *

On Android (and other Unix-based systems), this method returns the number of free bytes + * available to non-root users, regardless of whether you're actually running as root, and + * regardless of any quota or other restrictions that might apply to the user. (The {@code + * getFreeSpace} method returns the number of bytes potentially available to root.) + * + * @return The number of available bytes on the partition or 0L if the abstract pathname + * does not name a partition. On systems where this information is not available, this method + * will be equivalent to a call to {@link #getFreeSpace}. If there is no way to determine the + * current space left -1 is returned. + * @since 1.6 */ public long getUsableSpace() { if (isInvalid()) { @@ -1641,29 +1387,28 @@ public long getUsableSpace() { /* -- Temporary files -- */ private static class TempDirectory { - private TempDirectory() { } + private TempDirectory() {} // Android-changed: Don't cache java.io.tmpdir value // temporary directory location. - /* - private static final File tmpdir = new AmazeFile(AccessController - .doPrivileged(new GetPropertyAction("java.io.tmpdir"))); - static File location() { - return tmpdir; - } - */ + /* + private static final File tmpdir = new AmazeFile(AccessController + .doPrivileged(new GetPropertyAction("java.io.tmpdir"))); + static File location() { + return tmpdir; + } + */ // file name generation // private static final SecureRandom random = new SecureRandom(); static AmazeFile generateFile(String prefix, String suffix, AmazeFile dir, Random random) - throws IOException - { + throws IOException { // Android-changed: Use Math.randomIntInternal. This (pseudo) random number // is initialized post-fork long n = random.nextLong(); if (n == Long.MIN_VALUE) { - n = 0; // corner case + n = 0; // corner case } else { n = Math.abs(n); } @@ -1677,89 +1422,68 @@ static AmazeFile generateFile(String prefix, String suffix, AmazeFile dir, Rando if (!name.equals(f.getName()) || f.isInvalid()) { if (System.getSecurityManager() != null) throw new IOException("Unable to create temporary file"); - else - throw new IOException("Unable to create temporary file, " + f); + else throw new IOException("Unable to create temporary file, " + f); } return f; } } /** - *

Creates a new empty file in the specified directory, using the - * given prefix and suffix strings to generate its name. If this method - * returns successfully then it is guaranteed that: + * Creates a new empty file in the specified directory, using the given prefix and suffix strings + * to generate its name. If this method returns successfully then it is guaranteed that: * *

    - *
  1. The file denoted by the returned abstract pathname did not exist - * before this method was invoked, and - *
  2. Neither this method nor any of its variants will return the same - * abstract pathname again in the current invocation of the virtual - * machine. + *
  3. The file denoted by the returned abstract pathname did not exist before this method was + * invoked, and + *
  4. Neither this method nor any of its variants will return the same abstract pathname again + * in the current invocation of the virtual machine. *
* - * This method provides only part of a temporary-file facility. To arrange - * for a file created by this method to be deleted automatically, use the - * {@link #deleteOnExit} method. - * - *

The prefix argument must be at least three characters - * long. It is recommended that the prefix be a short, meaningful string - * such as "hjb" or "mail". The - * suffix argument may be null, in which case the - * suffix ".tmp" will be used. - * - *

To create the new file, the prefix and the suffix may first be - * adjusted to fit the limitations of the underlying platform. If the - * prefix is too long then it will be truncated, but its first three - * characters will always be preserved. If the suffix is too long then it - * too will be truncated, but if it begins with a period character - * ('.') then the period and the first three characters - * following it will always be preserved. Once these adjustments have been - * made the name of the new file will be generated by concatenating the - * prefix, five or more internally-generated characters, and the suffix. - * - *

If the directory argument is null then the - * system-dependent default temporary-file directory will be used. The - * default temporary-file directory is specified by the system property - * java.io.tmpdir. On UNIX systems the default value of this - * property is typically "/tmp" or "/var/tmp"; on - * Microsoft Windows systems it is typically "C:\\WINNT\\TEMP". A different - * value may be given to this system property when the Java virtual machine - * is invoked, but programmatic changes to this property are not guaranteed - * to have any effect upon the temporary directory used by this method. - * - * @param prefix The prefix string to be used in generating the file's - * name; must be at least three characters long - * - * @param suffix The suffix string to be used in generating the file's - * name; may be null, in which case the - * suffix ".tmp" will be used - * - * @param directory The directory in which the file is to be created, or - * null if the default temporary-file - * directory is to be used - * - * @return An abstract pathname denoting a newly-created empty file - * - * @throws IllegalArgumentException - * If the prefix argument contains fewer than three - * characters - * - * @throws IOException If a file could not be created - * + * This method provides only part of a temporary-file facility. To arrange for a file created by + * this method to be deleted automatically, use the {@link #deleteOnExit} method. + * + *

The prefix argument must be at least three characters long. It is recommended + * that the prefix be a short, meaningful string such as "hjb" or "mail" + * . The suffix argument may be null, in which case the suffix + * ".tmp" will be used. + * + *

To create the new file, the prefix and the suffix may first be adjusted to fit the + * limitations of the underlying platform. If the prefix is too long then it will be truncated, + * but its first three characters will always be preserved. If the suffix is too long then it too + * will be truncated, but if it begins with a period character ('.') then the period + * and the first three characters following it will always be preserved. Once these adjustments + * have been made the name of the new file will be generated by concatenating the prefix, five or + * more internally-generated characters, and the suffix. + * + *

If the directory argument is null then the system-dependent + * default temporary-file directory will be used. The default temporary-file directory is + * specified by the system property java.io.tmpdir. On UNIX systems the default value + * of this property is typically "/tmp" or "/var/tmp"; on Microsoft + * Windows systems it is typically "C:\\WINNT\\TEMP". A different value may be given + * to this system property when the Java virtual machine is invoked, but programmatic changes to + * this property are not guaranteed to have any effect upon the temporary directory used by this + * method. + * + * @param prefix The prefix string to be used in generating the file's name; must be at least + * three characters long + * @param suffix The suffix string to be used in generating the file's name; may be null + * , in which case the suffix ".tmp" will be used + * @param directory The directory in which the file is to be created, or null if the + * default temporary-file directory is to be used + * @return An abstract pathname denoting a newly-created empty file + * @throws IllegalArgumentException If the prefix argument contains fewer than three + * characters + * @throws IOException If a file could not be created * @since 1.2 */ - public AmazeFile createTempFile(String prefix, String suffix, - AmazeFile directory, Random random) - throws IOException - { - if (prefix.length() < 3) - throw new IllegalArgumentException("Prefix string too short"); - if (suffix == null) - suffix = ".tmp"; + public AmazeFile createTempFile(String prefix, String suffix, AmazeFile directory, Random random) + throws IOException { + if (prefix.length() < 3) throw new IllegalArgumentException("Prefix string too short"); + if (suffix == null) suffix = ".tmp"; // Android-changed: Handle java.io.tmpdir changes. - AmazeFile tmpdir = (directory != null) ? directory - : new AmazeFile(System.getProperty("java.io.tmpdir", ".")); + AmazeFile tmpdir = + (directory != null) ? directory : new AmazeFile(System.getProperty("java.io.tmpdir", ".")); AmazeFile f; do { f = TempDirectory.generateFile(prefix, suffix, tmpdir, random); @@ -1772,110 +1496,88 @@ public AmazeFile createTempFile(String prefix, String suffix, } /** - * Creates an empty file in the default temporary-file directory, using - * the given prefix and suffix to generate its name. Invoking this method - * is equivalent to invoking {@link #createTempFile(java.lang.String, + * Creates an empty file in the default temporary-file directory, using the given prefix and + * suffix to generate its name. Invoking this method is equivalent to invoking + * {@link #createTempFile(java.lang.String, * java.lang.String, java.io.File) * createTempFile(prefix, suffix, null)}. * - *

The {@link + *

The {@link * java.nio.file.Files#createTempFile(String,String,java.nio.file.attribute.FileAttribute[]) - * Files.createTempFile} method provides an alternative method to create an - * empty file in the temporary-file directory. Files created by that method - * may have more restrictive access permissions to files created by this - * method and so may be more suited to security-sensitive applications. - * - * @param prefix The prefix string to be used in generating the file's - * name; must be at least three characters long - * - * @param suffix The suffix string to be used in generating the file's - * name; may be null, in which case the - * suffix ".tmp" will be used - * - * @return An abstract pathname denoting a newly-created empty file - * - * @throws IllegalArgumentException - * If the prefix argument contains fewer than three - * characters - * - * @throws IOException If a file could not be created - * + * Files.createTempFile} method provides an alternative method to create an empty file in the + * temporary-file directory. Files created by that method may have more restrictive access + * permissions to files created by this method and so may be more suited to security-sensitive + * applications. + * + * @param prefix The prefix string to be used in generating the file's name; must be at least + * three characters long + * @param suffix The suffix string to be used in generating the file's name; may be null + * , in which case the suffix ".tmp" will be used + * @return An abstract pathname denoting a newly-created empty file + * @throws IllegalArgumentException If the prefix argument contains fewer than three + * characters + * @throws IOException If a file could not be created * @since 1.2 * @see java.nio.file.Files#createTempDirectory(String,FileAttribute[]) */ - public AmazeFile createTempFile(String prefix, String suffix, Random random) - throws IOException - { + public AmazeFile createTempFile(String prefix, String suffix, Random random) throws IOException { return createTempFile(prefix, suffix, null, random); } /* -- Basic infrastructure -- */ /** - * Compares two abstract pathnames lexicographically. The ordering - * defined by this method depends upon the underlying system. On UNIX - * systems, alphabetic case is significant in comparing pathnames; on Microsoft Windows - * systems it is not. - * - * @param pathname The abstract pathname to be compared to this abstract - * pathname - * - * @return Zero if the argument is equal to this abstract pathname, a - * value less than zero if this abstract pathname is - * lexicographically less than the argument, or a value greater - * than zero if this abstract pathname is lexicographically - * greater than the argument + * Compares two abstract pathnames lexicographically. The ordering defined by this method depends + * upon the underlying system. On UNIX systems, alphabetic case is significant in comparing + * pathnames; on Microsoft Windows systems it is not. * - * @since 1.2 + * @param pathname The abstract pathname to be compared to this abstract pathname + * @return Zero if the argument is equal to this abstract pathname, a value less than zero if this + * abstract pathname is lexicographically less than the argument, or a value greater than zero + * if this abstract pathname is lexicographically greater than the argument + * @since 1.2 */ public int compareTo(AmazeFile pathname) { return fs.compare(this, pathname); } /** - * Tests this abstract pathname for equality with the given object. - * Returns true if and only if the argument is not - * null and is an abstract pathname that denotes the same file - * or directory as this abstract pathname. Whether or not two abstract - * pathnames are equal depends upon the underlying system. On UNIX - * systems, alphabetic case is significant in comparing pathnames; on Microsoft Windows - * systems it is not. - * - * @param obj The object to be compared with this abstract pathname - * - * @return true if and only if the objects are the same; - * false otherwise + * Tests this abstract pathname for equality with the given object. Returns true if + * and only if the argument is not null and is an abstract pathname that denotes the + * same file or directory as this abstract pathname. Whether or not two abstract pathnames are + * equal depends upon the underlying system. On UNIX systems, alphabetic case is significant in + * comparing pathnames; on Microsoft Windows systems it is not. + * + * @param obj The object to be compared with this abstract pathname + * @return true if and only if the objects are the same; false otherwise */ public boolean equals(Object obj) { if (obj instanceof AmazeFile) { - return compareTo((AmazeFile)obj) == 0; + return compareTo((AmazeFile) obj) == 0; } return false; } /** - * Computes a hash code for this abstract pathname. Because equality of - * abstract pathnames is inherently system-dependent, so is the computation - * of their hash codes. On UNIX systems, the hash code of an abstract - * pathname is equal to the exclusive or of the hash code - * of its pathname string and the decimal value - * 1234321. On Microsoft Windows systems, the hash - * code is equal to the exclusive or of the hash code of - * its pathname string converted to lower case and the decimal - * value 1234321. Locale is not taken into account on - * lowercasing the pathname string. - * - * @return A hash code for this abstract pathname + * Computes a hash code for this abstract pathname. Because equality of abstract pathnames is + * inherently system-dependent, so is the computation of their hash codes. On UNIX systems, the + * hash code of an abstract pathname is equal to the exclusive or of the hash code of its + * pathname string and the decimal value 1234321. On Microsoft Windows systems, the + * hash code is equal to the exclusive or of the hash code of its pathname string + * converted to lower case and the decimal value 1234321. Locale is not taken into + * account on lowercasing the pathname string. + * + * @return A hash code for this abstract pathname */ public int hashCode() { return fs.hashCode(this); } /** - * Returns the pathname string of this abstract pathname. This is just the - * string returned by the {@link #getPath} method. + * Returns the pathname string of this abstract pathname. This is just the string returned by the + * {@link #getPath} method. * - * @return The string form of this abstract pathname + * @return The string form of this abstract pathname */ @NonNull public String toString() { @@ -1892,14 +1594,14 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(getPath()); } - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - public AmazeFile createFromParcel(Parcel in) { - return new AmazeFile(in.readString()); - } + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public AmazeFile createFromParcel(Parcel in) { + return new AmazeFile(in.readString()); + } - public AmazeFile[] newArray(int size) { - return new AmazeFile[size]; - } - }; + public AmazeFile[] newArray(int size) { + return new AmazeFile[size]; + } + }; } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileFilter.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileFilter.java index 3edf717acd..66d75635d7 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileFilter.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileFilter.java @@ -1,26 +1,21 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (C) 2014-2013 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * This file is part of Amaze File Manager. * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Amaze File Manager 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. * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * 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. * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ package com.amaze.filemanager.file_operations.filesystem.filetypes; @@ -30,9 +25,9 @@ /** * A filter for abstract pathnames. * - *

Instances of this interface may be passed to the {@link - * File#listFiles(java.io.FileFilter) listFiles(FileFilter)} method - * of the {@link java.io.File} class. + *

Instances of this interface may be passed to the {@link + * File#listFiles(java.io.FileFilter) listFiles(FileFilter)} method of the + * {@link java.io.File} class. * * @since 1.2 */ @@ -40,12 +35,10 @@ public interface AmazeFileFilter { /** - * Tests whether or not the specified abstract pathname should be - * included in a pathname list. + * Tests whether or not the specified abstract pathname should be included in a pathname list. * - * @param pathname The abstract pathname to be tested - * @return true if and only if pathname - * should be included + * @param pathname The abstract pathname to be tested + * @return true if and only if pathname should be included */ boolean accept(AmazeFile pathname); } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java index 63af050eac..3ccc383387 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java @@ -1,33 +1,26 @@ /* - * Copyright (C) 2014 The Android Open Source Project - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (C) 2014-2014 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * This file is part of Amaze File Manager. * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Amaze File Manager 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. * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * 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. * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ package com.amaze.filemanager.file_operations.filesystem.filetypes; import java.io.Closeable; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -37,144 +30,122 @@ public abstract class AmazeFileSystem implements Closeable { /* -- Normalization and construction -- */ - /** - * Is the path of this filesystem? - */ + /** Is the path of this filesystem? */ public abstract boolean isPathOfThisFilesystem(String path); - /** - * Return the local filesystem's name-separator character. - */ + /** Return the local filesystem's name-separator character. */ public abstract char getSeparator(); - /** - * Return the local filesystem's path-separator character. - */ + /** Return the local filesystem's path-separator character. */ public abstract char getPathSeparator(); /** - * Convert the given pathname string to normal form. If the string is - * already in normal form then it is simply returned. + * Convert the given pathname string to normal form. If the string is already in normal form then + * it is simply returned. */ public abstract String normalize(String path); /** - * Compute the length of this pathname string's prefix. The pathname - * string must be in normal form. + * Compute the length of this pathname string's prefix. The pathname string must be in normal + * form. */ public abstract int prefixLength(String path); /** - * Resolve the child pathname string against the parent. - * Both strings must be in normal form, and the result - * will be in normal form. + * Resolve the child pathname string against the parent. Both strings must be in normal form, and + * the result will be in normal form. */ public abstract String resolve(String parent, String child); /** - * Return the parent pathname string to be used when the parent-directory - * argument in one of the two-argument File constructors is the empty - * pathname. + * Return the parent pathname string to be used when the parent-directory argument in one of the + * two-argument File constructors is the empty pathname. */ public abstract String getDefaultParent(); /** - * Post-process the given URI path string if necessary. This is used on - * win32, e.g., to transform "/c:/foo" into "c:/foo". The path string - * still has slash separators; code in the File class will translate them - * after this method returns. + * Post-process the given URI path string if necessary. This is used on win32, e.g., to transform + * "/c:/foo" into "c:/foo". The path string still has slash separators; code in the File class + * will translate them after this method returns. */ public abstract String fromURIPath(String path); - /* -- Path operations -- */ - /** - * Tell whether or not the given abstract pathname is absolute. - */ + /** Tell whether or not the given abstract pathname is absolute. */ public abstract boolean isAbsolute(AmazeFile f); /** - * Resolve the given abstract pathname into absolute form. Invoked by the - * getAbsolutePath and getCanonicalPath methods in the F class. + * Resolve the given abstract pathname into absolute form. Invoked by the getAbsolutePath and + * getCanonicalPath methods in the F class. */ public abstract String resolve(AmazeFile f); public abstract String canonicalize(String path) throws IOException; - /* -- Attribute accessors -- */ /* Constants for simple boolean attributes */ - @Native - public static final int BA_EXISTS = 0x01; - @Native public static final int BA_REGULAR = 0x02; + @Native public static final int BA_EXISTS = 0x01; + @Native public static final int BA_REGULAR = 0x02; @Native public static final int BA_DIRECTORY = 0x04; - @Native public static final int BA_HIDDEN = 0x08; + @Native public static final int BA_HIDDEN = 0x08; /** - * Return the simple boolean attributes for the file or directory denoted - * by the given abstract pathname, or zero if it does not exist or some - * other I/O error occurs. + * Return the simple boolean attributes for the file or directory denoted by the given abstract + * pathname, or zero if it does not exist or some other I/O error occurs. */ public abstract int getBooleanAttributes(AmazeFile f); - @Native public static final int ACCESS_READ = 0x04; - @Native public static final int ACCESS_WRITE = 0x02; + @Native public static final int ACCESS_READ = 0x04; + @Native public static final int ACCESS_WRITE = 0x02; @Native public static final int ACCESS_EXECUTE = 0x01; // Android-added: b/25878034, to support F.exists() reimplementation. public static final int ACCESS_CHECK_EXISTS = 0x08; /** - * Check whether the file or directory denoted by the given abstract - * pathname may be accessed by this process. The second argument specifies - * which access, ACCESS_READ, ACCESS_WRITE or ACCESS_EXECUTE, to check. - * Return false if access is denied or an I/O error occurs + * Check whether the file or directory denoted by the given abstract pathname may be accessed by + * this process. The second argument specifies which access, ACCESS_READ, ACCESS_WRITE or + * ACCESS_EXECUTE, to check. Return false if access is denied or an I/O error occurs */ public abstract boolean checkAccess(AmazeFile f, int access); /** - * Set on or off the access permission (to owner only or to all) to the file - * or directory denoted by the given abstract pathname, based on the parameters - * enable, access and oweronly. + * Set on or off the access permission (to owner only or to all) to the file or directory denoted + * by the given abstract pathname, based on the parameters enable, access and oweronly. */ public abstract boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly); /** - * Return the time at which the file or directory denoted by the given - * abstract pathname was last modified, or zero if it does not exist or - * some other I/O error occurs. + * Return the time at which the file or directory denoted by the given abstract pathname was last + * modified, or zero if it does not exist or some other I/O error occurs. */ public abstract long getLastModifiedTime(AmazeFile f); /** - * Return the length in bytes of the file denoted by the given abstract - * pathname, or zero if it does not exist, is a directory, or some other - * I/O error occurs. + * Return the length in bytes of the file denoted by the given abstract pathname, or zero if it + * does not exist, is a directory, or some other I/O error occurs. */ public abstract long getLength(AmazeFile f) throws IOException; - /* -- File operations -- */ /** - * Create a new empty file with the given pathname. Return - * true if the file was created and false if a - * file or directory with the given pathname already exists. Throw an - * IOException if an I/O error occurs. + * Create a new empty file with the given pathname. Return true if the file was + * created and false if a file or directory with the given pathname already exists. + * Throw an IOException if an I/O error occurs. */ - public abstract boolean createFileExclusively(String pathname) - throws IOException; + public abstract boolean createFileExclusively(String pathname) throws IOException; /** - * Delete the file or directory denoted by the given abstract pathname, - * returning true if and only if the operation succeeds. + * Delete the file or directory denoted by the given abstract pathname, returning true + * if and only if the operation succeeds. */ public abstract boolean delete(AmazeFile f); /** - * List the elements of the directory denoted by the given abstract - * pathname. Return an array of strings naming the elements of the - * directory if successful; otherwise, return null. + * List the elements of the directory denoted by the given abstract pathname. Return an array of + * strings naming the elements of the directory if successful; otherwise, return null + * . */ public abstract String[] list(AmazeFile f); @@ -183,65 +154,55 @@ public abstract boolean createFileExclusively(String pathname) public abstract OutputStream getOutputStream(AmazeFile f); /** - * Create a new directory denoted by the given abstract pathname, - * returning true if and only if the operation succeeds. + * Create a new directory denoted by the given abstract pathname, returning true if + * and only if the operation succeeds. */ public abstract boolean createDirectory(AmazeFile f); /** - * Rename the file or directory denoted by the first abstract pathname to - * the second abstract pathname, returning true if and only if - * the operation succeeds. + * Rename the file or directory denoted by the first abstract pathname to the second abstract + * pathname, returning true if and only if the operation succeeds. */ public abstract boolean rename(AmazeFile f1, AmazeFile f2); /** - * Set the last-modified time of the file or directory denoted by the - * given abstract pathname, returning true if and only if the - * operation succeeds. + * Set the last-modified time of the file or directory denoted by the given abstract pathname, + * returning true if and only if the operation succeeds. */ public abstract boolean setLastModifiedTime(AmazeFile f, long time); /** - * Mark the file or directory denoted by the given abstract pathname as - * read-only, returning true if and only if the operation - * succeeds. + * Mark the file or directory denoted by the given abstract pathname as read-only, returning + * true if and only if the operation succeeds. */ public abstract boolean setReadOnly(AmazeFile f); - /* -- Filesystem interface -- */ - /** - * List the available filesystem roots. - */ + /** List the available filesystem roots. */ public abstract AmazeFile[] listRoots(); /* -- Disk usage -- */ - @Native public static final int SPACE_TOTAL = 0; - @Native public static final int SPACE_FREE = 1; + @Native public static final int SPACE_TOTAL = 0; + @Native public static final int SPACE_FREE = 1; @Native public static final int SPACE_USABLE = 2; public abstract long getSpace(AmazeFile f, int t); /* -- Basic infrastructure -- */ - /** - * Compare two abstract pathnames lexicographically. - */ + /** Compare two abstract pathnames lexicographically. */ public abstract int compare(AmazeFile f1, AmazeFile f2); - /** - * Compute the hash code of an abstract pathname. - */ + /** Compute the hash code of an abstract pathname. */ public abstract int hashCode(AmazeFile f); // Flags for enabling/disabling performance optimizations for file // name canonicalization // Android-changed: Disabled caches for security reasons (b/62301183) - //static boolean useCanonCaches = true; - //static boolean useCanonPrefixCache = true; - static boolean useCanonCaches = false; + // static boolean useCanonCaches = true; + // static boolean useCanonPrefixCache = true; + static boolean useCanonCaches = false; static boolean useCanonPrefixCache = false; private static boolean getBooleanProperty(String prop, boolean defaultVal) { @@ -255,10 +216,8 @@ private static boolean getBooleanProperty(String prop, boolean defaultVal) { } static { - useCanonCaches = getBooleanProperty("sun.io.useCanonCaches", - useCanonCaches); - useCanonPrefixCache = getBooleanProperty("sun.io.useCanonPrefixCache", - useCanonPrefixCache); + useCanonCaches = getBooleanProperty("sun.io.useCanonCaches", useCanonCaches); + useCanonPrefixCache = getBooleanProperty("sun.io.useCanonPrefixCache", useCanonPrefixCache); } /* @@ -354,5 +313,4 @@ public static String basicUnixResolve(String parent, String child) { public static int basicUnixHashCode(String path) { return path.hashCode() ^ 1234321; } - } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilenameFilter.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilenameFilter.java index 1251c4ec9e..899f2a1fc7 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilenameFilter.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilenameFilter.java @@ -1,26 +1,21 @@ /* - * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (C) 2014-2013 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * This file is part of Amaze File Manager. * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Amaze File Manager 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. * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * 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. * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ package com.amaze.filemanager.file_operations.filesystem.filetypes; @@ -29,27 +24,25 @@ // @see java.awt.FileDialog#setFilenameFilter(java.io.FilenameFilter) /** - * Instances of classes that implement this interface are used to - * filter filenames. These instances are used to filter directory - * listings in the list method of class - * File, and by the Abstract Window Toolkit's file - * dialog component. + * Instances of classes that implement this interface are used to filter filenames. These instances + * are used to filter directory listings in the list method of class File, + * and by the Abstract Window Toolkit's file dialog component. * - * @author Arthur van Hoff - * @author Jonathan Payne - * @see java.io.File - * @see java.io.File#list(java.io.FilenameFilter) - * @since JDK1.0 + * @author Arthur van Hoff + * @author Jonathan Payne + * @see java.io.File + * @see java.io.File#list(java.io.FilenameFilter) + * @since JDK1.0 */ @FunctionalInterface public interface AmazeFilenameFilter { /** * Tests if a specified file should be included in a file list. * - * @param dir the directory in which the file was found. - * @param name the name of the file. - * @return true if and only if the name should be - * included in the file list; false otherwise. + * @param dir the directory in which the file was found. + * @param name the name of the file. + * @return true if and only if the name should be included in the file list; + * false otherwise. */ boolean accept(AmazeFile dir, String name); } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java index 690bf91c66..4bc48e5663 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -1,18 +1,28 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.smb; import static com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.SMB_URI_PREFIX; import static com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.clearBaseContexts; -import android.net.Uri; -import android.text.TextUtils; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -20,6 +30,16 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; + +import android.net.Uri; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import jcifs.SmbConstants; import jcifs.smb.NtlmPasswordAuthenticator; import jcifs.smb.SmbException; @@ -27,12 +47,9 @@ import kotlin.NotImplementedError; /** - * Root is - * "smb://:@" - * or "smb://" - * or "smb://:@/?disableIpcSigningCheck=true" - * or "smb:///?disableIpcSigningCheck=true" - * Relative paths are not supported + * Root is "smb://:@" or "smb://" or + * "smb://:@/?disableIpcSigningCheck=true" or + * "smb:///?disableIpcSigningCheck=true" Relative paths are not supported */ public class SmbAmazeFileSystem extends AmazeFileSystem { public static final String TAG = SmbAmazeFileSystem.class.getSimpleName(); @@ -41,10 +58,12 @@ public class SmbAmazeFileSystem extends AmazeFileSystem { public static final char SEPARATOR = '/'; public static final String PARAM_DISABLE_IPC_SIGNING_CHECK = "disableIpcSigningCheck"; - private static final Pattern IPv4_PATTERN = Pattern.compile("[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+"); - private static final Pattern METADATA_PATTERN = Pattern.compile("([?][a-zA-Z]+=(\")?[a-zA-Z]+(\")?)+"); + private static final Pattern IPv4_PATTERN = + Pattern.compile("[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+"); + private static final Pattern METADATA_PATTERN = + Pattern.compile("([?][a-zA-Z]+=(\")?[a-zA-Z]+(\")?)+"); - private SmbAmazeFileSystem() { } + private SmbAmazeFileSystem() {} public static final SmbAmazeFileSystem INSTANCE = new SmbAmazeFileSystem(); @@ -67,7 +86,7 @@ public char getPathSeparator() { public String normalize(String pathname) { try { String canonical = canonicalize(pathname); - return canonical.substring(0, canonical.length()-1); + return canonical.substring(0, canonical.length() - 1); } catch (MalformedURLException e) { Log.e(TAG, "Error getting canonical path for SMB file", e); return null; @@ -81,7 +100,7 @@ public int prefixLength(String path) { } Matcher matcherMetadata = METADATA_PATTERN.matcher(path); - if(matcherMetadata.find()) { + if (matcherMetadata.find()) { return matcherMetadata.end(); } @@ -99,9 +118,7 @@ public String resolve(String parent, String child) { return prefix + basicUnixResolve(simplePathParent, simplePathChild); } - /** - * This makes no sense for SMB - */ + /** This makes no sense for SMB */ @Override public String getDefaultParent() { throw new IllegalStateException("There is no default SMB path"); @@ -204,17 +221,17 @@ public long getLength(AmazeFile f) throws SmbException, MalformedURLException { } private static SmbFile create(String path) throws MalformedURLException { - if(!path.endsWith(SEPARATOR + "")) { + if (!path.endsWith(SEPARATOR + "")) { path = path + SEPARATOR; } Uri uri = Uri.parse(path); boolean disableIpcSigningCheck = - Boolean.parseBoolean(uri.getQueryParameter(PARAM_DISABLE_IPC_SIGNING_CHECK)); + Boolean.parseBoolean(uri.getQueryParameter(PARAM_DISABLE_IPC_SIGNING_CHECK)); String userInfo = uri.getUserInfo(); return new SmbFile( - path.indexOf('?') < 0 ? path : path.substring(0, path.indexOf('?')), - CifsContexts.createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) - .withCredentials(createFrom(userInfo))); + path.indexOf('?') < 0 ? path : path.substring(0, path.indexOf('?')), + CifsContexts.createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) + .withCredentials(createFrom(userInfo))); } /** @@ -227,8 +244,7 @@ private static SmbFile create(String path) throws MalformedURLException { * for you already * @return {@link NtlmPasswordAuthenticator} instance */ - private static @NonNull - NtlmPasswordAuthenticator createFrom(@Nullable String userInfo) { + private static @NonNull NtlmPasswordAuthenticator createFrom(@Nullable String userInfo) { if (!TextUtils.isEmpty(userInfo)) { String dom = null; String user = null; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java index 79e602f336..b73ad2ef4e 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java @@ -29,8 +29,6 @@ import android.webkit.MimeTypeMap; -import jcifs.smb.SmbFile; - public class StreamSource extends RandomAccessStream { protected String mime; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/Streamer.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/Streamer.java index b57a309486..3eb76ee6af 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/Streamer.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/Streamer.java @@ -25,10 +25,10 @@ import java.util.Properties; import java.util.regex.Pattern; -import android.util.Log; - import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import android.util.Log; + import jcifs.smb.SmbFile; /** Created by Arpit on 06-07-2015. */ diff --git a/file_operations/src/test/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSourceTest.java b/file_operations/src/test/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSourceTest.java index 203805614f..33d849a13d 100644 --- a/file_operations/src/test/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSourceTest.java +++ b/file_operations/src/test/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSourceTest.java @@ -47,8 +47,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; -import jcifs.smb.SmbFile; - /** Created by Rustam Khadipash on 30/3/2018. */ @RunWith(AndroidJUnit4.class) @Config( From 94b9cc6e9d489e71b1d75f0487d15ce153074753 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Mon, 11 Oct 2021 12:57:27 -0300 Subject: [PATCH 04/46] Add nullability to AmazeFile and related classes --- .../filesystem/filetypes/AmazeFile.java | 157 ++++++++---------- .../filesystem/filetypes/AmazeFileSystem.java | 39 +++-- .../filetypes/smb/SmbAmazeFileSystem.java | 25 ++- 3 files changed, 113 insertions(+), 108 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 3fa2f9e7f3..5f8826333e 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -28,8 +28,10 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; @@ -38,10 +40,10 @@ import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import kotlin.Deprecated; import kotlin.NotImplementedError; -import kotlin.random.Random; // Android-added: Info about UTF-8 usage in filenames. /** @@ -134,39 +136,14 @@ public class AmazeFile implements Parcelable, Comparable { * This abstract pathname's normalized pathname string. A normalized pathname string uses the * default name-separator character and does not contain any duplicate or redundant separators. */ - private final String path; - - /** Enum type that indicates the status of a file path. */ - private static enum PathStatus { - INVALID, - CHECKED - }; + @NonNull private final String path; /** The flag indicating whether the file path is invalid. */ private transient PathStatus status = null; - /** - * Check if the file has an invalid path. Currently, the inspection of a file path is very - * limited, and it only covers Nul character check. Returning true means the path is definitely - * invalid/garbage. But returning false does not guarantee that the path is valid. - * - * @return true if the file path is invalid. - */ - final boolean isInvalid() { - if (status == null) { - status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED : PathStatus.INVALID; - } - return status == PathStatus.INVALID; - } - /** The length of this abstract pathname's prefix, or zero if it has no prefix. */ private final transient int prefixLength; - /** Returns the length of this abstract pathname's prefix. For use by FileSystem classes. */ - int getPrefixLength() { - return prefixLength; - } - /** * The system-dependent default name-separator character. This field is initialized to contain the * first character of the value of the system property file.separator. On UNIX @@ -200,10 +177,35 @@ int getPrefixLength() { */ public final String pathSeparator; + /** Enum type that indicates the status of a file path. */ + private enum PathStatus { + INVALID, + CHECKED + }; + + /** + * Check if the file has an invalid path. Currently, the inspection of a file path is very + * limited, and it only covers Nul character check. Returning true means the path is definitely + * invalid/garbage. But returning false does not guarantee that the path is valid. + * + * @return true if the file path is invalid. + */ + final boolean isInvalid() { + if (status == null) { + status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED : PathStatus.INVALID; + } + return status == PathStatus.INVALID; + } + + /** Returns the length of this abstract pathname's prefix. For use by FileSystem classes. */ + int getPrefixLength() { + return prefixLength; + } + /* -- Constructors -- */ /** Internal constructor for already-normalized pathname strings. */ - private AmazeFile(String pathname, int prefixLength) { + private AmazeFile(@NonNull String pathname, int prefixLength) { loadFilesystem(pathname); separatorChar = fs.getSeparator(); separator = "" + separatorChar; @@ -217,8 +219,7 @@ private AmazeFile(String pathname, int prefixLength) { * Internal constructor for already-normalized pathname strings. The parameter order is used to * disambiguate this method from the public(AmazeFile, String) constructor. */ - private AmazeFile(String child, AmazeFile parent) { - assert parent.path != null; + private AmazeFile(@NonNull String child, @NonNull AmazeFile parent) { assert (!parent.path.equals("")); loadFilesystem(parent.path); separatorChar = fs.getSeparator(); @@ -235,12 +236,8 @@ private AmazeFile(String child, AmazeFile parent) { * abstract pathname. * * @param pathname A pathname string - * @throws NullPointerException If the pathname argument is null */ - public AmazeFile(String pathname) { - if (pathname == null) { - throw new NullPointerException(); - } + public AmazeFile(@NonNull String pathname) { loadFilesystem(pathname); separatorChar = fs.getSeparator(); separator = "" + separatorChar; @@ -276,12 +273,8 @@ public AmazeFile(String pathname) { * * @param parent The parent pathname string * @param child The child pathname string - * @throws NullPointerException If child is null */ - public AmazeFile(String parent, String child) { - if (child == null) { - throw new NullPointerException(); - } + public AmazeFile(@Nullable String parent, @NonNull String child) { // BEGIN Android-changed: b/25859957, app-compat; don't substitute empty parent. if (parent != null && !parent.isEmpty()) { loadFilesystem(parent); @@ -321,12 +314,8 @@ public AmazeFile(String parent, String child) { * * @param parent The parent abstract pathname * @param child The child pathname string - * @throws NullPointerException If child is null */ - public AmazeFile(AmazeFile parent, String child) { - if (child == null) { - throw new NullPointerException(); - } + public AmazeFile(@Nullable AmazeFile parent, @NonNull String child) { if (parent != null) { loadFilesystem(parent.getPath()); separatorChar = fs.getSeparator(); @@ -374,13 +363,12 @@ public AmazeFile(AmazeFile parent, String child) { * * @param uri An absolute, hierarchical URI with a scheme equal to "file", a non-empty * path component, and undefined authority, query, and fragment components - * @throws NullPointerException If uri is null * @throws IllegalArgumentException If the preconditions on the parameter do not hold * @see #toURI() * @see java.net.URI * @since 1.4 */ - public AmazeFile(URI uri) { + public AmazeFile(@NonNull URI uri) { // Check our many preconditions if (!uri.isAbsolute()) throw new IllegalArgumentException("URI is not absolute"); @@ -427,6 +415,7 @@ private void loadFilesystem(String path) { * @return The name of the file or directory denoted by this abstract pathname, or the empty * string if this pathname's name sequence is empty */ + @NonNull public String getName() { int index = path.lastIndexOf(separatorChar); if (index < prefixLength) return path.substring(prefixLength); @@ -444,11 +433,13 @@ public String getName() { * @return The pathname string of the parent directory named by this abstract pathname, or * null if this pathname does not name a parent */ + @Nullable public String getParent() { int index = path.lastIndexOf(separatorChar); if (index < prefixLength) { - if ((prefixLength > 0) && (path.length() > prefixLength)) + if ((prefixLength > 0) && (path.length() > prefixLength)) { return path.substring(0, prefixLength); + } return null; } return path.substring(0, index); @@ -464,11 +455,13 @@ public String getParent() { * * @return The abstract pathname of the parent directory named by this abstract pathname, or * null if this pathname does not name a parent - * @since 1.2 */ + @Nullable public AmazeFile getParentFile() { String p = this.getParent(); - if (p == null) return null; + if (p == null) { + return null; + } return new AmazeFile(p, this.prefixLength); } @@ -478,6 +471,7 @@ public AmazeFile getParentFile() { * * @return The string form of this abstract pathname */ + @NonNull public String getPath() { return path; } @@ -508,6 +502,7 @@ public boolean isAbsolute() { * pathname * @see java.io.File#isAbsolute() */ + @NonNull public String getAbsolutePath() { return fs.resolve(this); } @@ -518,8 +513,8 @@ public String getAbsolutePath() { * * @return The absolute abstract pathname denoting the same file or directory as this abstract * pathname - * @since 1.2 */ + @NonNull public AmazeFile getAbsoluteFile() { String absPath = getAbsolutePath(); return new AmazeFile(absPath, fs.prefixLength(absPath)); @@ -549,6 +544,7 @@ public AmazeFile getAbsoluteFile() { * @since JDK1.1 * @see Path#toRealPath */ + @NonNull public String getCanonicalPath() throws IOException { if (isInvalid()) { throw new IOException("Invalid file path"); @@ -564,14 +560,15 @@ public String getCanonicalPath() throws IOException { * pathname * @throws IOException If an I/O error occurs, which is possible because the construction of the * canonical pathname may require filesystem queries - * @since 1.2 * @see Path#toRealPath */ + @NonNull public AmazeFile getCanonicalFile() throws IOException { String canonPath = getCanonicalPath(); return new AmazeFile(canonPath, fs.prefixLength(canonPath)); } + @NonNull private static String slashify(String path, boolean isDirectory) { String p = path; if (File.separatorChar != '/') p = p.replace(File.separatorChar, '/'); @@ -618,6 +615,7 @@ private static String slashify(String path, boolean isDirectory) { * @since 1.4 */ @Deprecated(message = "Left for reference, do not use") + @NonNull public URI toURI() { try { AmazeFile f = getAbsoluteFile(); @@ -724,7 +722,6 @@ public boolean isFile() { * * @return true if and only if the file denoted by this abstract pathname is hidden * according to the conventions of the underlying platform - * @since 1.2 */ public boolean isHidden() { if (isInvalid()) { @@ -788,7 +785,6 @@ public long length() throws IOException { * @return true if the named file does not exist and was successfully created; * false if the named file already exists * @throws IOException If an I/O error occurred - * @since 1.2 */ public boolean createNewFile() throws IOException { if (isInvalid()) { @@ -845,7 +841,6 @@ public boolean delete() { * * * @see #delete - * @since 1.2 */ public void deleteOnExit() { if (isInvalid()) { @@ -875,6 +870,7 @@ public void deleteOnExit() { * abstract pathname. The array will be empty if the directory is empty. Returns {@code null} * if this abstract pathname does not denote a directory, or if an I/O error occurs. */ + @Nullable public String[] list() { if (isInvalid()) { return null; @@ -899,6 +895,7 @@ public String[] list() { * if this abstract pathname does not denote a directory, or if an I/O error occurs. * @see java.nio.file.Files#newDirectoryStream(Path,String) */ + @Nullable public String[] list(AmazeFilenameFilter filter) { String names[] = list(); if ((names == null) || (filter == null)) { @@ -937,8 +934,8 @@ public String[] list(AmazeFilenameFilter filter) { * denoted by this abstract pathname. The array will be empty if the directory is empty. * Returns {@code null} if this abstract pathname does not denote a directory, or if an I/O * error occurs. - * @since 1.2 */ + @Nullable public AmazeFile[] listFiles() { String[] ss = list(); if (ss == null) return null; @@ -965,9 +962,9 @@ public AmazeFile[] listFiles() { * denoted by this abstract pathname. The array will be empty if the directory is empty. * Returns {@code null} if this abstract pathname does not denote a directory, or if an I/O * error occurs. - * @since 1.2 * @see java.nio.file.Files#newDirectoryStream(Path,String) */ + @Nullable public AmazeFile[] listFiles(AmazeFilenameFilter filter) { String ss[] = list(); if (ss == null) return null; @@ -991,9 +988,9 @@ public AmazeFile[] listFiles(AmazeFilenameFilter filter) { * denoted by this abstract pathname. The array will be empty if the directory is empty. * Returns {@code null} if this abstract pathname does not denote a directory, or if an I/O * error occurs. - * @since 1.2 * @see java.nio.file.Files#newDirectoryStream(Path,java.nio.file.DirectoryStream.Filter) */ + @Nullable public AmazeFile[] listFiles(AmazeFileFilter filter) { String ss[] = list(); if (ss == null) return null; @@ -1005,10 +1002,12 @@ public AmazeFile[] listFiles(AmazeFileFilter filter) { return files.toArray(new AmazeFile[files.size()]); } + @NonNull public InputStream getInputStream() { return fs.getInputStream(this); } + @NonNull public OutputStream getOutputStream() { return fs.getOutputStream(this); } @@ -1074,12 +1073,8 @@ public boolean mkdirs() { * * @param dest The new abstract pathname for the named file * @return true if and only if the renaming succeeded; false otherwise - * @throws NullPointerException If parameter dest is null */ - public boolean renameTo(AmazeFile dest) { - if (dest == null) { - throw new NullPointerException(); - } + public boolean renameTo(@NonNull AmazeFile dest) { if (this.isInvalid() || dest.isInvalid()) { return false; } @@ -1099,7 +1094,6 @@ public boolean renameTo(AmazeFile dest) { * January 1, 1970) * @return true if and only if the operation succeeded; false otherwise * @throws IllegalArgumentException If the argument is negative - * @since 1.2 */ public boolean setLastModified(long time) { if (time < 0) throw new IllegalArgumentException("Negative time"); @@ -1118,7 +1112,6 @@ public boolean setLastModified(long time) { * deleted depends upon the underlying system. * * @return true if and only if the operation succeeded; false otherwise - * @since 1.2 */ public boolean setReadOnly() { if (isInvalid()) { @@ -1387,22 +1380,11 @@ public long getUsableSpace() { /* -- Temporary files -- */ private static class TempDirectory { - private TempDirectory() {} - - // Android-changed: Don't cache java.io.tmpdir value - // temporary directory location. - /* - private static final File tmpdir = new AmazeFile(AccessController - .doPrivileged(new GetPropertyAction("java.io.tmpdir"))); - static File location() { - return tmpdir; - } - */ - // file name generation - // private static final SecureRandom random = new SecureRandom(); - static AmazeFile generateFile(String prefix, String suffix, AmazeFile dir, Random random) - throws IOException { + private static final SecureRandom random = new SecureRandom(); + + @NonNull + static AmazeFile generateFile(String prefix, String suffix, AmazeFile dir) throws IOException { // Android-changed: Use Math.randomIntInternal. This (pseudo) random number // is initialized post-fork @@ -1474,9 +1456,9 @@ static AmazeFile generateFile(String prefix, String suffix, AmazeFile dir, Rando * @throws IllegalArgumentException If the prefix argument contains fewer than three * characters * @throws IOException If a file could not be created - * @since 1.2 */ - public AmazeFile createTempFile(String prefix, String suffix, AmazeFile directory, Random random) + @NonNull + public AmazeFile createTempFile(String prefix, String suffix, AmazeFile directory) throws IOException { if (prefix.length() < 3) throw new IllegalArgumentException("Prefix string too short"); if (suffix == null) suffix = ".tmp"; @@ -1486,7 +1468,7 @@ public AmazeFile createTempFile(String prefix, String suffix, AmazeFile director (directory != null) ? directory : new AmazeFile(System.getProperty("java.io.tmpdir", ".")); AmazeFile f; do { - f = TempDirectory.generateFile(prefix, suffix, tmpdir, random); + f = TempDirectory.generateFile(prefix, suffix, tmpdir); } while ((fs.getBooleanAttributes(f) & AmazeFileSystem.BA_EXISTS) != 0); if (!fs.createFileExclusively(f.getPath())) @@ -1517,11 +1499,11 @@ public AmazeFile createTempFile(String prefix, String suffix, AmazeFile director * @throws IllegalArgumentException If the prefix argument contains fewer than three * characters * @throws IOException If a file could not be created - * @since 1.2 * @see java.nio.file.Files#createTempDirectory(String,FileAttribute[]) */ - public AmazeFile createTempFile(String prefix, String suffix, Random random) throws IOException { - return createTempFile(prefix, suffix, null, random); + @NonNull + public AmazeFile createTempFile(String prefix, String suffix) throws IOException { + return createTempFile(prefix, suffix, null); } /* -- Basic infrastructure -- */ @@ -1535,7 +1517,6 @@ public AmazeFile createTempFile(String prefix, String suffix, Random random) thr * @return Zero if the argument is equal to this abstract pathname, a value less than zero if this * abstract pathname is lexicographically less than the argument, or a value greater than zero * if this abstract pathname is lexicographically greater than the argument - * @since 1.2 */ public int compareTo(AmazeFile pathname) { return fs.compare(this, pathname); @@ -1597,7 +1578,7 @@ public void writeToParcel(Parcel dest, int flags) { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public AmazeFile createFromParcel(Parcel in) { - return new AmazeFile(in.readString()); + return new AmazeFile(Objects.requireNonNull(in.readString())); } public AmazeFile[] newArray(int size) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java index 3ccc383387..e98fec120c 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java @@ -26,12 +26,15 @@ import java.io.OutputStream; import java.lang.annotation.Native; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + public abstract class AmazeFileSystem implements Closeable { /* -- Normalization and construction -- */ /** Is the path of this filesystem? */ - public abstract boolean isPathOfThisFilesystem(String path); + public abstract boolean isPathOfThisFilesystem(@NonNull String path); /** Return the local filesystem's name-separator character. */ public abstract char getSeparator(); @@ -43,24 +46,27 @@ public abstract class AmazeFileSystem implements Closeable { * Convert the given pathname string to normal form. If the string is already in normal form then * it is simply returned. */ - public abstract String normalize(String path); + @NonNull + public abstract String normalize(@NonNull String path); /** * Compute the length of this pathname string's prefix. The pathname string must be in normal * form. */ - public abstract int prefixLength(String path); + public abstract int prefixLength(@NonNull String path); /** * Resolve the child pathname string against the parent. Both strings must be in normal form, and * the result will be in normal form. */ + @NonNull public abstract String resolve(String parent, String child); /** * Return the parent pathname string to be used when the parent-directory argument in one of the * two-argument File constructors is the empty pathname. */ + @NonNull public abstract String getDefaultParent(); /** @@ -68,7 +74,8 @@ public abstract class AmazeFileSystem implements Closeable { * "/c:/foo" into "c:/foo". The path string still has slash separators; code in the File class * will translate them after this method returns. */ - public abstract String fromURIPath(String path); + @NonNull + public abstract String fromURIPath(@NonNull String path); /* -- Path operations -- */ @@ -79,8 +86,10 @@ public abstract class AmazeFileSystem implements Closeable { * Resolve the given abstract pathname into absolute form. Invoked by the getAbsolutePath and * getCanonicalPath methods in the F class. */ + @NonNull public abstract String resolve(AmazeFile f); + @NonNull public abstract String canonicalize(String path) throws IOException; /* -- Attribute accessors -- */ @@ -147,10 +156,13 @@ public abstract class AmazeFileSystem implements Closeable { * strings naming the elements of the directory if successful; otherwise, return null * . */ + @Nullable public abstract String[] list(AmazeFile f); + @Nullable public abstract InputStream getInputStream(AmazeFile f); + @Nullable public abstract OutputStream getOutputStream(AmazeFile f); /** @@ -207,12 +219,10 @@ public abstract class AmazeFileSystem implements Closeable { private static boolean getBooleanProperty(String prop, boolean defaultVal) { String val = System.getProperty(prop); - if (val == null) return defaultVal; - if (val.equalsIgnoreCase("true")) { - return true; - } else { - return false; + if (val == null) { + return defaultVal; } + return val.equalsIgnoreCase("true"); } static { @@ -296,17 +306,22 @@ public static String simpleUnixNormalize(String pathname) { * questions. */ // Invariant: Both |parent| and |child| are normalized paths. - public static String basicUnixResolve(String parent, String child) { + @NonNull + public static String basicUnixResolve(@NonNull String parent, @NonNull String child) { if (child.isEmpty() || child.equals("/")) { return parent; } if (child.charAt(0) == '/') { - if (parent.equals("/")) return child; + if (parent.equals("/")) { + return child; + } return parent + child; } - if (parent.equals("/")) return parent + child; + if (parent.equals("/")) { + return parent + child; + } return parent + '/' + child; } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java index 4bc48e5663..8e3a8f31aa 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -68,7 +68,7 @@ private SmbAmazeFileSystem() {} public static final SmbAmazeFileSystem INSTANCE = new SmbAmazeFileSystem(); @Override - public boolean isPathOfThisFilesystem(String path) { + public boolean isPathOfThisFilesystem(@NonNull String path) { return path.startsWith(SmbAmazeFileSystem.PREFIX); } @@ -82,19 +82,21 @@ public char getPathSeparator() { return 0; } + @NonNull @Override - public String normalize(String pathname) { + public String normalize(@NonNull String pathname) { + String canonical; try { - String canonical = canonicalize(pathname); - return canonical.substring(0, canonical.length() - 1); + canonical = canonicalize(pathname); } catch (MalformedURLException e) { - Log.e(TAG, "Error getting canonical path for SMB file", e); - return null; + Log.e(TAG, "Error getting SMB file canonical path", e); + canonical = pathname.substring(0, prefixLength(pathname)) + "/"; } + return canonical.substring(0, canonical.length() - 1); } @Override - public int prefixLength(String path) { + public int prefixLength(@NonNull String path) { if (path.length() == 0) { return 0; } @@ -109,6 +111,7 @@ public int prefixLength(String path) { return matcher.end(); } + @NonNull @Override public String resolve(String parent, String child) { final String prefix = parent.substring(0, prefixLength(parent)); @@ -119,13 +122,15 @@ public String resolve(String parent, String child) { } /** This makes no sense for SMB */ + @NonNull @Override public String getDefaultParent() { throw new IllegalStateException("There is no default SMB path"); } + @NonNull @Override - public String fromURIPath(String path) { + public String fromURIPath(@NonNull String path) { throw new NotImplementedError(); } @@ -134,6 +139,7 @@ public boolean isAbsolute(AmazeFile f) { return f.getPath().startsWith(PREFIX); } + @NonNull @Override public String resolve(AmazeFile f) { if (isAbsolute(f)) { @@ -143,6 +149,7 @@ public String resolve(AmazeFile f) { throw new IllegalArgumentException("Relative paths are not supported"); } + @NonNull @Override public String canonicalize(String path) throws MalformedURLException { return create(path).getCanonicalPath(); @@ -304,6 +311,7 @@ public String[] list(AmazeFile f) { return list; } + @Nullable @Override public InputStream getInputStream(AmazeFile f) { try { @@ -314,6 +322,7 @@ public InputStream getInputStream(AmazeFile f) { } } + @Nullable @Override public OutputStream getOutputStream(AmazeFile f) { try { From f18d031f319f995a08bdab12c3c2c389f3e8ffba Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Mon, 11 Oct 2021 14:05:31 -0300 Subject: [PATCH 05/46] Fix tests for new SMB FileSystem --- .../filemanager/filesystem/Operations.java | 20 +++++++- .../asynctasks/AbstractDeleteTaskTestBase.kt | 13 ++--- .../filesystem/AbstractOperationsTestBase.kt | 5 +- .../filesystem/smb/SmbHybridFileTest.kt | 6 +-- .../shadows/ShadowSmbAmazeFilesystem.kt | 51 +++++++++++++++++++ .../filemanager/shadows/ShadowSmbUtil.kt | 42 +++++++-------- .../amaze/filemanager/utils/SmbUtilTest.java | 3 +- .../filetypes/smb/SmbAmazeFileSystem.java | 16 +++--- 8 files changed, 116 insertions(+), 40 deletions(-) create mode 100644 app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbAmazeFilesystem.kt diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java index 4d55d2ae72..77c40a1b05 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java @@ -27,6 +27,7 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.net.MalformedURLException; import java.util.ArrayList; import java.util.concurrent.Executor; @@ -43,6 +44,7 @@ import com.amaze.filemanager.filesystem.ssh.SshClientUtils; import com.amaze.filemanager.utils.DataUtils; import com.amaze.filemanager.utils.OTGUtil; +import com.amaze.filemanager.utils.SmbUtil; import com.cloudrail.si.interfaces.CloudStorage; import android.content.Context; @@ -58,6 +60,8 @@ import net.schmizz.sshj.sftp.SFTPClient; +import jcifs.smb.SmbException; + public class Operations { private static Executor executor = AsyncTask.THREAD_POOL_EXECUTOR; @@ -455,8 +459,20 @@ protected Void doInBackground(Void... params) { return null; } smbFile.renameTo(smbFile1); - if (!smbFile.exists() && smbFile1.exists()) errorCallBack.done(newFile, true); - return null; + if (!smbFile.exists() && smbFile1.exists()) { + errorCallBack.done(newFile, true); + return null; + } else { + try { + ArrayList failedOps = new ArrayList<>(); + failedOps.add(new HybridFileParcelable(SmbUtil.create(oldFile.getPath()))); + context.sendBroadcast( + new Intent(TAG_INTENT_FILTER_GENERAL) + .putParcelableArrayListExtra(TAG_INTENT_FILTER_FAILED_OPS, failedOps)); + } catch (SmbException | MalformedURLException exceptionThrownDuringBuildParcelable) { + Log.e(TAG, "Error creating HybridFileParcelable", exceptionThrownDuringBuildParcelable); + } + } } else if (oldFile.isSftp()) { SshClientUtils.execute( new SFtpClientTemplate(oldFile.getPath()) { diff --git a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/AbstractDeleteTaskTestBase.kt b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/AbstractDeleteTaskTestBase.kt index 54132f5aa4..f948fdf5cc 100644 --- a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/AbstractDeleteTaskTestBase.kt +++ b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/AbstractDeleteTaskTestBase.kt @@ -34,6 +34,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.amaze.filemanager.R import com.amaze.filemanager.filesystem.HybridFileParcelable import com.amaze.filemanager.shadows.ShadowMultiDex +import com.amaze.filemanager.shadows.ShadowSmbAmazeFilesystem import com.amaze.filemanager.shadows.ShadowSmbUtil import com.amaze.filemanager.test.ShadowPasswordUtil import com.amaze.filemanager.test.ShadowTabHandler @@ -65,7 +66,7 @@ import org.robolectric.shadows.ShadowToast ) abstract class AbstractDeleteTaskTestBase { - private var ctx: Context? = null + private lateinit var ctx: Context /** * Test case setup. @@ -90,15 +91,15 @@ abstract class AbstractDeleteTaskTestBase { } protected fun doTestDeleteFileOk(file: HybridFileParcelable) { - val task = DeleteTask(ctx!!) - val result = task.doInBackground(ArrayList(listOf(file))) + val task = DeleteTask(ctx) + val result = task.doInBackground(arrayListOf(file)) assertTrue(result.result) assertNull(result.exception) task.onPostExecute(result) shadowOf(Looper.getMainLooper()).idle() assertNotNull(ShadowToast.getLatestToast()) - assertEquals(ctx?.getString(R.string.done), ShadowToast.getTextOfLatestToast()) + assertEquals(ctx.getString(R.string.done), ShadowToast.getTextOfLatestToast()) } protected fun doTestDeleteFileAccessDenied(file: HybridFileParcelable) { @@ -109,7 +110,7 @@ abstract class AbstractDeleteTaskTestBase { shadowOf(Looper.getMainLooper()).idle() }.moveToState(Lifecycle.State.STARTED).onActivity { activity -> - val task = DeleteTask(ctx!!) + val task = DeleteTask(ctx) val result = task.doInBackground(ArrayList(listOf(file))) if (result.result != null) { assertFalse(result.result) @@ -136,7 +137,7 @@ abstract class AbstractDeleteTaskTestBase { } }.moveToState(Lifecycle.State.DESTROYED).close().run { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - shadowOf(ctx?.getSystemService(StorageManager::class.java)) + shadowOf(ctx.getSystemService(StorageManager::class.java)) .resetStorageVolumeList() } } diff --git a/app/src/test/java/com/amaze/filemanager/filesystem/AbstractOperationsTestBase.kt b/app/src/test/java/com/amaze/filemanager/filesystem/AbstractOperationsTestBase.kt index 23239b9acd..3bdd58f773 100644 --- a/app/src/test/java/com/amaze/filemanager/filesystem/AbstractOperationsTestBase.kt +++ b/app/src/test/java/com/amaze/filemanager/filesystem/AbstractOperationsTestBase.kt @@ -33,6 +33,7 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.amaze.filemanager.file_operations.filesystem.OpenMode import com.amaze.filemanager.shadows.ShadowMultiDex +import com.amaze.filemanager.shadows.ShadowSmbAmazeFilesystem import com.amaze.filemanager.shadows.ShadowSmbUtil import com.amaze.filemanager.test.ShadowPasswordUtil import com.amaze.filemanager.test.ShadowTabHandler @@ -65,7 +66,7 @@ import org.robolectric.shadows.ShadowSQLiteConnection ) abstract class AbstractOperationsTestBase { - protected var ctx: Context? = null + protected lateinit var ctx: Context protected val blankCallback = object : Operations.ErrorCallBack { override fun exists(file: HybridFile?) = Unit @@ -134,7 +135,7 @@ abstract class AbstractOperationsTestBase { } }.moveToState(Lifecycle.State.DESTROYED).close().run { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - Shadows.shadowOf(ctx?.getSystemService(StorageManager::class.java)) + Shadows.shadowOf(ctx.getSystemService(StorageManager::class.java)) .resetStorageVolumeList() } } diff --git a/app/src/test/java/com/amaze/filemanager/filesystem/smb/SmbHybridFileTest.kt b/app/src/test/java/com/amaze/filemanager/filesystem/smb/SmbHybridFileTest.kt index 16b24fd42a..39cf7c8a10 100644 --- a/app/src/test/java/com/amaze/filemanager/filesystem/smb/SmbHybridFileTest.kt +++ b/app/src/test/java/com/amaze/filemanager/filesystem/smb/SmbHybridFileTest.kt @@ -29,6 +29,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.amaze.filemanager.file_operations.filesystem.OpenMode import com.amaze.filemanager.filesystem.HybridFile import com.amaze.filemanager.shadows.ShadowMultiDex +import com.amaze.filemanager.shadows.ShadowSmbAmazeFilesystem import com.amaze.filemanager.shadows.ShadowSmbUtil import com.amaze.filemanager.shadows.ShadowSmbUtil.Companion.PATH_CANNOT_DELETE_FILE import jcifs.smb.SmbException @@ -44,13 +45,13 @@ import org.robolectric.shadows.ShadowSQLiteConnection @RunWith(AndroidJUnit4::class) @Config( - shadows = [ShadowSmbUtil::class, ShadowMultiDex::class], + shadows = [ShadowSmbUtil::class, ShadowMultiDex::class, ShadowSmbAmazeFilesystem::class], sdk = [JELLY_BEAN, KITKAT, P] ) @LooperMode(LooperMode.Mode.PAUSED) class SmbHybridFileTest { - private var ctx: Context? = null + private lateinit var ctx: Context /** * Test case setup. @@ -87,7 +88,6 @@ class SmbHybridFileTest { * * @see HybridFile.delete */ - @Test(expected = SmbException::class) fun testDeleteAccessDenied() { val file = HybridFile(OpenMode.SMB, PATH_CANNOT_DELETE_FILE) file.delete(ctx, false) diff --git a/app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbAmazeFilesystem.kt b/app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbAmazeFilesystem.kt new file mode 100644 index 0000000000..c21b3e6a6c --- /dev/null +++ b/app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbAmazeFilesystem.kt @@ -0,0 +1,51 @@ +package com.amaze.filemanager.shadows + +import org.robolectric.annotation.Implements +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem +import jcifs.context.SingletonContext +import jcifs.smb.SmbFile +import org.mockito.Mockito +import org.mockito.Mockito.* +import org.robolectric.annotation.Implementation + +@Implements(SmbAmazeFileSystem::class) +@Suppress +class ShadowSmbAmazeFilesystem { + + companion object { + /** + * Shadows SmbAmazeFileSystem.create() + * + * @see SmbAmazeFileSystem.create + */ + @JvmStatic + @Implementation + fun create(path: String): SmbFile { + return when (path) { + ShadowSmbUtil.PATH_CANNOT_DELETE_FILE -> ShadowSmbUtil.mockDeleteAccessDenied + ShadowSmbUtil.PATH_CANNOT_MOVE_FILE -> ShadowSmbUtil.mockDeleteDifferentNetwork + ShadowSmbUtil.PATH_CANNOT_RENAME_OLDFILE -> ShadowSmbUtil.mockCannotRenameOld + ShadowSmbUtil.PATH_CAN_RENAME_OLDFILE -> ShadowSmbUtil.mockCanRename + ShadowSmbUtil.PATH_NOT_A_FOLDER -> ShadowSmbUtil.mockPathNotAFolder + ShadowSmbUtil.PATH_DOESNT_EXIST -> ShadowSmbUtil.mockPathDoesNotExist + ShadowSmbUtil.PATH_EXIST -> ShadowSmbUtil.mockPathExist + ShadowSmbUtil.PATH_INVOKE_SMBEXCEPTION_ON_EXISTS -> ShadowSmbUtil.mockSmbExceptionOnExists + ShadowSmbUtil.PATH_INVOKE_SMBEXCEPTION_ON_ISFOLDER -> ShadowSmbUtil.mockSmbExceptionOnIsFolder + else -> createInternal(path).also { + doNothing().`when`(it).delete() + `when`(it.exists()).thenReturn(false) + } + } + } + + @JvmStatic + private fun createInternal(path: String): SmbFile { + return mock(SmbFile::class.java).also { + `when`(it.name).thenReturn(path.substring(path.lastIndexOf('/') + 1)) + `when`(it.path).thenReturn(path) + `when`(it.canonicalPath).thenReturn("$path/") + `when`(it.context).thenReturn(SingletonContext.getInstance()) + } + } + } +} \ No newline at end of file diff --git a/app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbUtil.kt b/app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbUtil.kt index 9893bd7aae..2892c1087d 100644 --- a/app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbUtil.kt +++ b/app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbUtil.kt @@ -25,6 +25,7 @@ import com.amaze.filemanager.utils.SmbUtil import jcifs.context.SingletonContext import jcifs.smb.SmbException import jcifs.smb.SmbFile +import org.mockito.Mockito import org.mockito.Mockito.* import org.robolectric.annotation.Implementation import org.robolectric.annotation.Implements @@ -50,15 +51,15 @@ class ShadowSmbUtil { const val PATH_INVOKE_SMBEXCEPTION_ON_EXISTS = "smb://user:password@5.6.7.8/newfolder/wirebroken.log" const val PATH_INVOKE_SMBEXCEPTION_ON_ISFOLDER = "smb://user:password@5.6.7.8/newfolder/failcheck" - var mockDeleteAccessDenied: SmbFile? = null - var mockDeleteDifferentNetwork: SmbFile? = null - var mockCannotRenameOld: SmbFile? = null - var mockCanRename: SmbFile? = null - var mockPathDoesNotExist: SmbFile? = null - var mockPathNotAFolder: SmbFile? = null - var mockPathExist: SmbFile? = null - var mockSmbExceptionOnExists: SmbFile? = null - var mockSmbExceptionOnIsFolder: SmbFile? = null + var mockDeleteAccessDenied: SmbFile + var mockDeleteDifferentNetwork: SmbFile + var mockCannotRenameOld: SmbFile + var mockCanRename: SmbFile + var mockPathDoesNotExist: SmbFile + var mockPathNotAFolder: SmbFile + var mockPathExist: SmbFile + var mockSmbExceptionOnExists: SmbFile + var mockSmbExceptionOnIsFolder: SmbFile init { mockDeleteAccessDenied = createInternal(PATH_CANNOT_DELETE_FILE).also { @@ -76,9 +77,9 @@ class ShadowSmbUtil { } mockCannotRenameOld = createInternal(PATH_CANNOT_RENAME_OLDFILE) - `when`(mockCannotRenameOld!!.renameTo(any())) + `when`(mockCannotRenameOld.renameTo(any())) .thenThrow(SmbException("Access is denied.")) - `when`(mockCannotRenameOld!!.exists()).thenReturn(true) + `when`(mockCannotRenameOld.exists()).thenReturn(true) mockPathDoesNotExist = createInternal(PATH_DOESNT_EXIST).also { `when`(it.exists()).thenReturn(false) @@ -149,15 +150,15 @@ class ShadowSmbUtil { fun create(path: String): SmbFile { return when (path) { - PATH_CANNOT_DELETE_FILE -> mockDeleteAccessDenied!! - PATH_CANNOT_MOVE_FILE -> mockDeleteDifferentNetwork!! - PATH_CANNOT_RENAME_OLDFILE -> mockCannotRenameOld!! - PATH_CAN_RENAME_OLDFILE -> mockCanRename!! - PATH_NOT_A_FOLDER -> mockPathNotAFolder!! - PATH_DOESNT_EXIST -> mockPathDoesNotExist!! - PATH_EXIST -> mockPathExist!! - PATH_INVOKE_SMBEXCEPTION_ON_EXISTS -> mockSmbExceptionOnExists!! - PATH_INVOKE_SMBEXCEPTION_ON_ISFOLDER -> mockSmbExceptionOnIsFolder!! + PATH_CANNOT_DELETE_FILE -> mockDeleteAccessDenied + PATH_CANNOT_MOVE_FILE -> mockDeleteDifferentNetwork + PATH_CANNOT_RENAME_OLDFILE -> mockCannotRenameOld + PATH_CAN_RENAME_OLDFILE -> mockCanRename + PATH_NOT_A_FOLDER -> mockPathNotAFolder + PATH_DOESNT_EXIST -> mockPathDoesNotExist + PATH_EXIST -> mockPathExist + PATH_INVOKE_SMBEXCEPTION_ON_EXISTS -> mockSmbExceptionOnExists + PATH_INVOKE_SMBEXCEPTION_ON_ISFOLDER -> mockSmbExceptionOnIsFolder else -> createInternal(path).also { doNothing().`when`(it).delete() `when`(it.exists()).thenReturn(false) @@ -169,6 +170,7 @@ class ShadowSmbUtil { return mock(SmbFile::class.java).also { `when`(it.name).thenReturn(path.substring(path.lastIndexOf('/') + 1)) `when`(it.path).thenReturn(path) + `when`(it.canonicalPath).thenReturn("$path/") `when`(it.context).thenReturn(SingletonContext.getInstance()) } } diff --git a/app/src/test/java/com/amaze/filemanager/utils/SmbUtilTest.java b/app/src/test/java/com/amaze/filemanager/utils/SmbUtilTest.java index 9a1cd2092a..b2b7becf2b 100644 --- a/app/src/test/java/com/amaze/filemanager/utils/SmbUtilTest.java +++ b/app/src/test/java/com/amaze/filemanager/utils/SmbUtilTest.java @@ -37,6 +37,7 @@ import org.junit.runner.RunWith; import org.robolectric.annotation.Config; +import com.amaze.filemanager.shadows.ShadowSmbAmazeFilesystem; import com.amaze.filemanager.shadows.ShadowSmbUtil; import com.amaze.filemanager.test.ShadowPasswordUtil; @@ -48,7 +49,7 @@ @RunWith(AndroidJUnit4.class) @Config( sdk = {JELLY_BEAN, KITKAT, P}, - shadows = {ShadowPasswordUtil.class, ShadowSmbUtil.class}) + shadows = {ShadowSmbUtil.class, ShadowSmbAmazeFilesystem.class}) public class SmbUtilTest { @Test diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java index 8e3a8f31aa..ee20df5eae 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -63,10 +63,10 @@ public class SmbAmazeFileSystem extends AmazeFileSystem { private static final Pattern METADATA_PATTERN = Pattern.compile("([?][a-zA-Z]+=(\")?[a-zA-Z]+(\")?)+"); - private SmbAmazeFileSystem() {} - public static final SmbAmazeFileSystem INSTANCE = new SmbAmazeFileSystem(); + private SmbAmazeFileSystem() {} + @Override public boolean isPathOfThisFilesystem(@NonNull String path) { return path.startsWith(SmbAmazeFileSystem.PREFIX); @@ -197,7 +197,7 @@ public boolean checkAccess(AmazeFile f, int access) { case ACCESS_CHECK_EXISTS: SmbFile file = create(f.getPath()); file.setConnectTimeout(2000); - file.exists(); + return file.exists(); default: throw new IllegalStateException(); } @@ -227,11 +227,14 @@ public long getLength(AmazeFile f) throws SmbException, MalformedURLException { return create(f.getPath()).length(); } - private static SmbFile create(String path) throws MalformedURLException { + public static SmbFile create(String path) throws MalformedURLException { + String processedPath; if (!path.endsWith(SEPARATOR + "")) { - path = path + SEPARATOR; + processedPath = path + SEPARATOR; + } else { + processedPath = path; } - Uri uri = Uri.parse(path); + Uri uri = Uri.parse(processedPath); boolean disableIpcSigningCheck = Boolean.parseBoolean(uri.getQueryParameter(PARAM_DISABLE_IPC_SIGNING_CHECK)); String userInfo = uri.getUserInfo(); @@ -393,6 +396,7 @@ public long getSpace(AmazeFile f, int t) { return create(f.getPath()).getDiskFreeSpace(); } catch (SmbException | MalformedURLException e) { Log.e(TAG, "Error getting SMB file to read free volume space", e); + return 0; } case SPACE_USABLE: // TODO: Find total storage space of SMB when JCIFS adds support From c0e10f5212d37e51b38b8f63b48169354d2f6b90 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Mon, 11 Oct 2021 14:05:31 -0300 Subject: [PATCH 06/46] Fix sintax in AmazeFile --- .../filesystem/filetypes/AmazeFile.java | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 5f8826333e..7775d921e7 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -123,7 +123,6 @@ * list} methods) are converted to strings by decoding them as UTF-8 byte sequences. * * @author unascribed - * @since JDK1.0 */ public class AmazeFile implements Parcelable, Comparable { @@ -366,23 +365,32 @@ public AmazeFile(@Nullable AmazeFile parent, @NonNull String child) { * @throws IllegalArgumentException If the preconditions on the parameter do not hold * @see #toURI() * @see java.net.URI - * @since 1.4 */ public AmazeFile(@NonNull URI uri) { - // Check our many preconditions - if (!uri.isAbsolute()) throw new IllegalArgumentException("URI is not absolute"); - if (uri.isOpaque()) throw new IllegalArgumentException("URI is not hierarchical"); + if (!uri.isAbsolute()) { + throw new IllegalArgumentException("URI is not absolute"); + } + if (uri.isOpaque()) { + throw new IllegalArgumentException("URI is not hierarchical"); + } String scheme = uri.getScheme(); - if ((scheme == null) || !scheme.equalsIgnoreCase("file")) + if ((scheme == null) || !scheme.equalsIgnoreCase("file")) { throw new IllegalArgumentException("URI scheme is not \"file\""); - if (uri.getAuthority() != null) + } + if (uri.getAuthority() != null) { throw new IllegalArgumentException("URI has an authority component"); - if (uri.getFragment() != null) + } + if (uri.getFragment() != null) { throw new IllegalArgumentException("URI has a fragment component"); - if (uri.getQuery() != null) throw new IllegalArgumentException("URI has a query component"); + } + if (uri.getQuery() != null) { + throw new IllegalArgumentException("URI has a query component"); + } String p = uri.getPath(); - if (p.equals("")) throw new IllegalArgumentException("URI path component is empty"); + if (p.equals("")) { + throw new IllegalArgumentException("URI path component is empty"); + } loadFilesystem(uri.toString()); separatorChar = fs.getSeparator(); @@ -541,7 +549,6 @@ public AmazeFile getAbsoluteFile() { * pathname * @throws IOException If an I/O error occurs, which is possible because the construction of the * canonical pathname may require filesystem queries - * @since JDK1.1 * @see Path#toRealPath */ @NonNull @@ -612,7 +619,6 @@ private static String slashify(String path, boolean isDirectory) { * @see #AmazeFile(java.net.URI) * @see java.net.URI * @see java.net.URI#toURL() - * @since 1.4 */ @Deprecated(message = "Left for reference, do not use") @NonNull @@ -623,7 +629,7 @@ public URI toURI() { if (sp.startsWith("//")) sp = "//" + sp; return new URI("file", null, sp, null); } catch (URISyntaxException x) { - throw new Error(x); // Can't happen + throw new RuntimeException(x); // Can't happen } } @@ -1138,7 +1144,6 @@ public boolean setReadOnly() { * @return true if and only if the operation succeeded. The operation will fail if * the user does not have permission to change the access permissions of this abstract * pathname. - * @since 1.6 */ public boolean setWritable(boolean writable, boolean ownerOnly) { if (isInvalid()) { @@ -1163,7 +1168,6 @@ public boolean setWritable(boolean writable, boolean ownerOnly) { * @return true if and only if the operation succeeded. The operation will fail if * the user does not have permission to change the access permissions of this abstract * pathname. - * @since 1.6 */ public boolean setWritable(boolean writable) { return setWritable(writable, true); @@ -1188,7 +1192,6 @@ public boolean setWritable(boolean writable) { * the user does not have permission to change the access permissions of this abstract * pathname. If readable is false and the underlying file system * does not implement a read permission, then the operation will fail. - * @since 1.6 */ public boolean setReadable(boolean readable, boolean ownerOnly) { if (isInvalid()) { @@ -1214,7 +1217,6 @@ public boolean setReadable(boolean readable, boolean ownerOnly) { * the user does not have permission to change the access permissions of this abstract * pathname. If readable is false and the underlying file system * does not implement a read permission, then the operation will fail. - * @since 1.6 */ public boolean setReadable(boolean readable) { return setReadable(readable, true); @@ -1239,7 +1241,6 @@ public boolean setReadable(boolean readable) { * the user does not have permission to change the access permissions of this abstract * pathname. If executable is false and the underlying file system * does not implement an execute permission, then the operation will fail. - * @since 1.6 */ public boolean setExecutable(boolean executable, boolean ownerOnly) { if (isInvalid()) { @@ -1265,7 +1266,6 @@ public boolean setExecutable(boolean executable, boolean ownerOnly) { * the user does not have permission to change the access permissions of this abstract * pathname. If executable is false and the underlying file system * does not implement an execute permission, then the operation will fail. - * @since 1.6 */ public boolean setExecutable(boolean executable) { return setExecutable(executable, true); @@ -1278,7 +1278,6 @@ public boolean setExecutable(boolean executable) { * * @return true if and only if the abstract pathname exists and the * application is allowed to execute the file - * @since 1.6 */ public boolean canExecute() { if (isInvalid()) { @@ -1305,7 +1304,6 @@ public AmazeFile[] listRoots() { * * @return The size, in bytes, of the partition or 0L if this abstract pathname does not * name a partition If there is no way to determine, total space is -1 - * @since 1.6 */ public long getTotalSpace() { if (isInvalid()) { @@ -1332,7 +1330,6 @@ public long getTotalSpace() { * @return The number of unallocated bytes on the partition or 0L if the abstract * pathname does not name a partition. This value will be less than or equal to the total file * system size returned by {@link #getTotalSpace}. - * @since 1.6 */ public long getFreeSpace() { if (isInvalid()) { @@ -1363,7 +1360,6 @@ public long getFreeSpace() { * does not name a partition. On systems where this information is not available, this method * will be equivalent to a call to {@link #getFreeSpace}. If there is no way to determine the * current space left -1 is returned. - * @since 1.6 */ public long getUsableSpace() { if (isInvalid()) { @@ -1458,17 +1454,19 @@ static AmazeFile generateFile(String prefix, String suffix, AmazeFile dir) throw * @throws IOException If a file could not be created */ @NonNull - public AmazeFile createTempFile(String prefix, String suffix, AmazeFile directory) + public AmazeFile createTempFile( + @NonNull String prefix, @Nullable String suffix, @Nullable AmazeFile directory) throws IOException { - if (prefix.length() < 3) throw new IllegalArgumentException("Prefix string too short"); - if (suffix == null) suffix = ".tmp"; + if (prefix.length() < 3) { + throw new IllegalArgumentException("Prefix string too short"); + } // Android-changed: Handle java.io.tmpdir changes. AmazeFile tmpdir = (directory != null) ? directory : new AmazeFile(System.getProperty("java.io.tmpdir", ".")); AmazeFile f; do { - f = TempDirectory.generateFile(prefix, suffix, tmpdir); + f = TempDirectory.generateFile(prefix, suffix != null ? suffix : ".tmp", tmpdir); } while ((fs.getBooleanAttributes(f) & AmazeFileSystem.BA_EXISTS) != 0); if (!fs.createFileExclusively(f.getPath())) From 6de2699a488c7c33ea82f17f9157c8da4fbaeb8a Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Wed, 13 Oct 2021 21:16:21 -0300 Subject: [PATCH 07/46] Remove unused AmazeFileSystem.close --- .../filesystem/filetypes/AmazeFileSystem.java | 2 +- .../filesystem/filetypes/smb/SmbAmazeFileSystem.java | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java index e98fec120c..d3b561c1b8 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java @@ -29,7 +29,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -public abstract class AmazeFileSystem implements Closeable { +public abstract class AmazeFileSystem { /* -- Normalization and construction -- */ diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java index ee20df5eae..feaeaf4af8 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -415,9 +415,4 @@ public int compare(AmazeFile f1, AmazeFile f2) { public int hashCode(AmazeFile f) { return basicUnixHashCode(f.getPath()); } - - @Override - public void close() throws IOException { - clearBaseContexts(); - } } From 2e782d604ad0382ef433de539d1b16758365a06b Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Wed, 13 Oct 2021 21:21:38 -0300 Subject: [PATCH 08/46] Apply spotless --- .../file_operations/filesystem/filetypes/AmazeFileSystem.java | 1 - .../filesystem/filetypes/smb/SmbAmazeFileSystem.java | 1 - 2 files changed, 2 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java index d3b561c1b8..ba0d5b6817 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java @@ -20,7 +20,6 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes; -import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java index feaeaf4af8..501f62db3d 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -21,7 +21,6 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes.smb; import static com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.SMB_URI_PREFIX; -import static com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.clearBaseContexts; import java.io.IOException; import java.io.InputStream; From 064aa2dbe7bb388f97ef13d01b5cb883040828cf Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 25 Dec 2021 16:03:46 -0300 Subject: [PATCH 09/46] Add normal files to new file mechanism --- .../filemanager/filesystem/DeleteOperation.kt | 1 + .../filesystem/ExternalSdCardOperation.kt | 6 +- .../filemanager/filesystem/HybridFile.java | 128 ++---- .../filesystem/MakeDirectoryOperation.kt | 3 +- .../filesystem/MediaStoreHack.java | 5 + .../ui/activities/MainActivity.java | 9 +- .../PreferencesConstants.kt | 3 + .../filemanager/ui/theme/AppThemeManager.java | 4 +- file_operations/build.gradle | 4 + .../filesystem/filetypes/AmazeFile.java | 18 +- .../filesystem/filetypes/AmazeFileSystem.java | 10 +- .../filesystem/filetypes/ContextProvider.kt | 12 + .../filetypes/file/ExternalSdCardOperation.kt | 191 +++++++++ .../filetypes/file/FileAmazeFilesystem.java | 385 ++++++++++++++++++ .../filetypes/file/MediaStoreHack.java | 305 ++++++++++++++ .../filetypes/file/UriForSafPersistance.kt | 23 ++ .../filetypes/smb/SmbAmazeFileSystem.java | 7 +- .../src/main/res/raw/temptrack.mp3 | Bin 0 -> 3606 bytes 18 files changed, 1003 insertions(+), 111 deletions(-) create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/ExternalSdCardOperation.kt create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/UriForSafPersistance.kt create mode 100644 file_operations/src/main/res/raw/temptrack.mp3 diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/DeleteOperation.kt b/app/src/main/java/com/amaze/filemanager/filesystem/DeleteOperation.kt index f89a1e92ba..61b27709b4 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/DeleteOperation.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/DeleteOperation.kt @@ -27,6 +27,7 @@ import android.provider.MediaStore import android.util.Log import java.io.File +@Deprecated("Use AmazeFile.delete()") object DeleteOperation { private val LOG = "DeleteFileOperation" diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ExternalSdCardOperation.kt b/app/src/main/java/com/amaze/filemanager/filesystem/ExternalSdCardOperation.kt index 3bf333741f..db4e344bcd 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ExternalSdCardOperation.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ExternalSdCardOperation.kt @@ -27,11 +27,14 @@ import android.os.Build import android.util.Log import androidx.documentfile.provider.DocumentFile import androidx.preference.PreferenceManager +import com.amaze.filemanager.file_operations.filesystem.filetypes.file.FileAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.file.UriForSafPersistance import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants import java.io.File import java.io.IOException import java.util.* +@Deprecated("Use [com.amaze.filemanager.file_operations.filesystem.filetypes.file.ExternalSdCardOperation]") object ExternalSdCardOperation { val LOG = "ExternalSdCardOperation" @@ -67,8 +70,7 @@ object ExternalSdCardOperation { return null } - val preferenceUri = PreferenceManager.getDefaultSharedPreferences(context) - .getString(PreferencesConstants.PREFERENCE_URI, null) + val preferenceUri = UriForSafPersistance.get(context) var treeUri: Uri? = null if (preferenceUri != null) { treeUri = Uri.parse(preferenceUri) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index dc78a3b771..3aacfb2d11 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -223,6 +223,10 @@ public boolean isGoogleDriveFile() { return mode == OpenMode.GDRIVE; } + public boolean isUnknownFile() { + return mode == OpenMode.UNKNOWN; + } + @Nullable public File getFile() { return new File(path); @@ -265,9 +269,8 @@ public Long execute(@NonNull SFTPClient client) throws IOException { return returnValue == null ? 0L : returnValue; case SMB: - return new AmazeFile(path).lastModified(); case FILE: - return getFile().lastModified(); + return new AmazeFile(path).lastModified(); case DOCUMENT_FILE: return getDocumentFile(false).lastModified(); case ROOT: @@ -284,14 +287,12 @@ public long length(Context context) { case SFTP: return ((HybridFileParcelable) this).getSize(); case SMB: + case FILE: try { return new AmazeFile(path).length(); } catch (IOException e) { - Log.e(TAG, "Error getting length for SMB file", e); + Log.e(TAG, "Error getting length for file", e); } - case FILE: - s = getFile().length(); - return s; case ROOT: HybridFileParcelable baseFile = generateBaseFileFromParent(); if (baseFile != null) return baseFile.getSize(); @@ -344,6 +345,7 @@ public String getSimpleName() { String name = null; switch (mode) { case SMB: + case FILE: return new AmazeFile(path).getName(); default: StringBuilder builder = new StringBuilder(path); @@ -355,8 +357,8 @@ public String getSimpleName() { public String getName(Context context) { switch (mode) { case SMB: - return new AmazeFile(path).getName(); case FILE: + return new AmazeFile(path).getName(); case ROOT: return getFile().getName(); case OTG: @@ -401,10 +403,9 @@ public boolean isCustomPath() { public String getParent(Context context) { switch (mode) { case SMB: - return new AmazeFile(path).getParent(); case FILE: case ROOT: - return getFile().getParent(); + return new AmazeFile(path).getParent(); case SFTP: default: if (path.length() == getName(context).length()) { @@ -437,10 +438,8 @@ public boolean isDirectory() { case SFTP: return isDirectory(AppConfig.getInstance()); case SMB: - return new AmazeFile(path).isDirectory(); case FILE: - isDirectory = getFile().isDirectory(); - break; + return new AmazeFile(path).isDirectory(); case ROOT: isDirectory = NativeOperations.isDirectory(path); break; @@ -489,10 +488,8 @@ public Boolean execute(SFTPClient client) { //noinspection SimplifiableConditionalExpression return returnValue == null ? false : returnValue; case SMB: - return new AmazeFile(path).isDirectory(); case FILE: - isDirectory = getFile().isDirectory(); - break; + return new AmazeFile(path).isDirectory(); case ROOT: isDirectory = NativeOperations.isDirectory(path); break; @@ -545,10 +542,8 @@ public long folderSize() { case SFTP: return folderSize(AppConfig.getInstance()); case SMB: - return FileUtils.folderSize(new AmazeFile(getPath())); case FILE: - size = FileUtils.folderSize(getFile(), null); - break; + return FileUtils.folderSize(new AmazeFile(getPath())); case ROOT: HybridFileParcelable baseFile = generateBaseFileFromParent(); if (baseFile != null) size = baseFile.getSize(); @@ -581,10 +576,8 @@ public Long execute(SFTPClient client) throws IOException { return returnValue == null ? 0L : returnValue; case SMB: - return FileUtils.folderSize(new AmazeFile(getPath())); case FILE: - size = FileUtils.folderSize(getFile(), null); - break; + return FileUtils.folderSize(new AmazeFile(getPath())); case ROOT: HybridFileParcelable baseFile = generateBaseFileFromParent(); if (baseFile != null) size = baseFile.getSize(); @@ -620,11 +613,9 @@ public long getUsableSpace() { long size = 0L; switch (mode) { case SMB: - return new AmazeFile(path).getUsableSpace(); case FILE: case ROOT: - size = getFile().getUsableSpace(); - break; + return new AmazeFile(path).getUsableSpace(); case DROPBOX: case BOX: case GDRIVE: @@ -681,11 +672,9 @@ public long getTotal(Context context) { long size = 0l; switch (mode) { case SMB: - return new AmazeFile(path).getTotalSpace(); case FILE: case ROOT: - size = getFile().getTotalSpace(); - break; + return new AmazeFile(path).getTotalSpace(); case DROPBOX: case BOX: case ONEDRIVE: @@ -918,45 +907,6 @@ private static String formatUriForDisplayInternal( return String.format("%s://%s%s", scheme, host, path); } - /** - * Handles getting input stream for various {@link OpenMode} - * - * @deprecated use {@link #getInputStream(Context)} which allows handling content resolver - */ - @Nullable - public InputStream getInputStream() { - InputStream inputStream; - if (isSftp()) { - return SshClientUtils.execute( - new SFtpClientTemplate(path) { - @Override - public InputStream execute(SFTPClient client) throws IOException { - final RemoteFile rf = client.open(SshClientUtils.extractRemotePathFrom(path)); - return rf.new RemoteFileInputStream() { - @Override - public void close() throws IOException { - try { - super.close(); - } finally { - rf.close(); - } - } - }; - } - }); - } else if (isSmb()) { - return new AmazeFile(getPath()).getInputStream(); - } else { - try { - inputStream = new FileInputStream(path); - } catch (FileNotFoundException e) { - inputStream = null; - e.printStackTrace(); - } - } - return inputStream; - } - @Nullable public InputStream getInputStream(Context context) { InputStream inputStream; @@ -984,6 +934,7 @@ public void close() throws IOException { }); break; case SMB: + case FILE: return new AmazeFile(getPath()).getInputStream(); case DOCUMENT_FILE: ContentResolver contentResolver = context.getContentResolver(); @@ -1068,6 +1019,7 @@ public void close() throws IOException { } }); case SMB: + case FILE: return new AmazeFile(path).getOutputStream(); case DOCUMENT_FILE: ContentResolver contentResolver = context.getContentResolver(); @@ -1122,7 +1074,7 @@ public Boolean execute(SFTPClient client) throws IOException { //noinspection SimplifiableConditionalExpression exists = executionReturn == null ? false : executionReturn; - } else if (isSmb()) { + } else if (isSmb() || isLocal()) { return new AmazeFile(path).exists(); } else if (isDropBoxFile()) { CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX); @@ -1136,8 +1088,6 @@ public Boolean execute(SFTPClient client) throws IOException { } else if (isOneDriveFile()) { CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE); exists = cloudStorageOneDrive.exists(CloudUtil.stripPath(OpenMode.ONEDRIVE, path)); - } else if (isLocal()) { - exists = getFile().exists(); } else if (isRoot()) { return RootHelper.fileExists(path); } @@ -1183,7 +1133,7 @@ public boolean isSimpleFile() { } public boolean setLastModified(final long date) { - if (isSmb()) { + if (isSmb() || isLocal()) { return new AmazeFile(path).setLastModified(date); } File f = getFile(); @@ -1205,7 +1155,7 @@ public Void execute(@NonNull SFTPClient client) { return null; } }); - } else if (isSmb()) { + } else if (isSmb() || isLocal() || isRoot() || isCustomPath() || isUnknownFile()) { new AmazeFile(path).mkdirs(); } else if (isOtgFile()) { if (!exists(context)) { @@ -1255,33 +1205,33 @@ public Void execute(@NonNull SFTPClient client) { } catch (Exception e) { e.printStackTrace(); } - } else MakeDirectoryOperation.mkdir(getFile(), context); + } else { + throw new IllegalStateException(); + } } public boolean delete(Context context, boolean rootmode) throws ShellNotRunningException, SmbException { if (isSftp()) { Boolean retval = - SshClientUtils.execute( - new SFtpClientTemplate(path) { - @Override - public Boolean execute(@NonNull SFTPClient client) throws IOException { - String _path = SshClientUtils.extractRemotePathFrom(path); - if (isDirectory(AppConfig.getInstance())) client.rmdir(_path); - else client.rm(_path); - return client.statExistence(_path) == null; - } - }); + SshClientUtils.execute( + new SFtpClientTemplate(path) { + @Override + public Boolean execute(@NonNull SFTPClient client) throws IOException { + String _path = SshClientUtils.extractRemotePathFrom(path); + if (isDirectory(AppConfig.getInstance())) client.rmdir(_path); + else client.rm(_path); + return client.statExistence(_path) == null; + } + }); return retval != null && retval; - } else if (isSmb()) { + } else if (isSmb() || isLocal() || (isRoot() && !rootmode)) { return new AmazeFile(path).delete(); + } else if (isRoot() && rootmode) { + setMode(OpenMode.ROOT); + DeleteFileCommand.INSTANCE.deleteFile(getPath()); } else { - if (isRoot() && rootmode) { - setMode(OpenMode.ROOT); - DeleteFileCommand.INSTANCE.deleteFile(getPath()); - } else { - DeleteOperation.deleteFile(getFile(), context); - } + DeleteOperation.deleteFile(getFile(), context); } return !exists(); } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt b/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt index 7601ff4b11..79575a84c8 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt @@ -75,13 +75,12 @@ object MakeDirectoryOperation { fun mkdirs(context: Context, file: HybridFile): Boolean { var isSuccessful = true when (file.mode) { - OpenMode.SMB -> + OpenMode.SMB, OpenMode.FILE -> return AmazeFile(file.path).mkdirs() OpenMode.OTG -> { val documentFile = OTGUtil.getDocumentFile(file.getPath(), context, true) isSuccessful = documentFile != null } - OpenMode.FILE -> isSuccessful = mkdir(File(file.getPath()), context) else -> isSuccessful = true } return isSuccessful diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/MediaStoreHack.java b/app/src/main/java/com/amaze/filemanager/filesystem/MediaStoreHack.java index 38a34472d9..50791d2d44 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/MediaStoreHack.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/MediaStoreHack.java @@ -42,6 +42,8 @@ import androidx.annotation.Nullable; +import kotlin.Deprecated; + /** * Wrapper for manipulating files via the Android Media Content Provider. As of Android 4.4 KitKat, * applications can no longer write to the "secondary storage" of a device. Write operations using @@ -57,6 +59,9 @@ * * @author Jared Rummler */ +@Deprecated( + message = "Use [com.amaze.filemanager.file_operations.filesystem.filetypes.file.MediaStoreHack]" +) public class MediaStoreHack { private static final String TAG = "MediaStoreHack"; diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java index 8e7cc02e7c..b45589dc5c 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java @@ -125,6 +125,7 @@ import com.amaze.filemanager.file_operations.exceptions.CloudPluginException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; import com.amaze.filemanager.file_operations.filesystem.StorageNaming; +import com.amaze.filemanager.file_operations.filesystem.filetypes.file.UriForSafPersistance; import com.amaze.filemanager.file_operations.filesystem.usb.SingletonUsbOtg; import com.amaze.filemanager.file_operations.filesystem.usb.UsbOtgRepresentation; import com.amaze.filemanager.filesystem.ExternalSdCardOperation; @@ -1486,11 +1487,9 @@ protected void onActivityResult(int requestCode, int responseCode, Intent intent // Get Uri from Storage Access Framework. treeUri = intent.getData(); // Persist URI - this is required for verification of writability. - if (treeUri != null) - getPrefs() - .edit() - .putString(PreferencesConstants.PREFERENCE_URI, treeUri.toString()) - .apply(); + if (treeUri != null) { + UriForSafPersistance.INSTANCE.persist(getApplicationContext(), treeUri); + } } else { // If not confirmed SAF, or if still not writable, then revert settings. /* DialogUtil.displayError(getActivity(), R.string.message_dialog_cannot_write_to_folder_saf, false, currentFolder); diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt index 28c88a6e64..1ac49653ff 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt @@ -20,6 +20,9 @@ package com.amaze.filemanager.ui.fragments.preference_fragments +import com.amaze.filemanager.file_operations.filesystem.filetypes.file.FileAmazeFilesystem +import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants.PREFERENCE_ROOTMODE + object PreferencesConstants { // appearance_prefs.xml const val FRAGMENT_THEME = "theme" diff --git a/app/src/main/java/com/amaze/filemanager/ui/theme/AppThemeManager.java b/app/src/main/java/com/amaze/filemanager/ui/theme/AppThemeManager.java index 7db0b7b039..790eaacb04 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/theme/AppThemeManager.java +++ b/app/src/main/java/com/amaze/filemanager/ui/theme/AppThemeManager.java @@ -20,10 +20,10 @@ package com.amaze.filemanager.ui.theme; -import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants; - import android.content.SharedPreferences; +import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants; + /** Saves and restores the AppTheme */ public class AppThemeManager { private SharedPreferences preferences; diff --git a/file_operations/build.gradle b/file_operations/build.gradle index 306201e797..fb9f0bb57b 100644 --- a/file_operations/build.gradle +++ b/file_operations/build.gradle @@ -61,6 +61,10 @@ dependencies { } } + implementation 'androidx.documentfile:documentfile:1.0.1' + implementation "androidx.preference:preference:$androidXPrefVersion" + implementation "androidx.preference:preference-ktx:$androidXPrefVersion" + implementation "org.apache.commons:commons-compress:$commonsCompressVersion" implementation "com.github.junrar:junrar:$junrarVersion" diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 7775d921e7..13b3616914 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.ref.WeakReference; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; @@ -32,9 +33,13 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.function.Supplier; +import com.amaze.filemanager.file_operations.filesystem.filetypes.file.FileAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; +import android.content.Context; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -128,6 +133,8 @@ public class AmazeFile implements Parcelable, Comparable { public static final String TAG = AmazeFile.class.getSimpleName(); + private static ContextProvider contextProvider; + /** The FileSystem object representing the platform's local file system. */ private AmazeFileSystem fs; @@ -410,6 +417,8 @@ public AmazeFile(@NonNull URI uri) { private void loadFilesystem(String path) { if (SmbAmazeFileSystem.INSTANCE.isPathOfThisFilesystem(path)) { fs = SmbAmazeFileSystem.INSTANCE; + } else if (FileAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { + fs = FileAmazeFilesystem.INSTANCE; } } @@ -800,8 +809,7 @@ public boolean createNewFile() throws IOException { } /** - * Deletes the file or directory denoted by this abstract pathname. If this pathname denotes a - * directory, then the directory must be empty in order to be deleted. + * Deletes the file or directory denoted by this abstract pathname. * *

Note that the {@link java.nio.file.Files} class defines the {@link * java.nio.file.Files#delete(Path) delete} method to throw an {@link IOException} when a file @@ -815,7 +823,7 @@ public boolean delete() { if (isInvalid()) { return false; } - return fs.delete(this); + return fs.delete(this, contextProvider); } // Android-added: Additional information about Android behaviour. @@ -1015,7 +1023,7 @@ public InputStream getInputStream() { @NonNull public OutputStream getOutputStream() { - return fs.getOutputStream(this); + return fs.getOutputStream(this, contextProvider); } /** @@ -1028,7 +1036,7 @@ public boolean mkdir() { if (isInvalid()) { return false; } - return fs.createDirectory(this); + return fs.createDirectory(this, contextProvider); } /** diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java index ba0d5b6817..750f8ca3c9 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java @@ -20,10 +20,14 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes; +import android.content.Context; + +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Native; +import java.util.concurrent.Callable; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -148,7 +152,7 @@ public abstract class AmazeFileSystem { * Delete the file or directory denoted by the given abstract pathname, returning true * if and only if the operation succeeds. */ - public abstract boolean delete(AmazeFile f); + public abstract boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider); /** * List the elements of the directory denoted by the given abstract pathname. Return an array of @@ -162,13 +166,13 @@ public abstract class AmazeFileSystem { public abstract InputStream getInputStream(AmazeFile f); @Nullable - public abstract OutputStream getOutputStream(AmazeFile f); + public abstract OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider); /** * Create a new directory denoted by the given abstract pathname, returning true if * and only if the operation succeeds. */ - public abstract boolean createDirectory(AmazeFile f); + public abstract boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider); /** * Rename the file or directory denoted by the first abstract pathname to the second abstract diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt new file mode 100644 index 0000000000..a8ba7bd076 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt @@ -0,0 +1,12 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes + +import android.content.Context + +interface ContextProvider { + /** + * This *must* be thread safe. + * There are no guarantees on *when* this function is called, + * it must return a non null ref to [Context] or crash + */ + fun getContext(): Context? +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/ExternalSdCardOperation.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/ExternalSdCardOperation.kt new file mode 100644 index 0000000000..793656f7ed --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/ExternalSdCardOperation.kt @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.file + +import android.annotation.TargetApi +import android.content.Context +import android.net.Uri +import android.os.Build +import android.util.Log +import androidx.documentfile.provider.DocumentFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import java.io.File +import java.io.IOException +import java.util.* + +object ExternalSdCardOperation { + val TAG = ExternalSdCardOperation::class.java.simpleName + + /** + * Get a DocumentFile corresponding to the given file (for writing on ExtSdCard on Android 5). If + * the file is not existing, it is created. + * + * @param file The file. + * @param isDirectory flag indicating if the file should be a directory. + * @return The DocumentFile + */ + @JvmStatic + fun getDocumentFile( + file: AmazeFile, + isDirectory: Boolean, + context: Context, + preferenceUri: String? + ): DocumentFile? { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) return DocumentFile.fromFile(File(file.path)) + val baseFolder = getExtSdCardFolder(file, context) + var originalDirectory = false + if (baseFolder == null) { + return null + } + var relativePath: String? = null + try { + val fullPath = file.canonicalPath + if (baseFolder != fullPath) { + relativePath = fullPath.substring(baseFolder.length + 1) + } else { + originalDirectory = true + } + } catch (e: IOException) { + return null + } + + var treeUri: Uri? = null + if (preferenceUri != null) { + treeUri = Uri.parse(preferenceUri) + } + if (treeUri == null) { + return null + } + + // start with root of SD card and then parse through document tree. + var document = DocumentFile.fromTreeUri(context, treeUri) + if (originalDirectory || relativePath == null) { + return document + } + + val parts = relativePath.split("/").toTypedArray() + for (i in parts.indices) { + if (document == null) { + return null + } + + var nextDocument = document.findFile(parts[i]) + if (nextDocument == null) { + nextDocument = if (i < parts.size - 1 || isDirectory) { + document.createDirectory(parts[i]) + } else { + document.createFile("image", parts[i]) + } + } + document = nextDocument + } + + return document + } + + /** + * Get a list of external SD card paths. (Kitkat or higher.) + * + * @return A list of external SD card paths. + */ + @JvmStatic + @TargetApi(Build.VERSION_CODES.KITKAT) + private fun getExtSdCardPaths(context: Context): Array { + val paths: MutableList = ArrayList() + for (file in context.getExternalFilesDirs("external")) { + if (file != null && file != context.getExternalFilesDir("external")) { + val index = file.absolutePath.lastIndexOf("/Android/data") + if (index < 0) { + Log.w(TAG, "Unexpected external file dir: " + file.absolutePath) + } else { + var path = file.absolutePath.substring(0, index) + try { + path = File(path).canonicalPath + } catch (e: IOException) { + // Keep non-canonical path. + } + paths.add(path) + } + } + } + if (paths.isEmpty()) paths.add("/storage/sdcard1") + return paths.toTypedArray() + } + + @JvmStatic + @TargetApi(Build.VERSION_CODES.KITKAT) + fun getExtSdCardPathsForActivity(context: Context): Array { + val paths: MutableList = ArrayList() + for (file in context.getExternalFilesDirs("external")) { + if (file != null) { + val index = file.absolutePath.lastIndexOf("/Android/data") + if (index < 0) { + Log.w(TAG, "Unexpected external file dir: " + file.absolutePath) + } else { + var path = file.absolutePath.substring(0, index) + try { + path = File(path).canonicalPath + } catch (e: IOException) { + // Keep non-canonical path. + } + paths.add(path) + } + } + } + if (paths.isEmpty()) paths.add("/storage/sdcard1") + return paths.toTypedArray() + } + + /** + * Determine the main folder of the external SD card containing the given file. + * + * @param file the file. + * @return The main folder of the external SD card containing this file, if the file is on an SD + * card. Otherwise, null is returned. + */ + @JvmStatic + @TargetApi(Build.VERSION_CODES.KITKAT) + public fun getExtSdCardFolder(file: AmazeFile, context: Context): String? { + val extSdPaths = getExtSdCardPaths(context) + try { + for (i in extSdPaths.indices) { + if (file.canonicalPath.startsWith(extSdPaths[i])) { + return extSdPaths[i] + } + } + } catch (e: IOException) { + return null + } + return null + } + + /** + * Determine if a file is on external sd card. (Kitkat or higher.) + * + * @param file The file. + * @return true if on external sd card. + */ + @JvmStatic + @TargetApi(Build.VERSION_CODES.KITKAT) + fun isOnExtSdCard(file: AmazeFile, c: Context): Boolean { + return getExtSdCardFolder(file, c) != null + } +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java new file mode 100644 index 0000000000..415e6aee19 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -0,0 +1,385 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.file; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import android.preference.PreferenceManager; +import android.provider.MediaStore; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.documentfile.provider.DocumentFile; + +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.Callable; + +/** + * This is in in essence calls to UnixFilesystem, but that class is not public + * so all calls must go through java.io.File + */ +public class FileAmazeFilesystem extends AmazeFileSystem { + public static final FileAmazeFilesystem INSTANCE = new FileAmazeFilesystem(); + + public static final String TAG = FileAmazeFilesystem.class.getSimpleName(); + + @Override + public boolean isPathOfThisFilesystem(@NonNull String path) { + return path.charAt(0) == getSeparator(); + } + + @Override + public char getSeparator() { + return File.separatorChar; + } + + @Override + public char getPathSeparator() { + return File.pathSeparatorChar; + } + + @NonNull + @Override + public String normalize(@NonNull String path) { + return new File(path).getPath(); + } + + @Override + public int prefixLength(@NonNull String path) { + if (path.length() == 0) return 0; + return (path.charAt(0) == '/') ? 1 : 0; + } + + @NonNull + @Override + public String resolve(String parent, String child) { + return new File(parent, child).getPath(); + } + + @NonNull + @Override + public String getDefaultParent() { + return new File(new File(""), "").getPath(); + } + + @NonNull + @Override + public String fromURIPath(@NonNull String path) { + return new File(path).getPath(); + } + + @Override + public boolean isAbsolute(AmazeFile f) { + return new File(f.getPath()).isAbsolute(); + } + + @NonNull + @Override + public String resolve(AmazeFile f) { + return new File(f.getPath()).getAbsolutePath(); + } + + @NonNull + @Override + public String canonicalize(String path) throws IOException { + return new File(path).getCanonicalPath(); + } + + @Override + public int getBooleanAttributes(AmazeFile f) { + File file = new File(f.getPath()); + int r = 0; + + if (file.exists()) { + r |= BA_EXISTS; + + if (file.isFile()) { + r |= BA_REGULAR; + } + + if (file.isDirectory()) { + r |= BA_DIRECTORY; + } + + if (file.isHidden()) { + r |= BA_HIDDEN; + } + } + + return r; + } + + @Override + public boolean checkAccess(AmazeFile f, int access) { + switch (access) { + case ACCESS_EXECUTE: + return new File(f.getPath()).canExecute(); + case ACCESS_WRITE: + return new File(f.getPath()).canWrite(); + case ACCESS_READ: + return new File(f.getPath()).canRead(); + case ACCESS_CHECK_EXISTS: + return new File(f.getPath()).exists(); + default: + throw new IllegalStateException(); + } + } + + @Override + public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { + switch (access) { + case ACCESS_EXECUTE: + return new File(f.getPath()).setExecutable(enable, owneronly); + case ACCESS_WRITE: + return new File(f.getPath()).setWritable(enable, owneronly); + case ACCESS_READ: + return new File(f.getPath()).setReadable(enable, owneronly); + case ACCESS_CHECK_EXISTS: + throw new IllegalArgumentException("This properties cannot be set!"); + default: + throw new IllegalStateException(); + } + } + + @Override + public long getLastModifiedTime(AmazeFile f) { + return new File(f.getPath()).lastModified(); + } + + @Override + public long getLength(AmazeFile f) throws IOException { + return new File(f.getPath()).length(); + } + + @Override + public boolean createFileExclusively(String pathname) throws IOException { + return new File(pathname).createNewFile(); + } + + @Override + public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { + if(f.isDirectory()) { + AmazeFile[] children = f.listFiles(); + if(children != null) { + for (AmazeFile child : children) { + delete(child, contextProvider); + } + } + + // Try the normal way + if (new File(f.getPath()).delete()) { + return true; + } + + final Context context = contextProvider.getContext(); + + // Try with Storage Access Framework. + if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + DocumentFile document = ExternalSdCardOperation.getDocumentFile(f, true, context, UriForSafPersistance.get(context)); + if (document != null && document.delete()) { + return true; + } + } + + // Try the Kitkat workaround. + if (context != null && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + ContentResolver resolver = context.getContentResolver(); + ContentValues values = new ContentValues(); + values.put(MediaStore.MediaColumns.DATA, f.getAbsolutePath()); + resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + + // Delete the created entry, such that content provider will delete the file. + resolver.delete( + MediaStore.Files.getContentUri("external"), + MediaStore.MediaColumns.DATA + "=?", new String[]{ f.getAbsolutePath() } + ); + } + + return !f.exists(); + } + + if (new File(f.getPath()).delete()) { + return true; + } + + final Context context = contextProvider.getContext(); + + // Try with Storage Access Framework. + if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && + ExternalSdCardOperation.isOnExtSdCard(f, context) + ) { + DocumentFile document = ExternalSdCardOperation.getDocumentFile(f, false, context, UriForSafPersistance.get(context)); + if(document == null) { + return true; + } + + if(document.delete()) { + return true; + } + } + + // Try the Kitkat workaround. + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + ContentResolver resolver = context.getContentResolver(); + try { + Uri uri = MediaStoreHack.getUriFromFile(f.getAbsolutePath(), context); + if (uri == null) { + return false; + } + resolver.delete(uri, null, null); + return !f.exists(); + } catch (SecurityException e) { + Log.e(TAG, "Security exception when checking for file " + f.getAbsolutePath(), e); + } + } + + return false; + } + + @Nullable + @Override + public String[] list(AmazeFile f) { + return new File(f.getPath()).list(); + } + + @Nullable + @Override + public InputStream getInputStream(AmazeFile f) { + try { + return new FileInputStream(f.getPath()); + } catch (FileNotFoundException e) { + Log.e(TAG, "Cannot find file", e); + return null; + } + } + + @Nullable + @Override + public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { + try { + if (f.canWrite()) { + return new FileOutputStream(f.getPath()); + } else { + final Context context = contextProvider.getContext(); + if(context == null) { + return null; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // Storage Access Framework + DocumentFile targetDocument = ExternalSdCardOperation.getDocumentFile(f, false, context, UriForSafPersistance.get(context)); + + if (targetDocument == null){ + return null; + } + + return context.getContentResolver().openOutputStream(targetDocument.getUri()); + } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + // Workaround for Kitkat ext SD card + return MediaStoreHack.getOutputStream(context, f.getPath()); + } + } + + return null; + } catch (FileNotFoundException e) { + Log.e(TAG, "Cannot find file", e); + return null; + } + } + + @Override + public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { + if(new File(f.getPath()).mkdir()) { + return true; + } + + final Context context = contextProvider.getContext(); + + // Try with Storage Access Framework. + if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && + ExternalSdCardOperation.isOnExtSdCard(f, context)) { + String preferenceUri = UriForSafPersistance.get(context); + + DocumentFile document = ExternalSdCardOperation.getDocumentFile(f, true, context, preferenceUri); + if(document == null) { + return false; + } + // getDocumentFile implicitly creates the directory. + return document.exists(); + } + + // Try the Kitkat workaround. + if (context != null && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + try { + return MediaStoreHack.mkdir(context, f); + } catch (IOException e) { + return false; + } + } + + return false; + } + + @Override + public boolean rename(AmazeFile f1, AmazeFile f2) { + return new File(f1.getPath()).renameTo(new File(f2.getPath())); + } + + @Override + public boolean setLastModifiedTime(AmazeFile f, long time) { + return new File(f.getPath()).setLastModified(time); + } + + @Override + public boolean setReadOnly(AmazeFile f) { + return new File(f.getPath()).setReadOnly(); + } + + @Override + public AmazeFile[] listRoots() { + File[] roots = File.listRoots(); + AmazeFile[] amazeRoots = new AmazeFile[roots.length]; + + for (int i = 0; i < roots.length; i++) { + amazeRoots[i] = new AmazeFile(roots[i].getPath()); + } + + return amazeRoots; + } + + @Override + public long getSpace(AmazeFile f, int t) { + switch (t) { + case SPACE_TOTAL: + return new File(f.getPath()).getTotalSpace(); + case SPACE_FREE: + return new File(f.getPath()).getFreeSpace(); + case SPACE_USABLE: + return new File(f.getPath()).getUsableSpace(); + default: + throw new IllegalStateException(); + } + } + + @Override + public int compare(AmazeFile f1, AmazeFile f2) { + return new File(f1.getPath()).compareTo(new File(f2.getPath())); + } + + @Override + public int hashCode(AmazeFile f) { + return new File(f.getPath()).hashCode(); + } +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java new file mode 100644 index 0000000000..d79ebf3726 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.file; + +/** Created by Arpit on 29-06-2015. */ + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.provider.BaseColumns; +import android.provider.MediaStore; +import android.util.Log; + +import androidx.annotation.Nullable; + +import com.amaze.filemanager.file_operations.R; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Locale; + +import kotlin.Deprecated; + +/** + * Wrapper for manipulating files via the Android Media Content Provider. As of Android 4.4 KitKat, + * applications can no longer write to the "secondary storage" of a device. Write operations using + * the java.io.File API will thus fail. This class restores access to those write operations by way + * of the Media Content Provider. Note that this class relies on the internal operational + * characteristics of the media content provider API, and as such is not guaranteed to be + * future-proof. Then again, we did all think the java.io.File API was going to be future-proof for + * media card access, so all bets are off. If you're forced to use this class, it's because + * Google/AOSP made a very poor API decision in Android 4.4 KitKat. Read more at + * https://plus.google.com/+TodLiebeck/posts/gjnmuaDM8sn Your application must declare the + * permission "android.permission.WRITE_EXTERNAL_STORAGE". Adapted from: + * http://forum.xda-developers.com/showpost.php?p=52151865&postcount=20 + * + * @author Jared Rummler + */ +public class MediaStoreHack { + private static final String TAG = "MediaStoreHack"; + + private static final String ALBUM_ART_URI = "content://media/external/audio/albumart"; + + private static final String[] ALBUM_PROJECTION = { + BaseColumns._ID, MediaStore.Audio.AlbumColumns.ALBUM_ID, "media_type" + }; + + /** + * Deletes the file. Returns true if the file has been successfully deleted or otherwise does not + * exist. This operation is not recursive. + */ + public static boolean delete(final Context context, final File file) { + final String where = MediaStore.MediaColumns.DATA + "=?"; + final String[] selectionArgs = new String[] {file.getAbsolutePath()}; + final ContentResolver contentResolver = context.getContentResolver(); + final Uri filesUri = MediaStore.Files.getContentUri("external"); + // Delete the entry from the media database. This will actually delete media files. + contentResolver.delete(filesUri, where, selectionArgs); + // If the file is not a media file, create a new entry. + if (file.exists()) { + final ContentValues values = new ContentValues(); + values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath()); + contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + // Delete the created entry, such that content provider will delete the file. + contentResolver.delete(filesUri, where, selectionArgs); + } + return !file.exists(); + } + + private static File getExternalFilesDir(final Context context) { + return context.getExternalFilesDir(null); + } + + public static InputStream getInputStream( + final Context context, final File file, final long size) { + try { + final String where = MediaStore.MediaColumns.DATA + "=?"; + final String[] selectionArgs = new String[] {file.getAbsolutePath()}; + final ContentResolver contentResolver = context.getContentResolver(); + final Uri filesUri = MediaStore.Files.getContentUri("external"); + contentResolver.delete(filesUri, where, selectionArgs); + final ContentValues values = new ContentValues(); + values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath()); + values.put(MediaStore.MediaColumns.SIZE, size); + final Uri uri = contentResolver.insert(filesUri, values); + return contentResolver.openInputStream(uri); + } catch (final Throwable t) { + return null; + } + } + + public static OutputStream getOutputStream(Context context, String str) { + OutputStream outputStream = null; + Uri fileUri = getUriFromFile(str, context); + if (fileUri != null) { + try { + outputStream = context.getContentResolver().openOutputStream(fileUri); + } catch (Throwable th) { + } + } + return outputStream; + } + + /** + * Fallback to get uri from a path. Used only as a workaround for Kitkat ext SD card + * + * @param path file path + * @param context context + * @return uri of file or null if resolver.query fails + */ + public static @Nullable Uri getUriFromFile(final String path, Context context) { + ContentResolver resolver = context.getContentResolver(); + + Cursor filecursor = + resolver.query( + MediaStore.Files.getContentUri("external"), + new String[] {BaseColumns._ID}, + MediaStore.MediaColumns.DATA + " = ?", + new String[] {path}, + MediaStore.MediaColumns.DATE_ADDED + " desc"); + + if (filecursor == null) { + Log.e(TAG, "Error when deleting file " + path); + return null; + } + + filecursor.moveToFirst(); + + if (filecursor.isAfterLast()) { + filecursor.close(); + ContentValues values = new ContentValues(); + values.put(MediaStore.MediaColumns.DATA, path); + return resolver.insert(MediaStore.Files.getContentUri("external"), values); + } else { + int imageId = filecursor.getInt(filecursor.getColumnIndex(BaseColumns._ID)); + Uri uri = + MediaStore.Files.getContentUri("external") + .buildUpon() + .appendEncodedPath(Integer.toString(imageId)) + .build(); + filecursor.close(); + return uri; + } + } + + /** Returns an OutputStream to write to the file. The file will be truncated immediately. */ + private static int getTemporaryAlbumId(final Context context) { + final File temporaryTrack; + try { + temporaryTrack = installTemporaryTrack(context); + } catch (final IOException ex) { + Log.w(MediaStoreHack.TAG, "Error installing temporary track.", ex); + return 0; + } + final Uri filesUri = MediaStore.Files.getContentUri("external"); + final String[] selectionArgs = {temporaryTrack.getAbsolutePath()}; + final ContentResolver contentResolver = context.getContentResolver(); + Cursor cursor = + contentResolver.query( + filesUri, ALBUM_PROJECTION, MediaStore.MediaColumns.DATA + "=?", selectionArgs, null); + if (cursor == null || !cursor.moveToFirst()) { + if (cursor != null) { + cursor.close(); + cursor = null; + } + final ContentValues values = new ContentValues(); + values.put(MediaStore.MediaColumns.DATA, temporaryTrack.getAbsolutePath()); + values.put(MediaStore.MediaColumns.TITLE, "{MediaWrite Workaround}"); + values.put(MediaStore.MediaColumns.SIZE, temporaryTrack.length()); + values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mpeg"); + values.put(MediaStore.Audio.AudioColumns.IS_MUSIC, true); + contentResolver.insert(filesUri, values); + } + cursor = + contentResolver.query( + filesUri, ALBUM_PROJECTION, MediaStore.MediaColumns.DATA + "=?", selectionArgs, null); + if (cursor == null) { + return 0; + } + if (!cursor.moveToFirst()) { + cursor.close(); + return 0; + } + final int id = cursor.getInt(0); + final int albumId = cursor.getInt(1); + final int mediaType = cursor.getInt(2); + cursor.close(); + final ContentValues values = new ContentValues(); + boolean updateRequired = false; + if (albumId == 0) { + values.put(MediaStore.Audio.AlbumColumns.ALBUM_ID, 13371337); + updateRequired = true; + } + if (mediaType != 2) { + values.put("media_type", 2); + updateRequired = true; + } + if (updateRequired) { + contentResolver.update(filesUri, values, BaseColumns._ID + "=" + id, null); + } + cursor = + contentResolver.query( + filesUri, ALBUM_PROJECTION, MediaStore.MediaColumns.DATA + "=?", selectionArgs, null); + if (cursor == null) { + return 0; + } + try { + if (!cursor.moveToFirst()) { + return 0; + } + return cursor.getInt(1); + } finally { + cursor.close(); + } + } + + private static File installTemporaryTrack(final Context context) throws IOException { + final File externalFilesDir = getExternalFilesDir(context); + if (externalFilesDir == null) { + return null; + } + final File temporaryTrack = new File(externalFilesDir, "temptrack.mp3"); + if (!temporaryTrack.exists()) { + InputStream in = null; + OutputStream out = null; + try { + in = context.getResources().openRawResource(R.raw.temptrack); + out = new FileOutputStream(temporaryTrack); + final byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + } finally { + out.close(); + in.close(); + } + } + return temporaryTrack; + } + + public static boolean mkdir(final Context context, final AmazeFile file) throws IOException { + if (file.exists()) { + return file.isDirectory(); + } + final File tmpFile = new File(new File(file.getPath()), ".MediaWriteTemp"); + final int albumId = getTemporaryAlbumId(context); + if (albumId == 0) { + throw new IOException("Failed to create temporary album id."); + } + final Uri albumUri = Uri.parse(String.format(Locale.US, ALBUM_ART_URI + "/%d", albumId)); + final ContentValues values = new ContentValues(); + values.put(MediaStore.MediaColumns.DATA, tmpFile.getAbsolutePath()); + final ContentResolver contentResolver = context.getContentResolver(); + if (contentResolver.update(albumUri, values, null, null) == 0) { + values.put(MediaStore.Audio.AlbumColumns.ALBUM_ID, albumId); + contentResolver.insert(Uri.parse(ALBUM_ART_URI), values); + } + try { + final ParcelFileDescriptor fd = contentResolver.openFileDescriptor(albumUri, "r"); + fd.close(); + } finally { + delete(context, tmpFile); + } + return file.exists(); + } + + public static boolean mkfile(final Context context, final File file) { + final OutputStream outputStream = getOutputStream(context, file.getPath()); + if (outputStream == null) { + return false; + } + try { + outputStream.close(); + return true; + } catch (final IOException e) { + } + return false; + } +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/UriForSafPersistance.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/UriForSafPersistance.kt new file mode 100644 index 0000000000..197b95c746 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/UriForSafPersistance.kt @@ -0,0 +1,23 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.file + +import android.content.Context +import android.net.Uri +import androidx.preference.PreferenceManager + +object UriForSafPersistance { + const val PREFERENCE_URI = "URI" + + @JvmStatic + fun persist(context: Context, treeUri: Uri) { + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putString(PREFERENCE_URI, treeUri.toString()) + .apply() + } + + @JvmStatic + fun get(context: Context): String? { + return PreferenceManager.getDefaultSharedPreferences(context) + .getString(PREFERENCE_URI, null) + } +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java index 501f62db3d..10e0b39cc1 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -31,6 +31,7 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import android.net.Uri; import android.text.TextUtils; @@ -285,7 +286,7 @@ public boolean createFileExclusively(String pathname) throws IOException { } @Override - public boolean delete(AmazeFile f) { + public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { try { create(f.getPath()).delete(); return true; @@ -326,7 +327,7 @@ public InputStream getInputStream(AmazeFile f) { @Nullable @Override - public OutputStream getOutputStream(AmazeFile f) { + public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { try { return create(f.getPath()).getOutputStream(); } catch (IOException e) { @@ -336,7 +337,7 @@ public OutputStream getOutputStream(AmazeFile f) { } @Override - public boolean createDirectory(AmazeFile f) { + public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { try { create(f.getPath()).mkdir(); return true; diff --git a/file_operations/src/main/res/raw/temptrack.mp3 b/file_operations/src/main/res/raw/temptrack.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..9935ce793fe05274eaba57813da97887403b7c7c GIT binary patch literal 3606 zcmeZtF=k-^0uINJ09QjGLjs7aeN$616T^!#OHviW^NX?*i}FkJQffmSeVhoW@C-4+ zufj3F)0u&RNfD?jGdD3kRlguFoq?I5Gr-TCmrIHZD9r2W;SvO-6+oDS2}u5tc)J)# zF%}28J29*~C-V}>Ip^u(7~*mKZRkm^1_ut`|NrN|HBfnE7rezm>lx8vnc7;_s=s zxVQub{#&@)Pf|WDd{>RzKaC;|FJ^{UT0XuE6Id2(vc5loG1VrDVTaN+cQHSasWJb~ z>WXYi(E4;i(d&Iuf@|r_*~gyAoS2qXneOejfIDQqy6UPI`!1+EXE)4G3EWg^#c)L@ zXoAigp*c!z*3T_@OuHA&Ot6)2ZE{sUeOWrK*W=qF)%Fv%P5*Y4?ViK1c#av*`Iyx& zn~kg2TD|75zBtL-a{WHHo$-1TWw$%a&#qd$o&)GJ22WQ%mvv4FO(8BJ?m(^x5E~g7 zn&=t;kzt6Dp_Q?vm9Z&Eyi0QiOrQq`85C!N<|p)mYEc zICK;sg}^|B%Ku-J4m_KGl=%QKg23^^U;>OE1|(OFG7%v#P%-rD-v8#&7(&DYZq{fF a;SQU@Au}37gCibryJ9qkaEHyvl>q=|Al4WF literal 0 HcmV?d00001 From bc451ace04995a38d1b5775bce63ebb64675c225 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 25 Dec 2021 17:47:12 -0300 Subject: [PATCH 10/46] Move cloud accounts to file_operations modules --- app/build.gradle | 2 +- .../asynctasks/CloudLoaderAsyncTask.java | 12 +- .../asynchronous/asynctasks/DeleteTask.java | 2 +- .../asynctasks/LoadFilesListTask.java | 2 +- .../asynctasks/movecopy/MoveFiles.java | 2 +- .../filemanager/filesystem/FileUtil.java | 2 +- .../filemanager/filesystem/HybridFile.java | 70 +++---- .../filemanager/filesystem/Operations.java | 31 +-- .../filesystem/cloud/CloudUtil.java | 14 +- .../filesystem/files/FileUtils.java | 4 +- .../filesystem/files/GenericCopyUtil.java | 4 +- .../ui/activities/MainActivity.java | 2 +- .../filemanager/ui/views/drawer/Drawer.java | 6 +- .../amaze/filemanager/utils/DataUtils.java | 124 ++---------- build.gradle | 1 + file_operations/build.gradle | 4 + .../filesystem/filetypes/AmazeFile.java | 7 +- .../filesystem/filetypes/cloud/Account.kt | 22 +++ .../filetypes/cloud/box/BoxAccount.kt | 6 + .../filetypes/cloud/dropbox/DropboxAccount.kt | 7 + .../cloud/dropbox/DropboxAmazeFilesystem.java | 178 ++++++++++++++++++ .../cloud/gdrive/GoogledriveAccount.kt | 6 + .../cloud/onedrive/OnedriveAccount.kt | 7 + 23 files changed, 327 insertions(+), 188 deletions(-) create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/Account.kt create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt diff --git a/app/build.gradle b/app/build.gradle index ee3b234b40..ff35a73e1a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation "com.github.topjohnwu.libsu:core:${libsuVersion}" implementation "com.github.topjohnwu.libsu:io:${libsuVersion}" - implementation 'com.cloudrail:cloudrail-si-android:2.22.4' + implementation "com.cloudrail:cloudrail-si-android:$cloudRailVersion" implementation 'com.github.PhilJay:MPAndroidChart:v3.0.2'//Nice charts and graphs diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/CloudLoaderAsyncTask.java b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/CloudLoaderAsyncTask.java index 7ca9671a5a..da4ce1c88b 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/CloudLoaderAsyncTask.java +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/CloudLoaderAsyncTask.java @@ -28,6 +28,10 @@ import com.amaze.filemanager.database.models.explorer.CloudEntry; import com.amaze.filemanager.file_operations.exceptions.CloudPluginException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAccount; import com.amaze.filemanager.ui.activities.MainActivity; import com.amaze.filemanager.utils.DataUtils; import com.cloudrail.si.CloudRail; @@ -130,7 +134,7 @@ public Boolean doInBackground(Void... voids) { cloudHandler.addEntry(cloudEntryGdrive); } - dataUtils.addAccount(cloudStorageDrive); + GoogledriveAccount.INSTANCE.add(cloudStorageDrive); hasUpdatedDrawer = true; } catch (CloudPluginException e) { e.printStackTrace(); @@ -201,7 +205,7 @@ public Boolean doInBackground(Void... voids) { cloudHandler.addEntry(cloudEntryDropbox); } - dataUtils.addAccount(cloudStorageDropbox); + DropboxAccount.INSTANCE.add(cloudStorageDropbox); hasUpdatedDrawer = true; } catch (CloudPluginException e) { e.printStackTrace(); @@ -265,7 +269,7 @@ public Boolean doInBackground(Void... voids) { cloudHandler.addEntry(cloudEntryBox); } - dataUtils.addAccount(cloudStorageBox); + BoxAccount.INSTANCE.add(cloudStorageBox); hasUpdatedDrawer = true; } catch (CloudPluginException e) { e.printStackTrace(); @@ -330,7 +334,7 @@ public Boolean doInBackground(Void... voids) { cloudHandler.addEntry(cloudEntryOnedrive); } - dataUtils.addAccount(cloudStorageOnedrive); + OnedriveAccount.INSTANCE.add(cloudStorageOnedrive); hasUpdatedDrawer = true; } catch (CloudPluginException e) { e.printStackTrace(); diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/DeleteTask.java b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/DeleteTask.java index 9eb5b99e5b..e8cba0f242 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/DeleteTask.java +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/DeleteTask.java @@ -172,7 +172,7 @@ private boolean doDeleteFile(@NonNull HybridFileParcelable file) throws Exceptio case BOX: case GDRIVE: case ONEDRIVE: - CloudStorage cloudStorage = dataUtils.getAccount(file.getMode()); + CloudStorage cloudStorage = dataUtils.getAccount(file.getMode()).getAccount(); try { cloudStorage.delete(CloudUtil.stripPath(file.getMode(), file.getPath())); return true; diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java index 98b5a6c8a8..e325c81898 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java @@ -225,7 +225,7 @@ public LoadFilesListTask( case BOX: case GDRIVE: case ONEDRIVE: - CloudStorage cloudStorage = dataUtils.getAccount(openmode); + CloudStorage cloudStorage = dataUtils.getAccount(openmode).getAccount(); list = new ArrayList<>(); try { diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/movecopy/MoveFiles.java b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/movecopy/MoveFiles.java index d4b6b6c9f1..12395b0ae5 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/movecopy/MoveFiles.java +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/movecopy/MoveFiles.java @@ -131,7 +131,7 @@ private MoveFilesReturn processFile( case GDRIVE: DataUtils dataUtils = DataUtils.getInstance(); - CloudStorage cloudStorage = dataUtils.getAccount(mode); + CloudStorage cloudStorage = dataUtils.getAccount(mode).getAccount(); if (baseFile.getMode() == mode) { // source and target both in same filesystem, use API method try { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java index 329089b3a0..7e26b012e6 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java @@ -197,7 +197,7 @@ public static final void writeUriToStorage( case GDRIVE: OpenMode mode = hFile.getMode(); - CloudStorage cloudStorage = dataUtils.getAccount(mode); + CloudStorage cloudStorage = dataUtils.getAccount(mode).getAccount(); String path = CloudUtil.stripPath(mode, finalFilePath); cloudStorage.upload(path, bufferedInputStream, documentFile.length(), true); retval.add(path); diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 3aacfb2d11..954d828016 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -43,6 +43,10 @@ import com.amaze.filemanager.file_operations.exceptions.ShellNotRunningException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAccount; import com.amaze.filemanager.file_operations.filesystem.root.NativeOperations; import com.amaze.filemanager.filesystem.cloud.CloudUtil; import com.amaze.filemanager.filesystem.files.FileUtils; @@ -304,30 +308,22 @@ public long length(Context context) { s = OTGUtil.getDocumentFile(path, context, false).length(); break; case DROPBOX: - s = - dataUtils - .getAccount(OpenMode.DROPBOX) + s = DropboxAccount.INSTANCE.getAccount() .getMetadata(CloudUtil.stripPath(OpenMode.DROPBOX, path)) .getSize(); break; case BOX: - s = - dataUtils - .getAccount(OpenMode.BOX) + s = BoxAccount.INSTANCE.getAccount() .getMetadata(CloudUtil.stripPath(OpenMode.BOX, path)) .getSize(); break; case ONEDRIVE: - s = - dataUtils - .getAccount(OpenMode.ONEDRIVE) + s = OnedriveAccount.INSTANCE.getAccount() .getMetadata(CloudUtil.stripPath(OpenMode.ONEDRIVE, path)) .getSize(); break; case GDRIVE: - s = - dataUtils - .getAccount(OpenMode.GDRIVE) + s = GoogledriveAccount.INSTANCE.getAccount() .getMetadata(CloudUtil.stripPath(OpenMode.GDRIVE, path)) .getSize(); break; @@ -500,30 +496,22 @@ public Boolean execute(SFTPClient client) { isDirectory = OTGUtil.getDocumentFile(path, context, false).isDirectory(); break; case DROPBOX: - isDirectory = - dataUtils - .getAccount(OpenMode.DROPBOX) + isDirectory = DropboxAccount.INSTANCE.getAccount() .getMetadata(CloudUtil.stripPath(OpenMode.DROPBOX, path)) .getFolder(); break; case BOX: - isDirectory = - dataUtils - .getAccount(OpenMode.BOX) + isDirectory = BoxAccount.INSTANCE.getAccount() .getMetadata(CloudUtil.stripPath(OpenMode.BOX, path)) .getFolder(); break; case GDRIVE: - isDirectory = - dataUtils - .getAccount(OpenMode.GDRIVE) + isDirectory = GoogledriveAccount.INSTANCE.getAccount() .getMetadata(CloudUtil.stripPath(OpenMode.GDRIVE, path)) .getFolder(); break; case ONEDRIVE: - isDirectory = - dataUtils - .getAccount(OpenMode.ONEDRIVE) + isDirectory = OnedriveAccount.INSTANCE.getAccount() .getMetadata(CloudUtil.stripPath(OpenMode.ONEDRIVE, path)) .getFolder(); break; @@ -600,7 +588,7 @@ public Long execute(SFTPClient client) throws IOException { case ONEDRIVE: size = FileUtils.folderSizeCloud( - mode, dataUtils.getAccount(mode).getMetadata(CloudUtil.stripPath(mode, path))); + mode, dataUtils.getAccount(mode).getAccount().getMetadata(CloudUtil.stripPath(mode, path))); break; default: return 0l; @@ -620,7 +608,7 @@ public long getUsableSpace() { case BOX: case GDRIVE: case ONEDRIVE: - SpaceAllocation spaceAllocation = dataUtils.getAccount(mode).getAllocation(); + SpaceAllocation spaceAllocation = dataUtils.getAccount(mode).getAccount().getAllocation(); size = spaceAllocation.getTotal() - spaceAllocation.getUsed(); break; case SFTP: @@ -679,7 +667,7 @@ public long getTotal(Context context) { case BOX: case ONEDRIVE: case GDRIVE: - SpaceAllocation spaceAllocation = dataUtils.getAccount(mode).getAllocation(); + SpaceAllocation spaceAllocation = dataUtils.getAccount(mode).getAccount().getAllocation(); size = spaceAllocation.getTotal(); break; case SFTP: @@ -791,7 +779,7 @@ public Boolean execute(SFTPClient client) { case GDRIVE: case ONEDRIVE: try { - CloudUtil.getCloudFiles(path, dataUtils.getAccount(mode), mode, onFileFound); + CloudUtil.getCloudFiles(path, dataUtils.getAccount(mode).getAccount(), mode, onFileFound); } catch (CloudPluginException e) { e.printStackTrace(); } @@ -874,7 +862,7 @@ public ArrayList execute(SFTPClient client) { case GDRIVE: case ONEDRIVE: try { - arrayList = CloudUtil.listFiles(path, dataUtils.getAccount(mode), mode); + arrayList = CloudUtil.listFiles(path, dataUtils.getAccount(mode).getAccount(), mode); } catch (CloudPluginException e) { e.printStackTrace(); arrayList = new ArrayList<>(); @@ -957,20 +945,20 @@ public void close() throws IOException { } break; case DROPBOX: - CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX); + CloudStorage cloudStorageDropbox = DropboxAccount.INSTANCE.getAccount(); Log.d(getClass().getSimpleName(), CloudUtil.stripPath(OpenMode.DROPBOX, path)); inputStream = cloudStorageDropbox.download(CloudUtil.stripPath(OpenMode.DROPBOX, path)); break; case BOX: - CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX); + CloudStorage cloudStorageBox = BoxAccount.INSTANCE.getAccount(); inputStream = cloudStorageBox.download(CloudUtil.stripPath(OpenMode.BOX, path)); break; case GDRIVE: - CloudStorage cloudStorageGDrive = dataUtils.getAccount(OpenMode.GDRIVE); + CloudStorage cloudStorageGDrive = GoogledriveAccount.INSTANCE.getAccount(); inputStream = cloudStorageGDrive.download(CloudUtil.stripPath(OpenMode.GDRIVE, path)); break; case ONEDRIVE: - CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE); + CloudStorage cloudStorageOneDrive = OnedriveAccount.INSTANCE.getAccount(); inputStream = cloudStorageOneDrive.download(CloudUtil.stripPath(OpenMode.ONEDRIVE, path)); break; default: @@ -1077,16 +1065,16 @@ public Boolean execute(SFTPClient client) throws IOException { } else if (isSmb() || isLocal()) { return new AmazeFile(path).exists(); } else if (isDropBoxFile()) { - CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX); + CloudStorage cloudStorageDropbox = DropboxAccount.INSTANCE.getAccount(); exists = cloudStorageDropbox.exists(CloudUtil.stripPath(OpenMode.DROPBOX, path)); } else if (isBoxFile()) { - CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX); + CloudStorage cloudStorageBox = BoxAccount.INSTANCE.getAccount(); exists = cloudStorageBox.exists(CloudUtil.stripPath(OpenMode.BOX, path)); } else if (isGoogleDriveFile()) { - CloudStorage cloudStorageGoogleDrive = dataUtils.getAccount(OpenMode.GDRIVE); + CloudStorage cloudStorageGoogleDrive = GoogledriveAccount.INSTANCE.getAccount(); exists = cloudStorageGoogleDrive.exists(CloudUtil.stripPath(OpenMode.GDRIVE, path)); } else if (isOneDriveFile()) { - CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE); + CloudStorage cloudStorageOneDrive = OnedriveAccount.INSTANCE.getAccount(); exists = cloudStorageOneDrive.exists(CloudUtil.stripPath(OpenMode.ONEDRIVE, path)); } else if (isRoot()) { return RootHelper.fileExists(path); @@ -1178,28 +1166,28 @@ public Void execute(@NonNull SFTPClient client) { } } } else if (isDropBoxFile()) { - CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX); + CloudStorage cloudStorageDropbox = DropboxAccount.INSTANCE.getAccount(); try { cloudStorageDropbox.createFolder(CloudUtil.stripPath(OpenMode.DROPBOX, path)); } catch (Exception e) { e.printStackTrace(); } } else if (isBoxFile()) { - CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX); + CloudStorage cloudStorageBox = BoxAccount.INSTANCE.getAccount(); try { cloudStorageBox.createFolder(CloudUtil.stripPath(OpenMode.BOX, path)); } catch (Exception e) { e.printStackTrace(); } } else if (isOneDriveFile()) { - CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE); + CloudStorage cloudStorageOneDrive = OnedriveAccount.INSTANCE.getAccount(); try { cloudStorageOneDrive.createFolder(CloudUtil.stripPath(OpenMode.ONEDRIVE, path)); } catch (Exception e) { e.printStackTrace(); } } else if (isGoogleDriveFile()) { - CloudStorage cloudStorageGdrive = dataUtils.getAccount(OpenMode.GDRIVE); + CloudStorage cloudStorageGdrive = GoogledriveAccount.INSTANCE.getAccount(); try { cloudStorageGdrive.createFolder(CloudUtil.stripPath(OpenMode.GDRIVE, path)); } catch (Exception e) { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java index 77c40a1b05..de126c73b1 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java @@ -35,6 +35,10 @@ import com.amaze.filemanager.file_operations.exceptions.ShellNotRunningException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAccount; import com.amaze.filemanager.filesystem.cloud.CloudUtil; import com.amaze.filemanager.filesystem.files.FileUtils; import com.amaze.filemanager.filesystem.root.MakeDirectoryCommand; @@ -116,9 +120,6 @@ public static void mkdir( @NonNull final ErrorCallBack errorCallBack) { new AsyncTask() { - - private DataUtils dataUtils = DataUtils.getInstance(); - private Function safCreateDirectory = input -> { if (input != null && input.isDirectory()) { @@ -174,7 +175,7 @@ protected Void doInBackground(Void... params) { false)); return null; } else if (file.isDropBoxFile()) { - CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX); + CloudStorage cloudStorageDropbox = DropboxAccount.INSTANCE.getAccount(); try { cloudStorageDropbox.createFolder(CloudUtil.stripPath(OpenMode.DROPBOX, file.getPath())); errorCallBack.done(file, true); @@ -183,7 +184,7 @@ protected Void doInBackground(Void... params) { errorCallBack.done(file, false); } } else if (file.isBoxFile()) { - CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX); + CloudStorage cloudStorageBox = BoxAccount.INSTANCE.getAccount(); try { cloudStorageBox.createFolder(CloudUtil.stripPath(OpenMode.BOX, file.getPath())); errorCallBack.done(file, true); @@ -192,7 +193,7 @@ protected Void doInBackground(Void... params) { errorCallBack.done(file, false); } } else if (file.isOneDriveFile()) { - CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE); + CloudStorage cloudStorageOneDrive = OnedriveAccount.INSTANCE.getAccount(); try { cloudStorageOneDrive.createFolder( CloudUtil.stripPath(OpenMode.ONEDRIVE, file.getPath())); @@ -202,7 +203,7 @@ protected Void doInBackground(Void... params) { errorCallBack.done(file, false); } } else if (file.isGoogleDriveFile()) { - CloudStorage cloudStorageGdrive = dataUtils.getAccount(OpenMode.GDRIVE); + CloudStorage cloudStorageGdrive = GoogledriveAccount.INSTANCE.getAccount(); try { cloudStorageGdrive.createFolder(CloudUtil.stripPath(OpenMode.GDRIVE, file.getPath())); errorCallBack.done(file, true); @@ -308,7 +309,7 @@ protected Void doInBackground(Void... params) { errorCallBack.done(file, file.exists()); return null; } else if (file.isDropBoxFile()) { - CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX); + CloudStorage cloudStorageDropbox = DropboxAccount.INSTANCE.getAccount(); try { byte[] tempBytes = new byte[0]; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(tempBytes); @@ -323,7 +324,7 @@ protected Void doInBackground(Void... params) { errorCallBack.done(file, false); } } else if (file.isBoxFile()) { - CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX); + CloudStorage cloudStorageBox = BoxAccount.INSTANCE.getAccount(); try { byte[] tempBytes = new byte[0]; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(tempBytes); @@ -335,7 +336,7 @@ protected Void doInBackground(Void... params) { errorCallBack.done(file, false); } } else if (file.isOneDriveFile()) { - CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE); + CloudStorage cloudStorageOneDrive = OnedriveAccount.INSTANCE.getAccount(); try { byte[] tempBytes = new byte[0]; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(tempBytes); @@ -350,7 +351,7 @@ protected Void doInBackground(Void... params) { errorCallBack.done(file, false); } } else if (file.isGoogleDriveFile()) { - CloudStorage cloudStorageGdrive = dataUtils.getAccount(OpenMode.GDRIVE); + CloudStorage cloudStorageGdrive = GoogledriveAccount.INSTANCE.getAccount(); try { byte[] tempBytes = new byte[0]; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(tempBytes); @@ -509,7 +510,7 @@ public Void execute(@NonNull SFTPClient client) { } }); } else if (oldFile.isDropBoxFile()) { - CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX); + CloudStorage cloudStorageDropbox = DropboxAccount.INSTANCE.getAccount(); try { cloudStorageDropbox.move( CloudUtil.stripPath(OpenMode.DROPBOX, oldFile.getPath()), @@ -520,7 +521,7 @@ public Void execute(@NonNull SFTPClient client) { errorCallBack.done(newFile, false); } } else if (oldFile.isBoxFile()) { - CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX); + CloudStorage cloudStorageBox = BoxAccount.INSTANCE.getAccount(); try { cloudStorageBox.move( CloudUtil.stripPath(OpenMode.BOX, oldFile.getPath()), @@ -531,7 +532,7 @@ public Void execute(@NonNull SFTPClient client) { errorCallBack.done(newFile, false); } } else if (oldFile.isOneDriveFile()) { - CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE); + CloudStorage cloudStorageOneDrive = OnedriveAccount.INSTANCE.getAccount(); try { cloudStorageOneDrive.move( CloudUtil.stripPath(OpenMode.ONEDRIVE, oldFile.getPath()), @@ -542,7 +543,7 @@ public Void execute(@NonNull SFTPClient client) { errorCallBack.done(newFile, false); } } else if (oldFile.isGoogleDriveFile()) { - CloudStorage cloudStorageGdrive = dataUtils.getAccount(OpenMode.GDRIVE); + CloudStorage cloudStorageGdrive = GoogledriveAccount.INSTANCE.getAccount(); try { cloudStorageGdrive.move( CloudUtil.stripPath(OpenMode.GDRIVE, oldFile.getPath()), diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java index f2e185ac94..5a00424c62 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java @@ -35,6 +35,10 @@ import com.amaze.filemanager.file_operations.filesystem.OpenMode; import com.amaze.filemanager.file_operations.filesystem.cloud.CloudStreamer; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAccount; import com.amaze.filemanager.filesystem.HybridFile; import com.amaze.filemanager.filesystem.HybridFileParcelable; import com.amaze.filemanager.filesystem.ssh.SFtpClientTemplate; @@ -201,18 +205,18 @@ protected Boolean doInBackground(String... params) { if (path.startsWith(CloudHandler.CLOUD_PREFIX_DROPBOX)) { // dropbox account serviceType = OpenMode.DROPBOX; - cloudStorage = dataUtils.getAccount(OpenMode.DROPBOX); + cloudStorage = DropboxAccount.INSTANCE.getAccount(); } else if (path.startsWith(CloudHandler.CLOUD_PREFIX_ONE_DRIVE)) { serviceType = OpenMode.ONEDRIVE; - cloudStorage = dataUtils.getAccount(OpenMode.ONEDRIVE); + cloudStorage = OnedriveAccount.INSTANCE.getAccount(); } else if (path.startsWith(CloudHandler.CLOUD_PREFIX_BOX)) { serviceType = OpenMode.BOX; - cloudStorage = dataUtils.getAccount(OpenMode.BOX); + cloudStorage = BoxAccount.INSTANCE.getAccount(); } else if (path.startsWith(CloudHandler.CLOUD_PREFIX_GOOGLE_DRIVE)) { serviceType = OpenMode.GDRIVE; - cloudStorage = dataUtils.getAccount(OpenMode.GDRIVE); + cloudStorage = GoogledriveAccount.INSTANCE.getAccount(); } else { throw new IllegalStateException(); } @@ -296,7 +300,7 @@ public void close() throws IOException { case ONEDRIVE: OpenMode mode = hybridFile.getMode(); - CloudStorage cloudStorageDropbox = dataUtils.getAccount(mode); + CloudStorage cloudStorageDropbox = dataUtils.getAccount(mode).getAccount(); String stripped = CloudUtil.stripPath(mode, hybridFile.getPath()); inputStream = cloudStorageDropbox.getThumbnail(stripped); break; diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java index 943f32eece..4f261847a2 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java @@ -168,7 +168,7 @@ public static long folderSizeCloud(OpenMode openMode, CloudMetaData sourceFileMe DataUtils dataUtils = DataUtils.getInstance(); long length = 0; - CloudStorage cloudStorage = dataUtils.getAccount(openMode); + CloudStorage cloudStorage = dataUtils.getAccount(openMode).getAccount(); for (CloudMetaData metaData : cloudStorage.getChildren(CloudUtil.stripPath(openMode, sourceFileMeta.getPath()))) { @@ -330,7 +330,7 @@ public static void shareCloudFile(String path, final OpenMode openMode, final Co @Override protected String doInBackground(String... params) { String shareFilePath = params[0]; - CloudStorage cloudStorage = DataUtils.getInstance().getAccount(openMode); + CloudStorage cloudStorage = DataUtils.getInstance().getAccount(openMode).getAccount(); return cloudStorage.createShareLink(CloudUtil.stripPath(openMode, shareFilePath)); } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/GenericCopyUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/files/GenericCopyUtil.java index a2e53f4948..d96b916c27 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/files/GenericCopyUtil.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/GenericCopyUtil.java @@ -130,7 +130,7 @@ private void startCopy( || mSourceFile.isOneDriveFile()) { OpenMode openMode = mSourceFile.getMode(); - CloudStorage cloudStorage = dataUtils.getAccount(openMode); + CloudStorage cloudStorage = dataUtils.getAccount(openMode).getAccount(); bufferedInputStream = new BufferedInputStream( cloudStorage.download(CloudUtil.stripPath(openMode, mSourceFile.getPath()))); @@ -273,7 +273,7 @@ private void cloudCopy( throws IOException { DataUtils dataUtils = DataUtils.getInstance(); // API doesn't support output stream, we'll upload the file directly - CloudStorage cloudStorage = dataUtils.getAccount(openMode); + CloudStorage cloudStorage = dataUtils.getAccount(openMode).getAccount(); if (mSourceFile.getMode() == openMode) { // we're in the same provider, use api method diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java index b45589dc5c..bb4988f7c5 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java @@ -2105,7 +2105,7 @@ this, getResources().getString(R.string.connection_exists), Toast.LENGTH_LONG) @Override public void deleteConnection(OpenMode service) { cloudHandler.clear(service); - dataUtils.removeAccount(service); + dataUtils.getAccount(service).removeAccount(); runOnUiThread(drawer::refreshDrawer); } diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java b/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java index ec956f0ab5..abc57e1724 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java +++ b/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java @@ -36,6 +36,7 @@ import com.amaze.filemanager.application.AppConfig; import com.amaze.filemanager.database.CloudHandler; import com.amaze.filemanager.file_operations.filesystem.OpenMode; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; import com.amaze.filemanager.file_operations.filesystem.usb.SingletonUsbOtg; import com.amaze.filemanager.filesystem.HybridFile; import com.amaze.filemanager.filesystem.RootHelper; @@ -334,7 +335,8 @@ public void refreshDrawer() { ArrayList accountAuthenticationList = new ArrayList<>(); if (CloudSheetFragment.isCloudProviderAvailable(mainActivity)) { - for (CloudStorage cloudStorage : dataUtils.getAccounts()) { + for (Account account : Account.Companion.getAccounts()) { + CloudStorage cloudStorage = account.getAccount(); @DrawableRes int deleteIcon = R.drawable.ic_delete_grey_24dp; if (cloudStorage instanceof Dropbox) { @@ -727,7 +729,7 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) { FileUtils.checkForPath(mainActivity, meta.path, mainActivity.isRootExplorer()); } - if (dataUtils.getAccounts().size() > 0 + if (Account.Companion.getAccounts().size() > 0 && (meta.path.startsWith(CloudHandler.CLOUD_PREFIX_BOX) || meta.path.startsWith(CloudHandler.CLOUD_PREFIX_DROPBOX) || meta.path.startsWith(CloudHandler.CLOUD_PREFIX_ONE_DRIVE) diff --git a/app/src/main/java/com/amaze/filemanager/utils/DataUtils.java b/app/src/main/java/com/amaze/filemanager/utils/DataUtils.java index 42a0149b1d..bc844a5f58 100644 --- a/app/src/main/java/com/amaze/filemanager/utils/DataUtils.java +++ b/app/src/main/java/com/amaze/filemanager/utils/DataUtils.java @@ -28,11 +28,11 @@ import com.amaze.filemanager.adapters.data.LayoutElementParcelable; import com.amaze.filemanager.application.AppConfig; import com.amaze.filemanager.file_operations.filesystem.OpenMode; -import com.cloudrail.si.interfaces.CloudStorage; -import com.cloudrail.si.services.Box; -import com.cloudrail.si.services.Dropbox; -import com.cloudrail.si.services.GoogleDrive; -import com.cloudrail.si.services.OneDrive; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAccount; import com.googlecode.concurrenttrees.radix.ConcurrentRadixTree; import com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharArrayNodeFactory; import com.googlecode.concurrenttrees.radix.node.concrete.voidvalue.VoidValue; @@ -69,8 +69,6 @@ public class DataUtils { private ArrayList servers = new ArrayList<>(); private ArrayList books = new ArrayList<>(); - private ArrayList accounts = new ArrayList<>(4); - /** List of checked items to persist when drag and drop from one tab to another */ private ArrayList checkedItemsList; @@ -107,41 +105,6 @@ public int containsBooks(String[] a) { return contains(a, books); } - /*public int containsAccounts(CloudEntry cloudEntry) { - return contains(a, accounts); - }*/ - - /** - * Checks whether cloud account of certain type is present or not - * - * @param serviceType the {@link OpenMode} of account to check - * @return the index of account, -1 if not found - */ - public synchronized int containsAccounts(OpenMode serviceType) { - int i = 0; - for (CloudStorage storage : accounts) { - - switch (serviceType) { - case BOX: - if (storage instanceof Box) return i; - break; - case DROPBOX: - if (storage instanceof Dropbox) return i; - break; - case GDRIVE: - if (storage instanceof GoogleDrive) return i; - break; - case ONEDRIVE: - if (storage instanceof OneDrive) return i; - break; - default: - return -1; - } - i++; - } - return -1; - } - public void clear() { hiddenfiles = new ConcurrentRadixTree<>(new DefaultCharArrayNodeFactory()); filesGridOrList = new ConcurrentInvertedRadixTree<>(new DefaultCharArrayNodeFactory()); @@ -150,7 +113,6 @@ public void clear() { tree = new ConcurrentInvertedRadixTree<>(new DefaultCharArrayNodeFactory()); servers = new ArrayList<>(); books = new ArrayList<>(); - accounts = new ArrayList<>(); } public void registerOnDataChangedListener(DataChangeListener l) { @@ -184,39 +146,6 @@ public void removeBook(int i) { } } - public synchronized void removeAccount(OpenMode serviceType) { - for (CloudStorage storage : accounts) { - switch (serviceType) { - case BOX: - if (storage instanceof Box) { - accounts.remove(storage); - return; - } - break; - case DROPBOX: - if (storage instanceof Dropbox) { - accounts.remove(storage); - return; - } - break; - case GDRIVE: - if (storage instanceof GoogleDrive) { - accounts.remove(storage); - return; - } - break; - case ONEDRIVE: - if (storage instanceof OneDrive) { - accounts.remove(storage); - return; - } - break; - default: - return; - } - } - } - public void removeServer(int i) { synchronized (servers) { if (servers.size() > i) servers.remove(i); @@ -254,10 +183,6 @@ public boolean addBook(final String[] i, boolean refreshdrawer) { } } - public void addAccount(CloudStorage storage) { - accounts.add(storage); - } - public void addServer(String[] i) { servers.add(i); } @@ -310,10 +235,6 @@ public synchronized void setBooks(ArrayList books) { if (books != null) this.books = books; } - public synchronized void setAccounts(ArrayList accounts) { - if (accounts != null) this.accounts = accounts; - } - public synchronized ArrayList getServers() { return servers; } @@ -322,30 +243,19 @@ public synchronized ArrayList getBooks() { return books; } - public synchronized ArrayList getAccounts() { - return accounts; - } - - public synchronized CloudStorage getAccount(OpenMode serviceType) { - for (CloudStorage storage : accounts) { - switch (serviceType) { - case BOX: - if (storage instanceof Box) return storage; - break; - case DROPBOX: - if (storage instanceof Dropbox) return storage; - break; - case GDRIVE: - if (storage instanceof GoogleDrive) return storage; - break; - case ONEDRIVE: - if (storage instanceof OneDrive) return storage; - break; - default: - return null; - } + public synchronized Account getAccount(OpenMode serviceType) { + switch (serviceType) { + case BOX: + return BoxAccount.INSTANCE; + case DROPBOX: + return DropboxAccount.INSTANCE; + case GDRIVE: + return GoogledriveAccount.INSTANCE; + case ONEDRIVE: + return OnedriveAccount.INSTANCE; + default: + return null; } - return null; } public boolean isFileHidden(String path) { diff --git a/build.gradle b/build.gradle index 763e164bdb..48dd55578d 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,7 @@ buildscript { mockkVersion = "1.12.2" reactiveXVersion = "2.2.9" reactiveXAndroidVersion = "2.1.1" + cloudRailVersion = "2.22.4" } repositories { google() diff --git a/file_operations/build.gradle b/file_operations/build.gradle index fb9f0bb57b..0c700a9b9a 100644 --- a/file_operations/build.gradle +++ b/file_operations/build.gradle @@ -61,6 +61,7 @@ dependencies { } } + //File implementation 'androidx.documentfile:documentfile:1.0.1' implementation "androidx.preference:preference:$androidXPrefVersion" implementation "androidx.preference:preference-ktx:$androidXPrefVersion" @@ -80,6 +81,9 @@ dependencies { //smb implementation "eu.agno3.jcifs:jcifs-ng:$jcifsVersion" + //Cloud + implementation "com.cloudrail:cloudrail-si-android:$cloudRailVersion" + implementation 'androidx.multidex:multidex:2.0.1'//Multiple dex files //ReactiveX diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 13b3616914..dcfa68042e 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -25,7 +25,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.lang.ref.WeakReference; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; @@ -33,13 +32,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.function.Supplier; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.file.FileAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; -import android.content.Context; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -419,6 +416,8 @@ private void loadFilesystem(String path) { fs = SmbAmazeFileSystem.INSTANCE; } else if (FileAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { fs = FileAmazeFilesystem.INSTANCE; + } else if (DropboxAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { + fs = DropboxAmazeFilesystem.INSTANCE; } } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/Account.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/Account.kt new file mode 100644 index 0000000000..6854d1ace1 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/Account.kt @@ -0,0 +1,22 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud + +import com.cloudrail.si.interfaces.CloudStorage + +abstract class Account { + companion object { + val accounts = mutableListOf() + } + + var account: CloudStorage? = null + protected set + + fun add(storage: CloudStorage) { + account = storage + accounts.add(this) + } + + fun removeAccount() { + account = null + accounts.remove(this) + } +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt new file mode 100644 index 0000000000..ca8d4e3e8b --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt @@ -0,0 +1,6 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box + +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account + +object BoxAccount: Account() { +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt new file mode 100644 index 0000000000..144d6bcad6 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt @@ -0,0 +1,7 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox + +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account + +object DropboxAccount: Account() { + +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java new file mode 100644 index 0000000000..4d62d2cd4c --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java @@ -0,0 +1,178 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class DropboxAmazeFilesystem extends AmazeFileSystem { + public static final String TAG = DropboxAmazeFilesystem.class.getSimpleName(); + + public static final String PREFIX = ""; + public static final char SEPARATOR = '/'; + + public static final DropboxAmazeFilesystem INSTANCE = new DropboxAmazeFilesystem(); + + private DropboxAmazeFilesystem() {} + + @Override + public boolean isPathOfThisFilesystem(@NonNull String path) { + return path.startsWith(SmbAmazeFileSystem.PREFIX); + } + + @Override + public char getSeparator() { + return SEPARATOR; + } + + @Override + public char getPathSeparator() { + return 0; + } + + @NonNull + @Override + public String normalize(@NonNull String path) { + return null; + } + + @Override + public int prefixLength(@NonNull String path) { + return 0; + } + + @NonNull + @Override + public String resolve(String parent, String child) { + return null; + } + + @NonNull + @Override + public String getDefaultParent() { + return null; + } + + @NonNull + @Override + public String fromURIPath(@NonNull String path) { + return null; + } + + @Override + public boolean isAbsolute(AmazeFile f) { + return false; + } + + @NonNull + @Override + public String resolve(AmazeFile f) { + return null; + } + + @NonNull + @Override + public String canonicalize(String path) throws IOException { + return null; + } + + @Override + public int getBooleanAttributes(AmazeFile f) { + return 0; + } + + @Override + public boolean checkAccess(AmazeFile f, int access) { + return false; + } + + @Override + public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { + return false; + } + + @Override + public long getLastModifiedTime(AmazeFile f) { + return 0; + } + + @Override + public long getLength(AmazeFile f) throws IOException { + return 0; + } + + @Override + public boolean createFileExclusively(String pathname) throws IOException { + return false; + } + + @Override + public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { + return false; + } + + @Nullable + @Override + public String[] list(AmazeFile f) { + return new String[0]; + } + + @Nullable + @Override + public InputStream getInputStream(AmazeFile f) { + return null; + } + + @Nullable + @Override + public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { + return null; + } + + @Override + public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { + return false; + } + + @Override + public boolean rename(AmazeFile f1, AmazeFile f2) { + return false; + } + + @Override + public boolean setLastModifiedTime(AmazeFile f, long time) { + return false; + } + + @Override + public boolean setReadOnly(AmazeFile f) { + return false; + } + + @Override + public AmazeFile[] listRoots() { + return new AmazeFile[0]; + } + + @Override + public long getSpace(AmazeFile f, int t) { + return 0; + } + + @Override + public int compare(AmazeFile f1, AmazeFile f2) { + return 0; + } + + @Override + public int hashCode(AmazeFile f) { + return 0; + } +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt new file mode 100644 index 0000000000..5a17caa052 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt @@ -0,0 +1,6 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive + +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account + +object GoogledriveAccount: Account() { +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt new file mode 100644 index 0000000000..2baf2628a1 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt @@ -0,0 +1,7 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive + +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account + +object OnedriveAccount: Account() { + +} \ No newline at end of file From 52d40195244c6885ba33e55e4eb3449d3f8b335a Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 26 Dec 2021 10:30:09 -0300 Subject: [PATCH 11/46] Add exception for impossible case in Smb file --- .../filesystem/filetypes/smb/SmbAmazeFileSystem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java index 10e0b39cc1..ce07840e7c 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -98,7 +98,7 @@ public String normalize(@NonNull String pathname) { @Override public int prefixLength(@NonNull String path) { if (path.length() == 0) { - return 0; + throw new IllegalArgumentException("This should never happen, all paths must start with SMB prefix"); } Matcher matcherMetadata = METADATA_PATTERN.matcher(path); From 6316891a3f336467be670bcfbd37911f30230742 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 26 Dec 2021 11:46:45 -0300 Subject: [PATCH 12/46] Cloud to new file system --- .../filemanager/database/CloudHandler.java | 12 +- .../filemanager/filesystem/HybridFile.java | 172 +++-------- .../filesystem/filetypes/AmazeFile.java | 10 + .../filesystem/filetypes/AmazeFileSystem.java | 20 +- .../filetypes/cloud/CloudAmazeFilesystem.java | 276 ++++++++++++++++++ .../filetypes/cloud/box/BoxAccount.kt | 3 +- .../cloud/box/BoxAmazeFilesystem.java | 25 ++ .../filetypes/cloud/dropbox/DropboxAccount.kt | 4 +- .../cloud/dropbox/DropboxAmazeFilesystem.java | 170 +---------- .../cloud/gdrive/GoogledriveAccount.kt | 3 +- .../gdrive/GoogledriveAmazeFilesystem.java | 24 ++ .../cloud/onedrive/OnedriveAccount.kt | 4 +- .../onedrive/OnedriveAmazeFilesystem.java | 25 ++ .../filetypes/smb/SmbAmazeFileSystem.java | 10 - 14 files changed, 437 insertions(+), 321 deletions(-) create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java diff --git a/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java b/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java index 9a50f36137..58007f1cc6 100644 --- a/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java +++ b/app/src/main/java/com/amaze/filemanager/database/CloudHandler.java @@ -25,6 +25,10 @@ import com.amaze.filemanager.database.models.explorer.CloudEntry; import com.amaze.filemanager.file_operations.exceptions.CloudPluginException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAmazeFilesystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAmazeFilesystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAmazeFilesystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAmazeFilesystem; import com.amaze.filemanager.ui.fragments.CloudSheetFragment; import android.content.Context; @@ -37,10 +41,10 @@ /** Created by vishal on 18/4/17. */ public class CloudHandler { - public static final String CLOUD_PREFIX_BOX = "box:/"; - public static final String CLOUD_PREFIX_DROPBOX = "dropbox:/"; - public static final String CLOUD_PREFIX_GOOGLE_DRIVE = "gdrive:/"; - public static final String CLOUD_PREFIX_ONE_DRIVE = "onedrive:/"; + public static final String CLOUD_PREFIX_BOX = BoxAmazeFilesystem.PREFIX; + public static final String CLOUD_PREFIX_DROPBOX = DropboxAmazeFilesystem.PREFIX; + public static final String CLOUD_PREFIX_GOOGLE_DRIVE = GoogledriveAmazeFilesystem.PREFIX; + public static final String CLOUD_PREFIX_ONE_DRIVE = OnedriveAmazeFilesystem.PREFIX; public static final String CLOUD_NAME_GOOGLE_DRIVE = "Google Drive™"; public static final String CLOUD_NAME_DROPBOX = "Dropbox"; diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 954d828016..db29d1f5a7 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -292,6 +292,10 @@ public long length(Context context) { return ((HybridFileParcelable) this).getSize(); case SMB: case FILE: + case DROPBOX: + case BOX: + case ONEDRIVE: + case GDRIVE: try { return new AmazeFile(path).length(); } catch (IOException e) { @@ -307,26 +311,6 @@ public long length(Context context) { case OTG: s = OTGUtil.getDocumentFile(path, context, false).length(); break; - case DROPBOX: - s = DropboxAccount.INSTANCE.getAccount() - .getMetadata(CloudUtil.stripPath(OpenMode.DROPBOX, path)) - .getSize(); - break; - case BOX: - s = BoxAccount.INSTANCE.getAccount() - .getMetadata(CloudUtil.stripPath(OpenMode.BOX, path)) - .getSize(); - break; - case ONEDRIVE: - s = OnedriveAccount.INSTANCE.getAccount() - .getMetadata(CloudUtil.stripPath(OpenMode.ONEDRIVE, path)) - .getSize(); - break; - case GDRIVE: - s = GoogledriveAccount.INSTANCE.getAccount() - .getMetadata(CloudUtil.stripPath(OpenMode.GDRIVE, path)) - .getSize(); - break; default: break; } @@ -354,6 +338,10 @@ public String getName(Context context) { switch (mode) { case SMB: case FILE: + case DROPBOX: + case BOX: + case ONEDRIVE: + case GDRIVE: return new AmazeFile(path).getName(); case ROOT: return getFile().getName(); @@ -401,6 +389,10 @@ public String getParent(Context context) { case SMB: case FILE: case ROOT: + case DROPBOX: + case BOX: + case ONEDRIVE: + case GDRIVE: return new AmazeFile(path).getParent(); case SFTP: default: @@ -435,6 +427,10 @@ public boolean isDirectory() { return isDirectory(AppConfig.getInstance()); case SMB: case FILE: + case DROPBOX: + case BOX: + case ONEDRIVE: + case GDRIVE: return new AmazeFile(path).isDirectory(); case ROOT: isDirectory = NativeOperations.isDirectory(path); @@ -485,6 +481,10 @@ public Boolean execute(SFTPClient client) { return returnValue == null ? false : returnValue; case SMB: case FILE: + case DROPBOX: + case BOX: + case ONEDRIVE: + case GDRIVE: return new AmazeFile(path).isDirectory(); case ROOT: isDirectory = NativeOperations.isDirectory(path); @@ -495,26 +495,6 @@ public Boolean execute(SFTPClient client) { case OTG: isDirectory = OTGUtil.getDocumentFile(path, context, false).isDirectory(); break; - case DROPBOX: - isDirectory = DropboxAccount.INSTANCE.getAccount() - .getMetadata(CloudUtil.stripPath(OpenMode.DROPBOX, path)) - .getFolder(); - break; - case BOX: - isDirectory = BoxAccount.INSTANCE.getAccount() - .getMetadata(CloudUtil.stripPath(OpenMode.BOX, path)) - .getFolder(); - break; - case GDRIVE: - isDirectory = GoogledriveAccount.INSTANCE.getAccount() - .getMetadata(CloudUtil.stripPath(OpenMode.GDRIVE, path)) - .getFolder(); - break; - case ONEDRIVE: - isDirectory = OnedriveAccount.INSTANCE.getAccount() - .getMetadata(CloudUtil.stripPath(OpenMode.ONEDRIVE, path)) - .getFolder(); - break; default: isDirectory = getFile().isDirectory(); break; @@ -565,6 +545,10 @@ public Long execute(SFTPClient client) throws IOException { return returnValue == null ? 0L : returnValue; case SMB: case FILE: + case DROPBOX: + case BOX: + case ONEDRIVE: + case GDRIVE: return FileUtils.folderSize(new AmazeFile(getPath())); case ROOT: HybridFileParcelable baseFile = generateBaseFileFromParent(); @@ -582,14 +566,6 @@ public Long execute(SFTPClient client) throws IOException { OpenMode.DOCUMENT_FILE, file -> totalBytes.addAndGet(FileUtils.getBaseFileSize(file, context))); break; - case DROPBOX: - case BOX: - case GDRIVE: - case ONEDRIVE: - size = - FileUtils.folderSizeCloud( - mode, dataUtils.getAccount(mode).getAccount().getMetadata(CloudUtil.stripPath(mode, path))); - break; default: return 0l; } @@ -603,14 +579,11 @@ public long getUsableSpace() { case SMB: case FILE: case ROOT: - return new AmazeFile(path).getUsableSpace(); case DROPBOX: case BOX: - case GDRIVE: case ONEDRIVE: - SpaceAllocation spaceAllocation = dataUtils.getAccount(mode).getAccount().getAllocation(); - size = spaceAllocation.getTotal() - spaceAllocation.getUsed(); - break; + case GDRIVE: + return new AmazeFile(path).getUsableSpace(); case SFTP: final Long returnValue = SshClientUtils.execute( @@ -662,14 +635,11 @@ public long getTotal(Context context) { case SMB: case FILE: case ROOT: - return new AmazeFile(path).getTotalSpace(); case DROPBOX: case BOX: case ONEDRIVE: case GDRIVE: - SpaceAllocation spaceAllocation = dataUtils.getAccount(mode).getAccount().getAllocation(); - size = spaceAllocation.getTotal(); - break; + return new AmazeFile(path).getTotalSpace(); case SFTP: final Long returnValue = SshClientUtils.execute( @@ -833,6 +803,10 @@ public ArrayList execute(SFTPClient client) { }); break; case SMB: + case DROPBOX: + case BOX: + case ONEDRIVE: + case GDRIVE: ArrayList result = new ArrayList<>(); for (AmazeFile smbFile1 : new AmazeFile(getPath()).listFiles()) { try { @@ -857,17 +831,6 @@ public ArrayList execute(SFTPClient client) { file -> hybridFileParcelables.add(file)); arrayList = hybridFileParcelables; break; - case DROPBOX: - case BOX: - case GDRIVE: - case ONEDRIVE: - try { - arrayList = CloudUtil.listFiles(path, dataUtils.getAccount(mode).getAccount(), mode); - } catch (CloudPluginException e) { - e.printStackTrace(); - arrayList = new ArrayList<>(); - } - break; default: arrayList = RootHelper.getFilesList(path, isRoot, true); } @@ -923,6 +886,10 @@ public void close() throws IOException { break; case SMB: case FILE: + case DROPBOX: + case BOX: + case ONEDRIVE: + case GDRIVE: return new AmazeFile(getPath()).getInputStream(); case DOCUMENT_FILE: ContentResolver contentResolver = context.getContentResolver(); @@ -944,23 +911,6 @@ public void close() throws IOException { inputStream = null; } break; - case DROPBOX: - CloudStorage cloudStorageDropbox = DropboxAccount.INSTANCE.getAccount(); - Log.d(getClass().getSimpleName(), CloudUtil.stripPath(OpenMode.DROPBOX, path)); - inputStream = cloudStorageDropbox.download(CloudUtil.stripPath(OpenMode.DROPBOX, path)); - break; - case BOX: - CloudStorage cloudStorageBox = BoxAccount.INSTANCE.getAccount(); - inputStream = cloudStorageBox.download(CloudUtil.stripPath(OpenMode.BOX, path)); - break; - case GDRIVE: - CloudStorage cloudStorageGDrive = GoogledriveAccount.INSTANCE.getAccount(); - inputStream = cloudStorageGDrive.download(CloudUtil.stripPath(OpenMode.GDRIVE, path)); - break; - case ONEDRIVE: - CloudStorage cloudStorageOneDrive = OnedriveAccount.INSTANCE.getAccount(); - inputStream = cloudStorageOneDrive.download(CloudUtil.stripPath(OpenMode.ONEDRIVE, path)); - break; default: try { inputStream = new FileInputStream(path); @@ -1008,6 +958,10 @@ public void close() throws IOException { }); case SMB: case FILE: + case DROPBOX: + case BOX: + case ONEDRIVE: + case GDRIVE: return new AmazeFile(path).getOutputStream(); case DOCUMENT_FILE: ContentResolver contentResolver = context.getContentResolver(); @@ -1062,20 +1016,8 @@ public Boolean execute(SFTPClient client) throws IOException { //noinspection SimplifiableConditionalExpression exists = executionReturn == null ? false : executionReturn; - } else if (isSmb() || isLocal()) { + } else if (isSmb() || isLocal() || isDropBoxFile() || isBoxFile() || isGoogleDriveFile() || isOneDriveFile()) { return new AmazeFile(path).exists(); - } else if (isDropBoxFile()) { - CloudStorage cloudStorageDropbox = DropboxAccount.INSTANCE.getAccount(); - exists = cloudStorageDropbox.exists(CloudUtil.stripPath(OpenMode.DROPBOX, path)); - } else if (isBoxFile()) { - CloudStorage cloudStorageBox = BoxAccount.INSTANCE.getAccount(); - exists = cloudStorageBox.exists(CloudUtil.stripPath(OpenMode.BOX, path)); - } else if (isGoogleDriveFile()) { - CloudStorage cloudStorageGoogleDrive = GoogledriveAccount.INSTANCE.getAccount(); - exists = cloudStorageGoogleDrive.exists(CloudUtil.stripPath(OpenMode.GDRIVE, path)); - } else if (isOneDriveFile()) { - CloudStorage cloudStorageOneDrive = OnedriveAccount.INSTANCE.getAccount(); - exists = cloudStorageOneDrive.exists(CloudUtil.stripPath(OpenMode.ONEDRIVE, path)); } else if (isRoot()) { return RootHelper.fileExists(path); } @@ -1121,7 +1063,7 @@ public boolean isSimpleFile() { } public boolean setLastModified(final long date) { - if (isSmb() || isLocal()) { + if (isSmb() || isLocal() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile()) { return new AmazeFile(path).setLastModified(date); } File f = getFile(); @@ -1143,7 +1085,7 @@ public Void execute(@NonNull SFTPClient client) { return null; } }); - } else if (isSmb() || isLocal() || isRoot() || isCustomPath() || isUnknownFile()) { + } else if (isSmb() || isLocal() || isRoot() || isCustomPath() || isUnknownFile() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile()) { new AmazeFile(path).mkdirs(); } else if (isOtgFile()) { if (!exists(context)) { @@ -1165,34 +1107,6 @@ public Void execute(@NonNull SFTPClient client) { parentDirectory.createDirectory(getName(context)); } } - } else if (isDropBoxFile()) { - CloudStorage cloudStorageDropbox = DropboxAccount.INSTANCE.getAccount(); - try { - cloudStorageDropbox.createFolder(CloudUtil.stripPath(OpenMode.DROPBOX, path)); - } catch (Exception e) { - e.printStackTrace(); - } - } else if (isBoxFile()) { - CloudStorage cloudStorageBox = BoxAccount.INSTANCE.getAccount(); - try { - cloudStorageBox.createFolder(CloudUtil.stripPath(OpenMode.BOX, path)); - } catch (Exception e) { - e.printStackTrace(); - } - } else if (isOneDriveFile()) { - CloudStorage cloudStorageOneDrive = OnedriveAccount.INSTANCE.getAccount(); - try { - cloudStorageOneDrive.createFolder(CloudUtil.stripPath(OpenMode.ONEDRIVE, path)); - } catch (Exception e) { - e.printStackTrace(); - } - } else if (isGoogleDriveFile()) { - CloudStorage cloudStorageGdrive = GoogledriveAccount.INSTANCE.getAccount(); - try { - cloudStorageGdrive.createFolder(CloudUtil.stripPath(OpenMode.GDRIVE, path)); - } catch (Exception e) { - e.printStackTrace(); - } } else { throw new IllegalStateException(); } @@ -1213,7 +1127,7 @@ public Boolean execute(@NonNull SFTPClient client) throws IOException { } }); return retval != null && retval; - } else if (isSmb() || isLocal() || (isRoot() && !rootmode)) { + } else if (isSmb() || isLocal() || (isRoot() && !rootmode) || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile()) { return new AmazeFile(path).delete(); } else if (isRoot() && rootmode) { setMode(OpenMode.ROOT); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index dcfa68042e..3d6265e8bb 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -33,7 +33,11 @@ import java.util.List; import java.util.Objects; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAmazeFilesystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAmazeFilesystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.file.FileAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; @@ -416,8 +420,14 @@ private void loadFilesystem(String path) { fs = SmbAmazeFileSystem.INSTANCE; } else if (FileAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { fs = FileAmazeFilesystem.INSTANCE; + } else if (BoxAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { + fs = DropboxAmazeFilesystem.INSTANCE; } else if (DropboxAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { fs = DropboxAmazeFilesystem.INSTANCE; + } else if (GoogledriveAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { + fs = DropboxAmazeFilesystem.INSTANCE; + } else if (OnedriveAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { + fs = DropboxAmazeFilesystem.INSTANCE; } } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java index 750f8ca3c9..4819fd3829 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java @@ -87,7 +87,7 @@ public abstract class AmazeFileSystem { /** * Resolve the given abstract pathname into absolute form. Invoked by the getAbsolutePath and - * getCanonicalPath methods in the F class. + * getCanonicalPath methods in the {@link AmazeFile} class. */ @NonNull public abstract String resolve(AmazeFile f); @@ -192,25 +192,33 @@ public abstract class AmazeFileSystem { */ public abstract boolean setReadOnly(AmazeFile f); + protected final String removePrefix(@NonNull String path) { + return path.substring(prefixLength(path)); + } + /* -- Filesystem interface -- */ /** List the available filesystem roots. */ public abstract AmazeFile[] listRoots(); /* -- Disk usage -- */ - @Native public static final int SPACE_TOTAL = 0; - @Native public static final int SPACE_FREE = 1; - @Native public static final int SPACE_USABLE = 2; + public static final int SPACE_TOTAL = 0; + public static final int SPACE_FREE = 1; + public static final int SPACE_USABLE = 2; public abstract long getSpace(AmazeFile f, int t); /* -- Basic infrastructure -- */ /** Compare two abstract pathnames lexicographically. */ - public abstract int compare(AmazeFile f1, AmazeFile f2); + public int compare(AmazeFile f1, AmazeFile f2) { + return f1.getPath().compareTo(f2.getPath()); + } /** Compute the hash code of an abstract pathname. */ - public abstract int hashCode(AmazeFile f); + public int hashCode(AmazeFile f) { + return basicUnixHashCode(f.getPath()); + } // Flags for enabling/disabling performance optimizations for file // name canonicalization diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java new file mode 100644 index 0000000000..0ae4ecc641 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java @@ -0,0 +1,276 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; +import com.cloudrail.si.interfaces.CloudStorage; +import com.cloudrail.si.types.CloudMetaData; +import com.cloudrail.si.types.SpaceAllocation; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.util.List; +import java.util.Objects; + +import jcifs.smb.SmbException; +import kotlin.NotImplementedError; + +public abstract class CloudAmazeFilesystem extends AmazeFileSystem { + public static final String TAG = CloudAmazeFilesystem.class.getSimpleName(); + + public static final char SEPARATOR = '/'; + + public abstract String getPrefix(); + + public abstract Account getAccount(); + + @Override + public boolean isPathOfThisFilesystem(@NonNull String path) { + return path.startsWith(getPrefix()); + } + + @Override + public char getSeparator() { + return SEPARATOR; + } + + @Override + public char getPathSeparator() { + return 0; + } + + @NonNull + @Override + public String normalize(@NonNull String path) { + String canonical; + try { + canonical = canonicalize(path); + } catch (IOException e) { + Log.e(TAG, "Error getting Dropbox file canonical path", e); + canonical = path + "/"; + } + return canonical.substring(0, canonical.length() - 1); + } + + @Override + public int prefixLength(@NonNull String path) { + return getPrefix().length(); + } + + @NonNull + @Override + public String resolve(String parent, String child) { + return getPrefix() + new File(removePrefix(parent), child); + } + + @NonNull + @Override + public String getDefaultParent() { + return getPrefix() + "/"; + } + + @NonNull + @Override + public String fromURIPath(@NonNull String path) { + throw new NotImplementedError(); + } + + @Override + public boolean isAbsolute(AmazeFile f) { + return true; //We don't accept relative paths for cloud + } + + @NonNull + @Override + public String resolve(AmazeFile f) { + if (isAbsolute(f)) { + return f.getPath(); + } + + throw new IllegalArgumentException("Relative paths are not supported"); + } + + @NonNull + @Override + public String canonicalize(String path) throws IOException { + return getPrefix() + new File(removePrefix(path)).getCanonicalPath(); + } + + public final int getBooleanAttributes(AmazeFile f) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + final String noPrefixPath = removePrefix(f.getPath()); + final CloudMetaData metadata = getAccount().getAccount().getMetadata(noPrefixPath); + int r = 0; + + if (account.exists(noPrefixPath)) { + r |= BA_EXISTS; + + r |= BA_REGULAR; // all files are regular (probably) + + if (metadata.getFolder()) { + r |= BA_DIRECTORY; + } + + //No way to know if its hidden + } + + return r; + } + + public final boolean checkAccess(AmazeFile f, int access) { + switch (access) { + case ACCESS_EXECUTE: + return false; // You aren't executing anything at the cloud + case ACCESS_WRITE: + return true; // Probably, can't check + case ACCESS_READ: + return true; // Probably, can't check + case ACCESS_CHECK_EXISTS: + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + final String noPrefixPath = removePrefix(f.getPath()); + + return account.exists(noPrefixPath); + default: + throw new IllegalStateException(); + } + } + + @Override + public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { + throw new NotImplementedError(); + } + + @Override + public long getLastModifiedTime(AmazeFile f) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + final String noPrefixPath = removePrefix(f.getPath()); + // TODO check that this actually returns seconds since epoch + return account.getMetadata(noPrefixPath).getContentModifiedAt(); + } + + @Override + public long getLength(AmazeFile f) throws IOException { + if (f.isDirectory()) { + return 0; + } + + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + final String noPrefixPath = removePrefix(f.getPath()); + + return account.getMetadata(noPrefixPath).getSize(); + } + + @Override + public boolean createFileExclusively(String pathname) throws IOException { + return false; + } + + @Override + public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + final String noPrefixPath = removePrefix(f.getPath()); + account.delete(noPrefixPath); + return true;// This seems to never fail + } + + @Nullable + @Override + public String[] list(AmazeFile f) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + final String noPrefixPath = removePrefix(f.getPath()); + final List metadatas = account.getChildren(noPrefixPath); + + String[] list = new String[metadatas.size()]; + for (int i = 0; i < list.length; i++) { + list[i] = normalize(getPrefix() + metadatas.get(i).getPath()); + } + return list; + } + + @Nullable + @Override + public InputStream getInputStream(AmazeFile f) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + final String noPrefixPath = removePrefix(f.getPath()); + return account.download(noPrefixPath); + } + + @Nullable + @Override + public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { + throw new NotImplementedError(); + } + + @Override + public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + final String noPrefixPath = removePrefix(f.getPath()); + account.createFolder(noPrefixPath); + return true;// This seems to never fail + } + + @Override + public boolean rename(AmazeFile f1, AmazeFile f2) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + account.move(removePrefix(f1.getPath()), removePrefix(f2.getPath())); + return true;// This seems to never fail + } + + @Override + public boolean setLastModifiedTime(AmazeFile f, long time) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + final String noPrefixPath = removePrefix(f.getPath()); + // TODO check that this actually returns seconds since epoch + account.getMetadata(noPrefixPath).setContentModifiedAt(time); + return true;// This seems to never fail + } + + @Override + public boolean setReadOnly(AmazeFile f) { + return false; // This doesn't seem possible + } + + @Override + public AmazeFile[] listRoots() { + return new AmazeFile[] { new AmazeFile(getPrefix() + "/") }; + } + + @Override + public long getSpace(AmazeFile f, int t) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + SpaceAllocation spaceAllocation = account.getAllocation(); + + switch (t) { + case SPACE_TOTAL: + return spaceAllocation.getTotal(); + case SPACE_FREE: + case SPACE_USABLE: + // The assumption is made that all free space is usable + return spaceAllocation.getTotal() - spaceAllocation.getUsed(); + default: + throw new IllegalStateException(); + } + } + +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt index ca8d4e3e8b..0170ec5162 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt @@ -2,5 +2,4 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -object BoxAccount: Account() { -} \ No newline at end of file +object BoxAccount: Account() \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java new file mode 100644 index 0000000000..5ae00452dd --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java @@ -0,0 +1,25 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box; + +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; + +public final class BoxAmazeFilesystem extends CloudAmazeFilesystem { + public static final String TAG = BoxAmazeFilesystem.class.getSimpleName(); + + public static final String PREFIX = "box:/"; + + public static final BoxAmazeFilesystem INSTANCE = new BoxAmazeFilesystem(); + + private BoxAmazeFilesystem() {} + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Account getAccount() { + return BoxAccount.INSTANCE; + } +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt index 144d6bcad6..d6b913dc56 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt @@ -2,6 +2,4 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -object DropboxAccount: Account() { - -} \ No newline at end of file +object DropboxAccount: Account() \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java index 4d62d2cd4c..e4c24fef52 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java @@ -1,178 +1,24 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; -import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class DropboxAmazeFilesystem extends AmazeFileSystem { +public final class DropboxAmazeFilesystem extends CloudAmazeFilesystem { public static final String TAG = DropboxAmazeFilesystem.class.getSimpleName(); - public static final String PREFIX = ""; - public static final char SEPARATOR = '/'; + public static final String PREFIX = "dropbox:/"; public static final DropboxAmazeFilesystem INSTANCE = new DropboxAmazeFilesystem(); private DropboxAmazeFilesystem() {} @Override - public boolean isPathOfThisFilesystem(@NonNull String path) { - return path.startsWith(SmbAmazeFileSystem.PREFIX); - } - - @Override - public char getSeparator() { - return SEPARATOR; - } - - @Override - public char getPathSeparator() { - return 0; - } - - @NonNull - @Override - public String normalize(@NonNull String path) { - return null; - } - - @Override - public int prefixLength(@NonNull String path) { - return 0; - } - - @NonNull - @Override - public String resolve(String parent, String child) { - return null; - } - - @NonNull - @Override - public String getDefaultParent() { - return null; - } - - @NonNull - @Override - public String fromURIPath(@NonNull String path) { - return null; - } - - @Override - public boolean isAbsolute(AmazeFile f) { - return false; - } - - @NonNull - @Override - public String resolve(AmazeFile f) { - return null; - } - - @NonNull - @Override - public String canonicalize(String path) throws IOException { - return null; - } - - @Override - public int getBooleanAttributes(AmazeFile f) { - return 0; - } - - @Override - public boolean checkAccess(AmazeFile f, int access) { - return false; - } - - @Override - public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { - return false; - } - - @Override - public long getLastModifiedTime(AmazeFile f) { - return 0; - } - - @Override - public long getLength(AmazeFile f) throws IOException { - return 0; - } - - @Override - public boolean createFileExclusively(String pathname) throws IOException { - return false; - } - - @Override - public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { - return false; - } - - @Nullable - @Override - public String[] list(AmazeFile f) { - return new String[0]; - } - - @Nullable - @Override - public InputStream getInputStream(AmazeFile f) { - return null; - } - - @Nullable - @Override - public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { - return null; - } - - @Override - public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { - return false; - } - - @Override - public boolean rename(AmazeFile f1, AmazeFile f2) { - return false; - } - - @Override - public boolean setLastModifiedTime(AmazeFile f, long time) { - return false; - } - - @Override - public boolean setReadOnly(AmazeFile f) { - return false; - } - - @Override - public AmazeFile[] listRoots() { - return new AmazeFile[0]; - } - - @Override - public long getSpace(AmazeFile f, int t) { - return 0; - } - - @Override - public int compare(AmazeFile f1, AmazeFile f2) { - return 0; + public String getPrefix() { + return PREFIX; } @Override - public int hashCode(AmazeFile f) { - return 0; + public Account getAccount() { + return DropboxAccount.INSTANCE; } } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt index 5a17caa052..5f8f3a63d8 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt @@ -2,5 +2,4 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -object GoogledriveAccount: Account() { -} \ No newline at end of file +object GoogledriveAccount: Account() \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java new file mode 100644 index 0000000000..37131943fb --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java @@ -0,0 +1,24 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive; + +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; + +public final class GoogledriveAmazeFilesystem extends CloudAmazeFilesystem { + public static final String TAG = GoogledriveAmazeFilesystem.class.getSimpleName(); + + public static final String PREFIX = "gdrive:/"; + + public static final GoogledriveAmazeFilesystem INSTANCE = new GoogledriveAmazeFilesystem(); + + private GoogledriveAmazeFilesystem() {} + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Account getAccount() { + return GoogledriveAccount.INSTANCE; + } +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt index 2baf2628a1..350ffd1774 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt @@ -2,6 +2,4 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedriv import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -object OnedriveAccount: Account() { - -} \ No newline at end of file +object OnedriveAccount: Account() \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java new file mode 100644 index 0000000000..85fe65a1c9 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java @@ -0,0 +1,25 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive; + +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; + +public final class OnedriveAmazeFilesystem extends CloudAmazeFilesystem { + public static final String TAG = OnedriveAmazeFilesystem.class.getSimpleName(); + + public static final String PREFIX = "onedrive:/"; + + public static final OnedriveAmazeFilesystem INSTANCE = new OnedriveAmazeFilesystem(); + + private OnedriveAmazeFilesystem() {} + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public Account getAccount() { + return OnedriveAccount.INSTANCE; + } +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java index ce07840e7c..a38449a0c1 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -405,14 +405,4 @@ public long getSpace(AmazeFile f, int t) { throw new IllegalStateException(); } } - - @Override - public int compare(AmazeFile f1, AmazeFile f2) { - return f1.getPath().compareTo(f2.getPath()); - } - - @Override - public int hashCode(AmazeFile f) { - return basicUnixHashCode(f.getPath()); - } } From acff69827667e293897ec32ea2ff9fe3666ebe0f Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 26 Dec 2021 11:47:42 -0300 Subject: [PATCH 13/46] Apply spotless --- .../filesystem/filetypes/AmazeFile.java | 1 - .../filesystem/filetypes/AmazeFileSystem.java | 7 +- .../filesystem/filetypes/ContextProvider.kt | 22 ++++- .../filesystem/filetypes/cloud/Account.kt | 22 ++++- .../filetypes/cloud/CloudAmazeFilesystem.java | 59 ++++++----- .../filetypes/cloud/box/BoxAccount.kt | 22 ++++- .../cloud/box/BoxAmazeFilesystem.java | 21 +++- .../filetypes/cloud/dropbox/DropboxAccount.kt | 22 ++++- .../cloud/dropbox/DropboxAmazeFilesystem.java | 20 ++++ .../cloud/gdrive/GoogledriveAccount.kt | 22 ++++- .../gdrive/GoogledriveAmazeFilesystem.java | 20 ++++ .../cloud/onedrive/OnedriveAccount.kt | 22 ++++- .../onedrive/OnedriveAmazeFilesystem.java | 21 +++- .../filetypes/file/ExternalSdCardOperation.kt | 4 +- .../filetypes/file/FileAmazeFilesystem.java | 98 ++++++++++++------- .../filetypes/file/MediaStoreHack.java | 21 ++-- .../filetypes/file/UriForSafPersistance.kt | 22 ++++- .../filetypes/smb/SmbAmazeFileSystem.java | 5 +- 18 files changed, 343 insertions(+), 88 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 3d6265e8bb..ae7a7718c8 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -35,7 +35,6 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.file.FileAmazeFilesystem; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java index 4819fd3829..f7ae654eb1 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java @@ -20,14 +20,10 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes; -import android.content.Context; - -import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Native; -import java.util.concurrent.Callable; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -166,7 +162,8 @@ public abstract class AmazeFileSystem { public abstract InputStream getInputStream(AmazeFile f); @Nullable - public abstract OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider); + public abstract OutputStream getOutputStream( + AmazeFile f, @NonNull ContextProvider contextProvider); /** * Create a new directory denoted by the given abstract pathname, returning true if diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt index a8ba7bd076..6f7e975a80 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes import android.content.Context @@ -9,4 +29,4 @@ interface ContextProvider { * it must return a non null ref to [Context] or crash */ fun getContext(): Context? -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/Account.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/Account.kt index 6854d1ace1..86bbfef698 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/Account.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/Account.kt @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.cloud import com.cloudrail.si.interfaces.CloudStorage @@ -19,4 +39,4 @@ abstract class Account { account = null accounts.remove(this) } -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java index 0ae4ecc641..94e1a4acd0 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java @@ -1,28 +1,44 @@ -package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud; +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 . + */ -import android.util.Log; +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Objects; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; -import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; import com.cloudrail.si.interfaces.CloudStorage; import com.cloudrail.si.types.CloudMetaData; import com.cloudrail.si.types.SpaceAllocation; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.MalformedURLException; -import java.util.List; -import java.util.Objects; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; -import jcifs.smb.SmbException; import kotlin.NotImplementedError; public abstract class CloudAmazeFilesystem extends AmazeFileSystem { @@ -87,7 +103,7 @@ public String fromURIPath(@NonNull String path) { @Override public boolean isAbsolute(AmazeFile f) { - return true; //We don't accept relative paths for cloud + return true; // We don't accept relative paths for cloud } @NonNull @@ -122,7 +138,7 @@ public final int getBooleanAttributes(AmazeFile f) { r |= BA_DIRECTORY; } - //No way to know if its hidden + // No way to know if its hidden } return r; @@ -185,7 +201,7 @@ public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { Objects.requireNonNull(account); final String noPrefixPath = removePrefix(f.getPath()); account.delete(noPrefixPath); - return true;// This seems to never fail + return true; // This seems to never fail } @Nullable @@ -224,7 +240,7 @@ public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProv Objects.requireNonNull(account); final String noPrefixPath = removePrefix(f.getPath()); account.createFolder(noPrefixPath); - return true;// This seems to never fail + return true; // This seems to never fail } @Override @@ -232,7 +248,7 @@ public boolean rename(AmazeFile f1, AmazeFile f2) { final CloudStorage account = getAccount().getAccount(); Objects.requireNonNull(account); account.move(removePrefix(f1.getPath()), removePrefix(f2.getPath())); - return true;// This seems to never fail + return true; // This seems to never fail } @Override @@ -242,7 +258,7 @@ public boolean setLastModifiedTime(AmazeFile f, long time) { final String noPrefixPath = removePrefix(f.getPath()); // TODO check that this actually returns seconds since epoch account.getMetadata(noPrefixPath).setContentModifiedAt(time); - return true;// This seems to never fail + return true; // This seems to never fail } @Override @@ -252,7 +268,7 @@ public boolean setReadOnly(AmazeFile f) { @Override public AmazeFile[] listRoots() { - return new AmazeFile[] { new AmazeFile(getPrefix() + "/") }; + return new AmazeFile[] {new AmazeFile(getPrefix() + "/")}; } @Override @@ -272,5 +288,4 @@ public long getSpace(AmazeFile f, int t) { throw new IllegalStateException(); } } - } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt index 0170ec5162..29ad8bce4b 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAccount.kt @@ -1,5 +1,25 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -object BoxAccount: Account() \ No newline at end of file +object BoxAccount : Account() diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java index 5ae00452dd..0d627ce580 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java @@ -1,8 +1,27 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; public final class BoxAmazeFilesystem extends CloudAmazeFilesystem { public static final String TAG = BoxAmazeFilesystem.class.getSimpleName(); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt index d6b913dc56..877ed8b2c4 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAccount.kt @@ -1,5 +1,25 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -object DropboxAccount: Account() \ No newline at end of file +object DropboxAccount : Account() diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java index e4c24fef52..7693ebfa63 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt index 5f8f3a63d8..4a3b2273fb 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAccount.kt @@ -1,5 +1,25 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -object GoogledriveAccount: Account() \ No newline at end of file +object GoogledriveAccount : Account() diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java index 37131943fb..4336bb2b5a 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt index 350ffd1774..e99dada65f 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAccount.kt @@ -1,5 +1,25 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -object OnedriveAccount: Account() \ No newline at end of file +object OnedriveAccount : Account() diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java index 85fe65a1c9..8bd06bbe16 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java @@ -1,8 +1,27 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; public final class OnedriveAmazeFilesystem extends CloudAmazeFilesystem { public static final String TAG = OnedriveAmazeFilesystem.class.getSimpleName(); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/ExternalSdCardOperation.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/ExternalSdCardOperation.kt index 793656f7ed..b4c0da242d 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/ExternalSdCardOperation.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/ExternalSdCardOperation.kt @@ -49,7 +49,9 @@ object ExternalSdCardOperation { context: Context, preferenceUri: String? ): DocumentFile? { - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) return DocumentFile.fromFile(File(file.path)) + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + return DocumentFile.fromFile(File(file.path)) + } val baseFolder = getExtSdCardFolder(file, context) var originalDirectory = false if (baseFolder == null) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java index 415e6aee19..d200d00e65 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -1,11 +1,42 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.file; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; + import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.net.Uri; import android.os.Build; -import android.preference.PreferenceManager; import android.provider.MediaStore; import android.util.Log; @@ -13,22 +44,9 @@ import androidx.annotation.Nullable; import androidx.documentfile.provider.DocumentFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.concurrent.Callable; - /** - * This is in in essence calls to UnixFilesystem, but that class is not public - * so all calls must go through java.io.File + * This is in in essence calls to UnixFilesystem, but that class is not public so all calls must go + * through java.io.File */ public class FileAmazeFilesystem extends AmazeFileSystem { public static final FileAmazeFilesystem INSTANCE = new FileAmazeFilesystem(); @@ -170,9 +188,9 @@ public boolean createFileExclusively(String pathname) throws IOException { @Override public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { - if(f.isDirectory()) { + if (f.isDirectory()) { AmazeFile[] children = f.listFiles(); - if(children != null) { + if (children != null) { for (AmazeFile child : children) { delete(child, contextProvider); } @@ -187,7 +205,9 @@ public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { // Try with Storage Access Framework. if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - DocumentFile document = ExternalSdCardOperation.getDocumentFile(f, true, context, UriForSafPersistance.get(context)); + DocumentFile document = + ExternalSdCardOperation.getDocumentFile( + f, true, context, UriForSafPersistance.get(context)); if (document != null && document.delete()) { return true; } @@ -202,9 +222,9 @@ public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { // Delete the created entry, such that content provider will delete the file. resolver.delete( - MediaStore.Files.getContentUri("external"), - MediaStore.MediaColumns.DATA + "=?", new String[]{ f.getAbsolutePath() } - ); + MediaStore.Files.getContentUri("external"), + MediaStore.MediaColumns.DATA + "=?", + new String[] {f.getAbsolutePath()}); } return !f.exists(); @@ -217,15 +237,17 @@ public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { final Context context = contextProvider.getContext(); // Try with Storage Access Framework. - if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && - ExternalSdCardOperation.isOnExtSdCard(f, context) - ) { - DocumentFile document = ExternalSdCardOperation.getDocumentFile(f, false, context, UriForSafPersistance.get(context)); - if(document == null) { + if (context != null + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + && ExternalSdCardOperation.isOnExtSdCard(f, context)) { + DocumentFile document = + ExternalSdCardOperation.getDocumentFile( + f, false, context, UriForSafPersistance.get(context)); + if (document == null) { return true; } - if(document.delete()) { + if (document.delete()) { return true; } } @@ -273,15 +295,17 @@ public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contex return new FileOutputStream(f.getPath()); } else { final Context context = contextProvider.getContext(); - if(context == null) { + if (context == null) { return null; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Storage Access Framework - DocumentFile targetDocument = ExternalSdCardOperation.getDocumentFile(f, false, context, UriForSafPersistance.get(context)); + DocumentFile targetDocument = + ExternalSdCardOperation.getDocumentFile( + f, false, context, UriForSafPersistance.get(context)); - if (targetDocument == null){ + if (targetDocument == null) { return null; } @@ -301,19 +325,21 @@ public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contex @Override public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { - if(new File(f.getPath()).mkdir()) { + if (new File(f.getPath()).mkdir()) { return true; } final Context context = contextProvider.getContext(); // Try with Storage Access Framework. - if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && - ExternalSdCardOperation.isOnExtSdCard(f, context)) { + if (context != null + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + && ExternalSdCardOperation.isOnExtSdCard(f, context)) { String preferenceUri = UriForSafPersistance.get(context); - DocumentFile document = ExternalSdCardOperation.getDocumentFile(f, true, context, preferenceUri); - if(document == null) { + DocumentFile document = + ExternalSdCardOperation.getDocumentFile(f, true, context, preferenceUri); + if (document == null) { return false; } // getDocumentFile implicitly creates the directory. diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java index d79ebf3726..23e1e11cf9 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java @@ -21,6 +21,15 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes.file; /** Created by Arpit on 29-06-2015. */ +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Locale; + +import com.amaze.filemanager.file_operations.R; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import android.content.ContentResolver; import android.content.ContentValues; @@ -34,18 +43,6 @@ import androidx.annotation.Nullable; -import com.amaze.filemanager.file_operations.R; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Locale; - -import kotlin.Deprecated; - /** * Wrapper for manipulating files via the Android Media Content Provider. As of Android 4.4 KitKat, * applications can no longer write to the "secondary storage" of a device. Write operations using diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/UriForSafPersistance.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/UriForSafPersistance.kt index 197b95c746..4852a54c81 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/UriForSafPersistance.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/UriForSafPersistance.kt @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.file import android.content.Context @@ -20,4 +40,4 @@ object UriForSafPersistance { return PreferenceManager.getDefaultSharedPreferences(context) .getString(PREFERENCE_URI, null) } -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java index a38449a0c1..5e38429156 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -98,7 +98,8 @@ public String normalize(@NonNull String pathname) { @Override public int prefixLength(@NonNull String path) { if (path.length() == 0) { - throw new IllegalArgumentException("This should never happen, all paths must start with SMB prefix"); + throw new IllegalArgumentException( + "This should never happen, all paths must start with SMB prefix"); } Matcher matcherMetadata = METADATA_PATTERN.matcher(path); @@ -286,7 +287,7 @@ public boolean createFileExclusively(String pathname) throws IOException { } @Override - public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { + public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { try { create(f.getPath()).delete(); return true; From c06093f03ef01d735f496798fbc311ef44b5d9a8 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 26 Dec 2021 12:10:05 -0300 Subject: [PATCH 14/46] Remove URI handling from AmazeFile --- .../filesystem/filetypes/AmazeFile.java | 118 ------------------ .../filesystem/filetypes/AmazeFileSystem.java | 8 -- .../filetypes/cloud/CloudAmazeFilesystem.java | 6 - .../filetypes/file/FileAmazeFilesystem.java | 6 - .../filetypes/smb/SmbAmazeFileSystem.java | 6 - 5 files changed, 144 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index ae7a7718c8..87c18d3802 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -345,75 +345,6 @@ public AmazeFile(@Nullable AmazeFile parent, @NonNull String child) { this.prefixLength = fs.prefixLength(this.path); } - /** - * Creates a new AmazeFile instance by converting the given file: URI into an - * abstract pathname. - * - *

The exact form of a file: URI is system-dependent, hence the transformation - * performed by this constructor is also system-dependent. - * - *

For a given abstract pathname f it is guaranteed that - * - *

- * - * new AmazeFile( f.{@link #toURI() - * toURI}()).equals( f.{@link #getAbsoluteFile() getAbsoluteFile}()) - * - *
- * - * so long as the original abstract pathname, the URI, and the new abstract pathname are all - * created in (possibly different invocations of) the same Java virtual machine. This relationship - * typically does not hold, however, when a file: URI that is created in a virtual - * machine on one operating system is converted into an abstract pathname in a virtual machine on - * a different operating system. - * - * @param uri An absolute, hierarchical URI with a scheme equal to "file", a non-empty - * path component, and undefined authority, query, and fragment components - * @throws IllegalArgumentException If the preconditions on the parameter do not hold - * @see #toURI() - * @see java.net.URI - */ - public AmazeFile(@NonNull URI uri) { - // Check our many preconditions - if (!uri.isAbsolute()) { - throw new IllegalArgumentException("URI is not absolute"); - } - if (uri.isOpaque()) { - throw new IllegalArgumentException("URI is not hierarchical"); - } - String scheme = uri.getScheme(); - if ((scheme == null) || !scheme.equalsIgnoreCase("file")) { - throw new IllegalArgumentException("URI scheme is not \"file\""); - } - if (uri.getAuthority() != null) { - throw new IllegalArgumentException("URI has an authority component"); - } - if (uri.getFragment() != null) { - throw new IllegalArgumentException("URI has a fragment component"); - } - if (uri.getQuery() != null) { - throw new IllegalArgumentException("URI has a query component"); - } - String p = uri.getPath(); - if (p.equals("")) { - throw new IllegalArgumentException("URI path component is empty"); - } - - loadFilesystem(uri.toString()); - separatorChar = fs.getSeparator(); - separator = "" + separatorChar; - pathSeparatorChar = fs.getPathSeparator(); - pathSeparator = "" + pathSeparatorChar; - - // Okay, now initialize - p = fs.fromURIPath(p); - if (File.separatorChar != '/') { - p = p.replace('/', File.separatorChar); - } - this.path = fs.normalize(p); - this.prefixLength = fs.prefixLength(this.path); - } - private void loadFilesystem(String path) { if (SmbAmazeFileSystem.INSTANCE.isPathOfThisFilesystem(path)) { fs = SmbAmazeFileSystem.INSTANCE; @@ -601,55 +532,6 @@ private static String slashify(String path, boolean isDirectory) { return p; } - /** - * Constructs a file: URI that represents this abstract pathname. - * - *

The exact form of the URI is system-dependent. If it can be determined that the file denoted - * by this abstract pathname is a directory, then the resulting URI will end with a slash. - * - *

For a given abstract pathname f, it is guaranteed that - * - *

- * - * new {@link #AmazeFile(java.net.URI) - * File}( f.toURI()).equals( f.{@link #getAbsoluteFile() - * getAbsoluteFile}()) - * - *
- * - * so long as the original abstract pathname, the URI, and the new abstract pathname are all - * created in (possibly different invocations of) the same Java virtual machine. Due to the - * system-dependent nature of abstract pathnames, however, this relationship typically does not - * hold when a file: URI that is created in a virtual machine on one operating system is - * converted into an abstract pathname in a virtual machine on a different operating system. - * - *

Note that when this abstract pathname represents a UNC pathname then all components of the - * UNC (including the server name component) are encoded in the {@code URI} path. The authority - * component is undefined, meaning that it is represented as {@code null}. The {@link Path} class - * defines the {@link Path#toUri toUri} method to encode the server name in the authority - * component of the resulting {@code URI}. The {@link #toPath toPath} method may be used to obtain - * a {@code Path} representing this abstract pathname. - * - * @return An absolute, hierarchical URI with a scheme equal to "file", a path - * representing this abstract pathname, and undefined authority, query, and fragment - * components - * @see #AmazeFile(java.net.URI) - * @see java.net.URI - * @see java.net.URI#toURL() - */ - @Deprecated(message = "Left for reference, do not use") - @NonNull - public URI toURI() { - try { - AmazeFile f = getAbsoluteFile(); - String sp = slashify(f.getPath(), f.isDirectory()); - if (sp.startsWith("//")) sp = "//" + sp; - return new URI("file", null, sp, null); - } catch (URISyntaxException x) { - throw new RuntimeException(x); // Can't happen - } - } - /* -- Attribute accessors -- */ // Android-changed. Removed javadoc comment about special privileges diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java index f7ae654eb1..e0675c515f 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java @@ -68,14 +68,6 @@ public abstract class AmazeFileSystem { @NonNull public abstract String getDefaultParent(); - /** - * Post-process the given URI path string if necessary. This is used on win32, e.g., to transform - * "/c:/foo" into "c:/foo". The path string still has slash separators; code in the File class - * will translate them after this method returns. - */ - @NonNull - public abstract String fromURIPath(@NonNull String path); - /* -- Path operations -- */ /** Tell whether or not the given abstract pathname is absolute. */ diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java index 94e1a4acd0..569a3edb45 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java @@ -95,12 +95,6 @@ public String getDefaultParent() { return getPrefix() + "/"; } - @NonNull - @Override - public String fromURIPath(@NonNull String path) { - throw new NotImplementedError(); - } - @Override public boolean isAbsolute(AmazeFile f) { return true; // We don't accept relative paths for cloud diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java index d200d00e65..efee4c2cdf 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -92,12 +92,6 @@ public String getDefaultParent() { return new File(new File(""), "").getPath(); } - @NonNull - @Override - public String fromURIPath(@NonNull String path) { - return new File(path).getPath(); - } - @Override public boolean isAbsolute(AmazeFile f) { return new File(f.getPath()).isAbsolute(); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java index 5e38429156..155cc2d853 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -129,12 +129,6 @@ public String getDefaultParent() { throw new IllegalStateException("There is no default SMB path"); } - @NonNull - @Override - public String fromURIPath(@NonNull String path) { - throw new NotImplementedError(); - } - @Override public boolean isAbsolute(AmazeFile f) { return f.getPath().startsWith(PREFIX); From 7e577cf5efa349b4fdca9e839824096634430675 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 26 Dec 2021 12:14:35 -0300 Subject: [PATCH 15/46] Remove path separator handling from AmazeFilesystem --- .../filesystem/filetypes/AmazeFile.java | 31 ------------------- .../filesystem/filetypes/AmazeFileSystem.java | 3 -- .../filetypes/cloud/CloudAmazeFilesystem.java | 5 --- .../filetypes/file/FileAmazeFilesystem.java | 5 --- .../filetypes/smb/SmbAmazeFileSystem.java | 7 +---- 5 files changed, 1 insertion(+), 50 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 87c18d3802..07668273d8 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -166,23 +166,6 @@ public class AmazeFile implements Parcelable, Comparable { */ public final String separator; - /** - * The system-dependent path-separator character. This field is initialized to contain the first - * character of the value of the system property path.separator. This character is - * used to separate filenames in a sequence of files given as a path list. On UNIX - * systems, this character is ':'; on Microsoft Windows systems it is ';' - * . - * - * @see java.lang.System#getProperty(java.lang.String) - */ - public final char pathSeparatorChar; - - /** - * The system-dependent path-separator character, represented as a string for convenience. This - * string contains a single character, namely {@link #pathSeparatorChar}. - */ - public final String pathSeparator; - /** Enum type that indicates the status of a file path. */ private enum PathStatus { INVALID, @@ -215,8 +198,6 @@ private AmazeFile(@NonNull String pathname, int prefixLength) { loadFilesystem(pathname); separatorChar = fs.getSeparator(); separator = "" + separatorChar; - pathSeparatorChar = fs.getPathSeparator(); - pathSeparator = "" + pathSeparatorChar; this.path = pathname; this.prefixLength = prefixLength; } @@ -230,8 +211,6 @@ private AmazeFile(@NonNull String child, @NonNull AmazeFile parent) { loadFilesystem(parent.path); separatorChar = fs.getSeparator(); separator = "" + separatorChar; - pathSeparatorChar = fs.getPathSeparator(); - pathSeparator = "" + pathSeparatorChar; this.path = fs.resolve(parent.path, child); this.prefixLength = parent.prefixLength; } @@ -247,8 +226,6 @@ public AmazeFile(@NonNull String pathname) { loadFilesystem(pathname); separatorChar = fs.getSeparator(); separator = "" + separatorChar; - pathSeparatorChar = fs.getPathSeparator(); - pathSeparator = "" + pathSeparatorChar; this.path = fs.normalize(pathname); this.prefixLength = fs.prefixLength(this.path); } @@ -286,16 +263,12 @@ public AmazeFile(@Nullable String parent, @NonNull String child) { loadFilesystem(parent); separatorChar = fs.getSeparator(); separator = "" + separatorChar; - pathSeparatorChar = fs.getPathSeparator(); - pathSeparator = "" + pathSeparatorChar; this.path = fs.resolve(fs.normalize(parent), fs.normalize(child)); // END Android-changed: b/25859957, app-compat; don't substitute empty parent. } else { loadFilesystem(child); separatorChar = fs.getSeparator(); separator = "" + separatorChar; - pathSeparatorChar = fs.getPathSeparator(); - pathSeparator = "" + pathSeparatorChar; this.path = fs.normalize(child); } this.prefixLength = fs.prefixLength(this.path); @@ -326,8 +299,6 @@ public AmazeFile(@Nullable AmazeFile parent, @NonNull String child) { loadFilesystem(parent.getPath()); separatorChar = fs.getSeparator(); separator = "" + separatorChar; - pathSeparatorChar = fs.getPathSeparator(); - pathSeparator = "" + pathSeparatorChar; if (parent.path.equals("")) { this.path = fs.resolve(fs.getDefaultParent(), fs.normalize(child)); @@ -338,8 +309,6 @@ public AmazeFile(@Nullable AmazeFile parent, @NonNull String child) { loadFilesystem(child); separatorChar = fs.getSeparator(); separator = "" + separatorChar; - pathSeparatorChar = fs.getPathSeparator(); - pathSeparator = "" + pathSeparatorChar; this.path = fs.normalize(child); } this.prefixLength = fs.prefixLength(this.path); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java index e0675c515f..2a50bf9d49 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java @@ -38,9 +38,6 @@ public abstract class AmazeFileSystem { /** Return the local filesystem's name-separator character. */ public abstract char getSeparator(); - /** Return the local filesystem's path-separator character. */ - public abstract char getPathSeparator(); - /** * Convert the given pathname string to normal form. If the string is already in normal form then * it is simply returned. diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java index 569a3edb45..970fb12998 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java @@ -60,11 +60,6 @@ public char getSeparator() { return SEPARATOR; } - @Override - public char getPathSeparator() { - return 0; - } - @NonNull @Override public String normalize(@NonNull String path) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java index efee4c2cdf..4228c4f1e4 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -63,11 +63,6 @@ public char getSeparator() { return File.separatorChar; } - @Override - public char getPathSeparator() { - return File.pathSeparatorChar; - } - @NonNull @Override public String normalize(@NonNull String path) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java index 155cc2d853..8948730495 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java @@ -76,12 +76,7 @@ public boolean isPathOfThisFilesystem(@NonNull String path) { public char getSeparator() { return SEPARATOR; } - - @Override - public char getPathSeparator() { - return 0; - } - + @NonNull @Override public String normalize(@NonNull String pathname) { From ba009a44cd771ef7ab1677c56029520b76fa85e9 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 26 Dec 2021 12:16:10 -0300 Subject: [PATCH 16/46] Rename a few files to make them more standard --- .../shadows/ShadowSmbAmazeFilesystem.kt | 7 ++-- .../filesystem/filetypes/AmazeFile.java | 39 +++++++++---------- ...zeFileSystem.java => AmazeFilesystem.java} | 2 +- .../filetypes/cloud/CloudAmazeFilesystem.java | 4 +- .../filetypes/file/FileAmazeFilesystem.java | 4 +- ...ileSystem.java => SmbAmazeFilesystem.java} | 16 ++++---- 6 files changed, 34 insertions(+), 38 deletions(-) rename file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/{AmazeFileSystem.java => AmazeFilesystem.java} (99%) rename file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/{SmbAmazeFileSystem.java => SmbAmazeFilesystem.java} (96%) diff --git a/app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbAmazeFilesystem.kt b/app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbAmazeFilesystem.kt index c21b3e6a6c..6ee4d6b522 100644 --- a/app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbAmazeFilesystem.kt +++ b/app/src/test/java/com/amaze/filemanager/shadows/ShadowSmbAmazeFilesystem.kt @@ -1,14 +1,13 @@ package com.amaze.filemanager.shadows import org.robolectric.annotation.Implements -import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFilesystem import jcifs.context.SingletonContext import jcifs.smb.SmbFile -import org.mockito.Mockito import org.mockito.Mockito.* import org.robolectric.annotation.Implementation -@Implements(SmbAmazeFileSystem::class) +@Implements(SmbAmazeFilesystem::class) @Suppress class ShadowSmbAmazeFilesystem { @@ -16,7 +15,7 @@ class ShadowSmbAmazeFilesystem { /** * Shadows SmbAmazeFileSystem.create() * - * @see SmbAmazeFileSystem.create + * @see SmbAmazeFilesystem.create */ @JvmStatic @Implementation diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 07668273d8..f0203b1410 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -25,8 +25,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.URI; -import java.net.URISyntaxException; import java.nio.file.Path; import java.security.SecureRandom; import java.util.ArrayList; @@ -38,7 +36,7 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.file.FileAmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFileSystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFilesystem; import android.os.Parcel; import android.os.Parcelable; @@ -47,7 +45,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import kotlin.Deprecated; import kotlin.NotImplementedError; // Android-added: Info about UTF-8 usage in filenames. @@ -136,7 +133,7 @@ public class AmazeFile implements Parcelable, Comparable { private static ContextProvider contextProvider; /** The FileSystem object representing the platform's local file system. */ - private AmazeFileSystem fs; + private AmazeFilesystem fs; /** * This abstract pathname's normalized pathname string. A normalized pathname string uses the @@ -315,8 +312,8 @@ public AmazeFile(@Nullable AmazeFile parent, @NonNull String child) { } private void loadFilesystem(String path) { - if (SmbAmazeFileSystem.INSTANCE.isPathOfThisFilesystem(path)) { - fs = SmbAmazeFileSystem.INSTANCE; + if (SmbAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { + fs = SmbAmazeFilesystem.INSTANCE; } else if (FileAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { fs = FileAmazeFilesystem.INSTANCE; } else if (BoxAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { @@ -515,7 +512,7 @@ public boolean canRead() { if (isInvalid()) { return false; } - return fs.checkAccess(this, AmazeFileSystem.ACCESS_READ); + return fs.checkAccess(this, AmazeFilesystem.ACCESS_READ); } // Android-changed. Removed javadoc comment about special privileges @@ -531,7 +528,7 @@ public boolean canWrite() { if (isInvalid()) { return false; } - return fs.checkAccess(this, AmazeFileSystem.ACCESS_WRITE); + return fs.checkAccess(this, AmazeFilesystem.ACCESS_WRITE); } /** @@ -546,7 +543,7 @@ public boolean exists() { } // Android-changed: b/25878034 work around SELinux stat64 denial. - return fs.checkAccess(this, AmazeFileSystem.ACCESS_CHECK_EXISTS); + return fs.checkAccess(this, AmazeFilesystem.ACCESS_CHECK_EXISTS); } /** @@ -564,7 +561,7 @@ public boolean isDirectory() { if (isInvalid()) { return false; } - return ((fs.getBooleanAttributes(this) & AmazeFileSystem.BA_DIRECTORY) != 0); + return ((fs.getBooleanAttributes(this) & AmazeFilesystem.BA_DIRECTORY) != 0); } /** @@ -585,7 +582,7 @@ public boolean isFile() { if (isInvalid()) { return false; } - return ((fs.getBooleanAttributes(this) & AmazeFileSystem.BA_REGULAR) != 0); + return ((fs.getBooleanAttributes(this) & AmazeFilesystem.BA_REGULAR) != 0); } /** @@ -601,7 +598,7 @@ public boolean isHidden() { if (isInvalid()) { return false; } - return ((fs.getBooleanAttributes(this) & AmazeFileSystem.BA_HIDDEN) != 0); + return ((fs.getBooleanAttributes(this) & AmazeFilesystem.BA_HIDDEN) != 0); } /** @@ -1016,7 +1013,7 @@ public boolean setWritable(boolean writable, boolean ownerOnly) { if (isInvalid()) { return false; } - return fs.setPermission(this, AmazeFileSystem.ACCESS_WRITE, writable, ownerOnly); + return fs.setPermission(this, AmazeFilesystem.ACCESS_WRITE, writable, ownerOnly); } // Android-changed. Removed javadoc comment about special privileges @@ -1064,7 +1061,7 @@ public boolean setReadable(boolean readable, boolean ownerOnly) { if (isInvalid()) { return false; } - return fs.setPermission(this, AmazeFileSystem.ACCESS_READ, readable, ownerOnly); + return fs.setPermission(this, AmazeFilesystem.ACCESS_READ, readable, ownerOnly); } // Android-changed. Removed javadoc comment about special privileges @@ -1113,7 +1110,7 @@ public boolean setExecutable(boolean executable, boolean ownerOnly) { if (isInvalid()) { return false; } - return fs.setPermission(this, AmazeFileSystem.ACCESS_EXECUTE, executable, ownerOnly); + return fs.setPermission(this, AmazeFilesystem.ACCESS_EXECUTE, executable, ownerOnly); } // Android-changed. Removed javadoc comment about special privileges @@ -1150,7 +1147,7 @@ public boolean canExecute() { if (isInvalid()) { return false; } - return fs.checkAccess(this, AmazeFileSystem.ACCESS_EXECUTE); + return fs.checkAccess(this, AmazeFilesystem.ACCESS_EXECUTE); } /* -- Filesystem interface -- */ @@ -1177,7 +1174,7 @@ public long getTotalSpace() { return 0L; } try { - return fs.getSpace(this, AmazeFileSystem.SPACE_TOTAL); + return fs.getSpace(this, AmazeFilesystem.SPACE_TOTAL); } catch (NotImplementedError e) { Log.w(TAG, "Call to unimplemented fuction", e); return -1; @@ -1202,7 +1199,7 @@ public long getFreeSpace() { if (isInvalid()) { return 0L; } - return fs.getSpace(this, AmazeFileSystem.SPACE_FREE); + return fs.getSpace(this, AmazeFilesystem.SPACE_FREE); } // Android-added: Replaced generic platform info with Android specific one. @@ -1233,7 +1230,7 @@ public long getUsableSpace() { return 0L; } try { - return fs.getSpace(this, AmazeFileSystem.SPACE_USABLE); + return fs.getSpace(this, AmazeFilesystem.SPACE_USABLE); } catch (NotImplementedError e) { Log.w(TAG, "Call to unimplemented fuction", e); return -1; @@ -1334,7 +1331,7 @@ public AmazeFile createTempFile( AmazeFile f; do { f = TempDirectory.generateFile(prefix, suffix != null ? suffix : ".tmp", tmpdir); - } while ((fs.getBooleanAttributes(f) & AmazeFileSystem.BA_EXISTS) != 0); + } while ((fs.getBooleanAttributes(f) & AmazeFilesystem.BA_EXISTS) != 0); if (!fs.createFileExclusively(f.getPath())) throw new IOException("Unable to create temporary file"); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java similarity index 99% rename from file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java rename to file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java index 2a50bf9d49..d62b51020a 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java @@ -28,7 +28,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -public abstract class AmazeFileSystem { +public abstract class AmazeFilesystem { /* -- Normalization and construction -- */ diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java index 970fb12998..a63302655e 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java @@ -28,7 +28,7 @@ import java.util.Objects; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import com.cloudrail.si.interfaces.CloudStorage; import com.cloudrail.si.types.CloudMetaData; @@ -41,7 +41,7 @@ import kotlin.NotImplementedError; -public abstract class CloudAmazeFilesystem extends AmazeFileSystem { +public abstract class CloudAmazeFilesystem extends AmazeFilesystem { public static final String TAG = CloudAmazeFilesystem.class.getSimpleName(); public static final char SEPARATOR = '/'; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java index 4228c4f1e4..5f8daab286 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -29,7 +29,7 @@ import java.io.OutputStream; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import android.content.ContentResolver; @@ -48,7 +48,7 @@ * This is in in essence calls to UnixFilesystem, but that class is not public so all calls must go * through java.io.File */ -public class FileAmazeFilesystem extends AmazeFileSystem { +public class FileAmazeFilesystem extends AmazeFilesystem { public static final FileAmazeFilesystem INSTANCE = new FileAmazeFilesystem(); public static final String TAG = FileAmazeFilesystem.class.getSimpleName(); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java similarity index 96% rename from file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java rename to file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java index 8948730495..d816eeb7ad 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFileSystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java @@ -30,7 +30,7 @@ import java.util.regex.Pattern; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFileSystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import android.net.Uri; @@ -51,8 +51,8 @@ * "smb://:@/?disableIpcSigningCheck=true" or * "smb:///?disableIpcSigningCheck=true" Relative paths are not supported */ -public class SmbAmazeFileSystem extends AmazeFileSystem { - public static final String TAG = SmbAmazeFileSystem.class.getSimpleName(); +public class SmbAmazeFilesystem extends AmazeFilesystem { + public static final String TAG = SmbAmazeFilesystem.class.getSimpleName(); public static final String PREFIX = SMB_URI_PREFIX; public static final char SEPARATOR = '/'; @@ -63,20 +63,20 @@ public class SmbAmazeFileSystem extends AmazeFileSystem { private static final Pattern METADATA_PATTERN = Pattern.compile("([?][a-zA-Z]+=(\")?[a-zA-Z]+(\")?)+"); - public static final SmbAmazeFileSystem INSTANCE = new SmbAmazeFileSystem(); + public static final SmbAmazeFilesystem INSTANCE = new SmbAmazeFilesystem(); - private SmbAmazeFileSystem() {} + private SmbAmazeFilesystem() {} @Override public boolean isPathOfThisFilesystem(@NonNull String path) { - return path.startsWith(SmbAmazeFileSystem.PREFIX); + return path.startsWith(SmbAmazeFilesystem.PREFIX); } @Override public char getSeparator() { return SEPARATOR; } - + @NonNull @Override public String normalize(@NonNull String pathname) { @@ -298,7 +298,7 @@ public String[] list(AmazeFile f) { final String prefix = f.getPath().substring(0, prefixLength(f.getPath())); for (int i = 0; i < list.length; i++) { - list[i] = SmbAmazeFileSystem.INSTANCE.normalize(prefix + getSeparator() + list[i]); + list[i] = SmbAmazeFilesystem.INSTANCE.normalize(prefix + getSeparator() + list[i]); } return list; From 38bd68f5613b122efa3238d1f6452b2909fdb106 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 26 Dec 2021 18:47:16 -0300 Subject: [PATCH 17/46] Code duplication fixes in filesystem classes --- .../com/amaze/filemanager/utils/OTGUtil.kt | 3 +- .../filesystem/filetypes/AmazeFilesystem.java | 21 ++- .../filetypes/cloud/CloudAmazeFilesystem.java | 21 +-- .../filetypes/file/FileAmazeFilesystem.java | 5 + .../filetypes/otg/OtgAmazeFilesystem.java | 155 ++++++++++++++++++ .../filetypes/smb/SmbAmazeFilesystem.java | 14 +- 6 files changed, 190 insertions(+), 29 deletions(-) create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/otg/OtgAmazeFilesystem.java diff --git a/app/src/main/java/com/amaze/filemanager/utils/OTGUtil.kt b/app/src/main/java/com/amaze/filemanager/utils/OTGUtil.kt index 5cab338b6a..f13ddada1f 100644 --- a/app/src/main/java/com/amaze/filemanager/utils/OTGUtil.kt +++ b/app/src/main/java/com/amaze/filemanager/utils/OTGUtil.kt @@ -33,6 +33,7 @@ import androidx.annotation.RequiresApi import androidx.documentfile.provider.DocumentFile import com.amaze.filemanager.exceptions.DocumentFileNotFoundException import com.amaze.filemanager.file_operations.filesystem.OpenMode +import com.amaze.filemanager.file_operations.filesystem.filetypes.otg.OtgAmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.usb.SingletonUsbOtg import com.amaze.filemanager.file_operations.filesystem.usb.UsbOtgRepresentation import com.amaze.filemanager.filesystem.HybridFileParcelable @@ -42,7 +43,7 @@ import kotlin.collections.ArrayList /** Created by Vishal on 27-04-2017. */ object OTGUtil { - const val PREFIX_OTG = "otg:/" + const val PREFIX_OTG = OtgAmazeFilesystem.PREFIX private const val PREFIX_DOCUMENT_FILE = "content:/" const val PREFIX_MEDIA_REMOVABLE = "/mnt/media_rw" diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java index d62b51020a..cc939d6227 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java @@ -28,15 +28,28 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFilesystem; + public abstract class AmazeFilesystem { + public static final char STANDARD_SEPARATOR = '/'; + /* -- Normalization and construction -- */ + /** + * filesystem prefix + */ + public abstract String getPrefix(); + /** Is the path of this filesystem? */ - public abstract boolean isPathOfThisFilesystem(@NonNull String path); + public boolean isPathOfThisFilesystem(@NonNull String path) { + return path.startsWith(getPrefix()); + } /** Return the local filesystem's name-separator character. */ - public abstract char getSeparator(); + public char getSeparator() { + return STANDARD_SEPARATOR; + } /** * Convert the given pathname string to normal form. If the string is already in normal form then @@ -49,7 +62,9 @@ public abstract class AmazeFilesystem { * Compute the length of this pathname string's prefix. The pathname string must be in normal * form. */ - public abstract int prefixLength(@NonNull String path); + public int prefixLength(@NonNull String path) { + return getPrefix().length(); + } /** * Resolve the child pathname string against the parent. Both strings must be in normal form, and diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java index a63302655e..7728d4d6c5 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java @@ -44,20 +44,16 @@ public abstract class CloudAmazeFilesystem extends AmazeFilesystem { public static final String TAG = CloudAmazeFilesystem.class.getSimpleName(); - public static final char SEPARATOR = '/'; - - public abstract String getPrefix(); - public abstract Account getAccount(); @Override - public boolean isPathOfThisFilesystem(@NonNull String path) { - return path.startsWith(getPrefix()); - } + public int prefixLength(@NonNull String path) { + if (path.length() == 0) { + throw new IllegalArgumentException( + "This should never happen, all paths must start with OTG prefix"); + } - @Override - public char getSeparator() { - return SEPARATOR; + return super.prefixLength(path); } @NonNull @@ -73,11 +69,6 @@ public String normalize(@NonNull String path) { return canonical.substring(0, canonical.length() - 1); } - @Override - public int prefixLength(@NonNull String path) { - return getPrefix().length(); - } - @NonNull @Override public String resolve(String parent, String child) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java index 5f8daab286..70717b2e3f 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -58,6 +58,11 @@ public boolean isPathOfThisFilesystem(@NonNull String path) { return path.charAt(0) == getSeparator(); } + @Override + public String getPrefix() { + return null; + } + @Override public char getSeparator() { return File.separatorChar; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/otg/OtgAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/otg/OtgAmazeFilesystem.java new file mode 100644 index 0000000000..5263c65539 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/otg/OtgAmazeFilesystem.java @@ -0,0 +1,155 @@ +package com.amaze.filemanager.file_operations.filesystem.filetypes.otg; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.documentfile.provider.DocumentFile; + +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFilesystem; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class OtgAmazeFilesystem extends AmazeFilesystem { + public static final String TAG = OtgAmazeFilesystem.class.getSimpleName(); + + public static final String PREFIX = "otg:/"; + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public int prefixLength(@NonNull String path) { + if (path.length() == 0) { + throw new IllegalArgumentException( + "This should never happen, all paths must start with OTG prefix"); + } + + return super.prefixLength(path); + } + + @NonNull + @Override + public String normalize(@NonNull String path) { + return null; + } + + @NonNull + @Override + public String resolve(String parent, String child) { + return null; + } + + @NonNull + @Override + public String getDefaultParent() { + return null; + } + + @Override + public boolean isAbsolute(AmazeFile f) { + return true; // Relative paths are not supported + } + + @NonNull + @Override + public String resolve(AmazeFile f) { + return null; + } + + @NonNull + @Override + public String canonicalize(String path) throws IOException { + return null; + } + + @Override + public int getBooleanAttributes(AmazeFile f) { + return 0; + } + + @Override + public boolean checkAccess(AmazeFile f, int access) { + return false; + } + + @Override + public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { + return false; + } + + @Override + public long getLastModifiedTime(AmazeFile f) { + return 0; + } + + @Override + public long getLength(AmazeFile f) throws IOException { + return 0; + } + + @Override + public boolean createFileExclusively(String pathname) throws IOException { + return false; + } + + @Override + public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { + return false; + } + + @Nullable + @Override + public String[] list(AmazeFile f) { + return new String[0]; + } + + @Nullable + @Override + public InputStream getInputStream(AmazeFile f) { + return null; + } + + @Nullable + @Override + public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { + return null; + } + + @Override + public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { + return false; + } + + @Override + public boolean rename(AmazeFile f1, AmazeFile f2) { + return false; + } + + @Override + public boolean setLastModifiedTime(AmazeFile f, long time) { + return false; + } + + @Override + public boolean setReadOnly(AmazeFile f) { + return false; + } + + @Override + public AmazeFile[] listRoots() { + return new AmazeFile[0]; + } + + @Override + public long getSpace(AmazeFile f, int t) { + return 0; + } + + +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java index d816eeb7ad..b42eda06e3 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java @@ -55,7 +55,6 @@ public class SmbAmazeFilesystem extends AmazeFilesystem { public static final String TAG = SmbAmazeFilesystem.class.getSimpleName(); public static final String PREFIX = SMB_URI_PREFIX; - public static final char SEPARATOR = '/'; public static final String PARAM_DISABLE_IPC_SIGNING_CHECK = "disableIpcSigningCheck"; private static final Pattern IPv4_PATTERN = @@ -68,13 +67,8 @@ public class SmbAmazeFilesystem extends AmazeFilesystem { private SmbAmazeFilesystem() {} @Override - public boolean isPathOfThisFilesystem(@NonNull String path) { - return path.startsWith(SmbAmazeFilesystem.PREFIX); - } - - @Override - public char getSeparator() { - return SEPARATOR; + public String getPrefix() { + return PREFIX; } @NonNull @@ -219,8 +213,8 @@ public long getLength(AmazeFile f) throws SmbException, MalformedURLException { public static SmbFile create(String path) throws MalformedURLException { String processedPath; - if (!path.endsWith(SEPARATOR + "")) { - processedPath = path + SEPARATOR; + if (!path.endsWith(STANDARD_SEPARATOR + "")) { + processedPath = path + STANDARD_SEPARATOR; } else { processedPath = path; } From c187668412dc22e9b48863e5c831ba7339fade1a Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 26 Dec 2021 19:29:12 -0300 Subject: [PATCH 18/46] Legibility fixes to Smb filesystem --- .../filetypes/smb/SmbAmazeFilesystem.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java index b42eda06e3..7f4fd71f21 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java @@ -40,6 +40,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import jcifs.CIFSContext; import jcifs.SmbConstants; import jcifs.smb.NtlmPasswordAuthenticator; import jcifs.smb.SmbException; @@ -222,10 +223,19 @@ public static SmbFile create(String path) throws MalformedURLException { boolean disableIpcSigningCheck = Boolean.parseBoolean(uri.getQueryParameter(PARAM_DISABLE_IPC_SIGNING_CHECK)); String userInfo = uri.getUserInfo(); - return new SmbFile( - path.indexOf('?') < 0 ? path : path.substring(0, path.indexOf('?')), - CifsContexts.createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) - .withCredentials(createFrom(userInfo))); + + final String noExtraInfoPath; + if (path.contains("?")) { + noExtraInfoPath = path.substring(0, path.indexOf('?')); + } else { + noExtraInfoPath = path; + } + + final CIFSContext context = CifsContexts + .createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) + .withCredentials(createFrom(userInfo)); + + return new SmbFile(noExtraInfoPath, context); } /** From c777f920143dc3de7ab9abe4cc6f61dfce7ca2a7 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 26 Dec 2021 20:21:02 -0300 Subject: [PATCH 19/46] Fixed Smb files not showing under new filesystem --- .../filesystem/filetypes/AmazeFile.java | 11 ++++++++++- .../filesystem/filetypes/smb/SmbAmazeFilesystem.java | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index f0203b1410..19501776a5 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -340,7 +340,16 @@ private void loadFilesystem(String path) { @NonNull public String getName() { int index = path.lastIndexOf(separatorChar); - if (index < prefixLength) return path.substring(prefixLength); + if (index < prefixLength) { + return path.substring(prefixLength); + } + if(path.endsWith("/")) { + int newIndex = path.substring(0, path.length()-2).lastIndexOf(separatorChar); + if (newIndex < prefixLength) { + return path.substring(prefixLength); + } + return path.substring(newIndex + 1); + } return path.substring(index + 1); } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java index 7f4fd71f21..40edd23752 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java @@ -82,7 +82,7 @@ public String normalize(@NonNull String pathname) { Log.e(TAG, "Error getting SMB file canonical path", e); canonical = pathname.substring(0, prefixLength(pathname)) + "/"; } - return canonical.substring(0, canonical.length() - 1); + return canonical; } @Override From 9a19a46aa136b94a346d960a5dc6332e1801de88 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 26 Dec 2021 20:23:18 -0300 Subject: [PATCH 20/46] Fixed broken smb folders after going back --- .../filesystem/filetypes/AmazeFile.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 19501776a5..0e8d6a45cd 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -373,6 +373,16 @@ public String getParent() { } return null; } + if(path.endsWith("/")) { + int newIndex = path.substring(0, path.length()-2).lastIndexOf(separatorChar); + if (newIndex < prefixLength) { + if ((prefixLength > 0) && (path.length() > prefixLength)) { + return path.substring(0, prefixLength); + } + return null; + } + return path.substring(0, newIndex); + } return path.substring(0, index); } From c00b6accfac4193beb8604b767e8b7b71fcf64ab Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Fri, 7 Jan 2022 18:42:07 -0300 Subject: [PATCH 21/46] Add ssh to new filesystem mechanism and modify instance loading --- .../filemanager/filesystem/HybridFile.java | 179 +------ .../filesystem/files/FileUtils.java | 25 +- .../filesystem/ssh/SshAmazeFilesystem.java | 454 ++++++++++++++++++ .../filesystem/filetypes/AmazeFile.java | 24 +- .../filesystem/filetypes/AmazeFilesystem.java | 4 +- .../cloud/box/BoxAmazeFilesystem.java | 5 + .../cloud/dropbox/DropboxAmazeFilesystem.java | 5 + .../gdrive/GoogledriveAmazeFilesystem.java | 5 + .../onedrive/OnedriveAmazeFilesystem.java | 5 + .../filetypes/file/FileAmazeFilesystem.java | 6 + .../filetypes/smb/SmbAmazeFilesystem.java | 4 + 11 files changed, 529 insertions(+), 187 deletions(-) create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index db29d1f5a7..be719e0663 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -258,20 +258,6 @@ HybridFileParcelable generateBaseFileFromParent() { public long lastModified() { switch (mode) { case SFTP: - final Long returnValue = - SshClientUtils.execute( - new SFtpClientTemplate(path) { - @Override - public Long execute(@NonNull SFTPClient client) throws IOException { - return client.mtime(SshClientUtils.extractRemotePathFrom(path)); - } - }); - - if (returnValue == null) { - Log.e(TAG, "Error obtaining last modification time over SFTP"); - } - - return returnValue == null ? 0L : returnValue; case SMB: case FILE: return new AmazeFile(path).lastModified(); @@ -289,7 +275,6 @@ public long length(Context context) { long s = 0L; switch (mode) { case SFTP: - return ((HybridFileParcelable) this).getSize(); case SMB: case FILE: case DROPBOX: @@ -324,6 +309,7 @@ public String getPath() { public String getSimpleName() { String name = null; switch (mode) { + case SFTP: case SMB: case FILE: return new AmazeFile(path).getName(); @@ -529,20 +515,6 @@ public long folderSize(Context context) { switch (mode) { case SFTP: - final Long returnValue = - SshClientUtils.execute( - new SFtpClientTemplate(path) { - @Override - public Long execute(SFTPClient client) throws IOException { - return client.size(SshClientUtils.extractRemotePathFrom(path)); - } - }); - - if (returnValue == null) { - Log.e(TAG, "Error obtaining size of folder over SFTP"); - } - - return returnValue == null ? 0L : returnValue; case SMB: case FILE: case DROPBOX: @@ -583,40 +555,8 @@ public long getUsableSpace() { case BOX: case ONEDRIVE: case GDRIVE: - return new AmazeFile(path).getUsableSpace(); case SFTP: - final Long returnValue = - SshClientUtils.execute( - new SFtpClientTemplate(path) { - @Override - public Long execute(@NonNull SFTPClient client) throws IOException { - try { - Statvfs.Response response = - new Statvfs.Response( - path, - client - .getSFTPEngine() - .request( - Statvfs.request( - client, SshClientUtils.extractRemotePathFrom(path))) - .retrieve()); - return response.diskFreeSpace(); - } catch (SFTPException e) { - Log.e(TAG, "Error querying server", e); - return 0L; - } catch (Buffer.BufferException e) { - Log.e(TAG, "Error parsing reply", e); - return 0L; - } - } - }); - - if (returnValue == null) { - Log.e(TAG, "Error obtaining usable space over SFTP"); - } - - size = returnValue == null ? 0L : returnValue; - break; + return new AmazeFile(path).getUsableSpace(); case DOCUMENT_FILE: size = FileProperties.getDeviceStorageRemainingSpace(SafRootHolder.INSTANCE.getVolumeLabel()); @@ -639,40 +579,8 @@ public long getTotal(Context context) { case BOX: case ONEDRIVE: case GDRIVE: - return new AmazeFile(path).getTotalSpace(); case SFTP: - final Long returnValue = - SshClientUtils.execute( - new SFtpClientTemplate(path) { - @Override - public Long execute(@NonNull SFTPClient client) throws IOException { - try { - Statvfs.Response response = - new Statvfs.Response( - path, - client - .getSFTPEngine() - .request( - Statvfs.request( - client, SshClientUtils.extractRemotePathFrom(path))) - .retrieve()); - return response.diskSize(); - } catch (SFTPException e) { - Log.e(TAG, "Error querying server", e); - return 0L; - } catch (Buffer.BufferException e) { - Log.e(TAG, "Error parsing reply", e); - return 0L; - } - } - }); - - if (returnValue == null) { - Log.e(TAG, "Error obtaining total space over SFTP"); - } - - size = returnValue == null ? 0L : returnValue; - break; + return new AmazeFile(path).getTotalSpace(); case OTG: // TODO: Find total storage space of OTG when {@link DocumentFile} API adds support DocumentFile documentFile = OTGUtil.getDocumentFile(path, context, false); @@ -864,26 +772,6 @@ public InputStream getInputStream(Context context) { switch (mode) { case SFTP: - inputStream = - SshClientUtils.execute( - new SFtpClientTemplate(path, false) { - @Override - public InputStream execute(final SFTPClient client) throws IOException { - final RemoteFile rf = client.open(SshClientUtils.extractRemotePathFrom(path)); - return rf.new RemoteFileInputStream() { - @Override - public void close() throws IOException { - try { - super.close(); - } finally { - rf.close(); - client.close(); - } - } - }; - } - }); - break; case SMB: case FILE: case DROPBOX: @@ -928,34 +816,6 @@ public OutputStream getOutputStream(Context context) { OutputStream outputStream; switch (mode) { case SFTP: - return SshClientUtils.execute( - new SshClientTemplate(path, false) { - @Override - public OutputStream execute(final SSHClient ssh) throws IOException { - final SFTPClient client = ssh.newSFTPClient(); - final RemoteFile rf = - client.open( - SshClientUtils.extractRemotePathFrom(path), - EnumSet.of( - net.schmizz.sshj.sftp.OpenMode.WRITE, - net.schmizz.sshj.sftp.OpenMode.CREAT)); - return rf.new RemoteFileOutputStream() { - @Override - public void close() throws IOException { - try { - super.close(); - } finally { - try { - rf.close(); - client.close(); - } catch (Exception e) { - Log.w(TAG, "Error closing stream", e); - } - } - } - }; - } - }); case SMB: case FILE: case DROPBOX: @@ -1063,7 +923,7 @@ public boolean isSimpleFile() { } public boolean setLastModified(final long date) { - if (isSmb() || isLocal() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile()) { + if (isSmb() || isLocal() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile() || isSftp()) { return new AmazeFile(path).setLastModified(date); } File f = getFile(); @@ -1071,21 +931,7 @@ public boolean setLastModified(final long date) { } public void mkdir(Context context) { - if (isSftp()) { - SshClientUtils.execute( - new SFtpClientTemplate(path) { - @Override - public Void execute(@NonNull SFTPClient client) { - try { - client.mkdir(SshClientUtils.extractRemotePathFrom(path)); - } catch (IOException e) { - Log.e(TAG, "Error making directory over SFTP", e); - } - // FIXME: anything better than throwing a null to make Rx happy? - return null; - } - }); - } else if (isSmb() || isLocal() || isRoot() || isCustomPath() || isUnknownFile() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile()) { + if (isSftp() || isSmb() || isLocal() || isRoot() || isCustomPath() || isUnknownFile() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile()) { new AmazeFile(path).mkdirs(); } else if (isOtgFile()) { if (!exists(context)) { @@ -1114,20 +960,7 @@ public Void execute(@NonNull SFTPClient client) { public boolean delete(Context context, boolean rootmode) throws ShellNotRunningException, SmbException { - if (isSftp()) { - Boolean retval = - SshClientUtils.execute( - new SFtpClientTemplate(path) { - @Override - public Boolean execute(@NonNull SFTPClient client) throws IOException { - String _path = SshClientUtils.extractRemotePathFrom(path); - if (isDirectory(AppConfig.getInstance())) client.rmdir(_path); - else client.rm(_path); - return client.statExistence(_path) == null; - } - }); - return retval != null && retval; - } else if (isSmb() || isLocal() || (isRoot() && !rootmode) || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile()) { + if (isSftp() || isSmb() || isLocal() || (isRoot() && !rootmode) || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile()) { return new AmazeFile(path).delete(); } else if (isRoot() && rootmode) { setMode(OpenMode.ROOT); diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java index 4f261847a2..d5608ea545 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java @@ -46,6 +46,16 @@ import androidx.core.util.Pair; import androidx.documentfile.provider.DocumentFile; +import java.io.File; +import java.io.IOException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedList; +import java.util.concurrent.atomic.AtomicLong; + import com.afollestad.materialdialogs.MaterialDialog; import com.amaze.filemanager.R; import com.amaze.filemanager.adapters.data.LayoutElementParcelable; @@ -127,6 +137,19 @@ public static long folderSize(HybridFile directory, OnProgressUpdate updat } public static long folderSize(AmazeFile directory) { + long naiveSize; + + try { + naiveSize = directory.length(); + } catch (IOException e) { + Log.e(TAG, "Unexpected error getting size from AmazeFile", e); + naiveSize = 0; + } + + if(naiveSize != 0) { + return naiveSize; + } + long length = 0; try { for (AmazeFile file : directory.listFiles()) { @@ -353,7 +376,7 @@ public static void shareCloudFiles(ArrayList files, fin new AsyncTask() { @Override protected String doInBackground(String... params) { - CloudStorage cloudStorage = DataUtils.getInstance().getAccount(openMode); + CloudStorage cloudStorage = DataUtils.getInstance().getAccount(openMode).getAccount(); StringBuilder links = new StringBuilder(); links.append(cloudStorage.createShareLink(CloudUtil.stripPath(openMode, params[0]))); for (int i = 1; i < params.length; i++) { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java new file mode 100644 index 0000000000..e8cba2b29a --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -0,0 +1,454 @@ +package com.amaze.filemanager.filesystem.ssh; + +import static net.schmizz.sshj.sftp.FileMode.Type.DIRECTORY; +import static net.schmizz.sshj.sftp.FileMode.Type.REGULAR; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.amaze.filemanager.application.AppConfig; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; +import com.amaze.filemanager.filesystem.HybridFileParcelable; + +import net.schmizz.sshj.SSHClient; +import net.schmizz.sshj.common.Buffer; +import net.schmizz.sshj.sftp.FileAttributes; +import net.schmizz.sshj.sftp.RemoteFile; +import net.schmizz.sshj.sftp.RemoteResourceInfo; +import net.schmizz.sshj.sftp.SFTPClient; +import net.schmizz.sshj.sftp.SFTPException; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + +import jcifs.SmbConstants; +import jcifs.smb.SmbException; +import jcifs.smb.SmbFile; +import kotlin.NotImplementedError; + +public class SshAmazeFilesystem extends AmazeFilesystem { + public static final String PREFIX = "ssh:/"; + + public static final SshAmazeFilesystem INSTANCE = new SshAmazeFilesystem(); + + private static final String TAG = SshAmazeFilesystem.class.getSimpleName(); + + static { + AmazeFile.addFilesystem(INSTANCE); + } + + private SshAmazeFilesystem() {} + + @Override + public String getPrefix() { + return PREFIX; + } + + @NonNull + @Override + public String normalize(@NonNull String path) { + return PREFIX + new File(removePrefix(path)).getAbsolutePath(); + } + + @NonNull + @Override + public String resolve(String parent, String child) { + return PREFIX + new File(removePrefix(parent), child).getAbsolutePath(); + } + + @NonNull + @Override + public String getDefaultParent() { + return PREFIX + "/"; + } + + @Override + public boolean isAbsolute(AmazeFile f) { + return true; // Always absolute + } + + @NonNull + @Override + public String resolve(AmazeFile f) { + return f.getPath(); // No relative paths + } + + @NonNull + @Override + public String canonicalize(String path) throws IOException { + final String returnValue = + SshClientUtils.execute( + new SFtpClientTemplate(path) { + @Override + public String execute(SFTPClient client) throws IOException { + return client.canonicalize(path); + } + }); + + if (returnValue == null) { + throw new IOException("Error canonicalizing SFTP path!"); + } + + return returnValue; + } + + @Override + public int getBooleanAttributes(AmazeFile f) { + final SFtpClientTemplate template = new SFtpClientTemplate(f.getPath()) { + @Override + public Integer execute(@NonNull SFTPClient client) throws IOException { + Integer r = 0; + + if (client.statExistence(f.getPath()) != null) { + r |= BA_EXISTS; + + if (client.lstat(f.getPath()).getType() == REGULAR) { + r |= BA_REGULAR; + } + + if (client.lstat(f.getPath()).getType() == DIRECTORY) { + r |= BA_DIRECTORY; + } + + //Assume its not hidden + } + + return r; + } + }; + + final Integer returnValue = SshClientUtils.execute(template); + + if(returnValue == null) { + return 0; + } + + return returnValue; + } + + @Override + public boolean checkAccess(AmazeFile f, int access) { + throw new NotImplementedError(); + } + + @Override + public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { + throw new NotImplementedError(); + } + + @Override + public long getLastModifiedTime(AmazeFile f) { + final Long returnValue = + SshClientUtils.execute( + new SFtpClientTemplate(f.getPath()) { + @Override + public Long execute(@NonNull SFTPClient client) throws IOException { + return client.mtime(SshClientUtils.extractRemotePathFrom(f.getPath())); + } + }); + + if (returnValue == null) { + Log.e(TAG, "Error obtaining last modification time over SFTP"); + return 0; + } + + return returnValue; + } + + @Override + public long getLength(AmazeFile f) throws IOException { + if(f.isDirectory()) { + final Long returnValue = + SshClientUtils.execute( + new SFtpClientTemplate(f.getPath()) { + @Override + public Long execute(SFTPClient client) throws IOException { + return client.size(SshClientUtils.extractRemotePathFrom(f.getPath())); + } + }); + + if (returnValue == null) { + Log.e(TAG, "Error obtaining size of folder over SFTP"); + return 0; + } + + return returnValue; + } + + Long length = SshClientUtils.execute( + new SFtpClientTemplate(f.getPath()) { + @Override + public Long execute(@NonNull SFTPClient client) throws IOException { + return client.size(SshClientUtils.extractRemotePathFrom(f.getPath())); + } + }); + + if(length == null) { + throw new IOException("Failed getting size for ssh!"); + } + + return length; + } + + @Override + public boolean createFileExclusively(String pathname) throws IOException { + return false; + } + + @Override + public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { + Boolean retval = + SshClientUtils.execute( + new SFtpClientTemplate(f.getPath()) { + @Override + public Boolean execute(@NonNull SFTPClient client) throws IOException { + String _path = SshClientUtils.extractRemotePathFrom(f.getPath()); + if (f.isDirectory()) { + client.rmdir(_path); + } else { + client.rm(_path); + } + return client.statExistence(_path) == null; + } + }); + return retval != null && retval; + } + + @Nullable + @Override + public String[] list(AmazeFile f) { + List fileNameList = SshClientUtils.execute( + new SFtpClientTemplate>(f.getPath()) { + @Override + public List execute(@NonNull SFTPClient client) { + List infoList; + try { + infoList = client.ls(SshClientUtils.extractRemotePathFrom(f.getPath())); + } catch (IOException e) { + Log.w(TAG, "Failed listing files for ssh connection!", e); + return Collections.emptyList(); + } + + ArrayList retval = new ArrayList<>(infoList.size()); + + for (RemoteResourceInfo info : infoList) { + retval.add(resolve(f.getPath(), info.getName())); + } + + return retval; + } + }); + + if(fileNameList == null) { + return null; + } + + return fileNameList.toArray(new String[0]); + } + + @Nullable + @Override + public InputStream getInputStream(AmazeFile f) { + return SshClientUtils.execute( + new SFtpClientTemplate(f.getPath(), false) { + @Override + public InputStream execute(final SFTPClient client) throws IOException { + final RemoteFile rf = client.open(SshClientUtils.extractRemotePathFrom(f.getPath())); + return rf.new RemoteFileInputStream() { + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + rf.close(); + client.close(); + } + } + }; + } + }); + } + + @Nullable + @Override + public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { + return SshClientUtils.execute( + new SshClientTemplate(f.getPath(), false) { + @Override + public OutputStream execute(final SSHClient ssh) throws IOException { + final SFTPClient client = ssh.newSFTPClient(); + final RemoteFile rf = + client.open( + SshClientUtils.extractRemotePathFrom(f.getPath()), + EnumSet.of( + net.schmizz.sshj.sftp.OpenMode.WRITE, + net.schmizz.sshj.sftp.OpenMode.CREAT)); + return rf.new RemoteFileOutputStream() { + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + try { + rf.close(); + client.close(); + } catch (Exception e) { + Log.w(TAG, "Error closing stream", e); + } + } + } + }; + } + }); + } + + @Override + public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { + SshClientUtils.execute( + new SFtpClientTemplate(f.getPath()) { + @Override + public Void execute(@NonNull SFTPClient client) { + try { + client.mkdir(SshClientUtils.extractRemotePathFrom(f.getPath())); + } catch (IOException e) { + Log.e(TAG, "Error making directory over SFTP", e); + } + // FIXME: anything better than throwing a null to make Rx happy? + return null; + } + }); + return true; + } + + @Override + public boolean rename(AmazeFile f1, AmazeFile f2) { + Boolean retval = + SshClientUtils.execute( + new SFtpClientTemplate(f1.getPath()) { + @Override + public Boolean execute(@NonNull SFTPClient client) throws IOException { + try { + client.rename(f1.getPath(), f2.getPath()); + return true; + } catch (IOException e) { + Log.e(TAG, "Error renaming over SFTP", e); + return false; + } + } + }); + + if (retval == null) { + Log.e(TAG, "Error renaming over SFTP"); + + return false; + } + + return retval; + } + + @Override + public boolean setLastModifiedTime(AmazeFile f, long time) { + throw new NotImplementedError(); + } + + @Override + public boolean setReadOnly(AmazeFile f) { + return false; + } + + @Override + public AmazeFile[] listRoots() { + return new AmazeFile[] { new AmazeFile(getDefaultParent()) }; + } + + @Override + public long getSpace(AmazeFile f, int t) { + switch (t) { + case SPACE_TOTAL: { + final Long returnValue = + SshClientUtils.execute( + new SFtpClientTemplate(f.getPath()) { + @Override + public Long execute(@NonNull SFTPClient client) throws IOException { + try { + Statvfs.Response response = + new Statvfs.Response( + f.getPath(), + client + .getSFTPEngine() + .request( + Statvfs.request( + client, SshClientUtils.extractRemotePathFrom(f.getPath()))) + .retrieve()); + return response.diskSize(); + } catch (SFTPException e) { + Log.e(TAG, "Error querying SFTP server", e); + return 0L; + } catch (Buffer.BufferException e) { + Log.e(TAG, "Error parsing SFTP reply", e); + return 0L; + } + } + }); + + if (returnValue == null) { + Log.e(TAG, "Error obtaining total space over SFTP"); + + return 0; + } + + return returnValue; + } + case SPACE_FREE: { + final Long returnValue = + SshClientUtils.execute( + new SFtpClientTemplate(f.getPath()) { + @Override + public Long execute(@NonNull SFTPClient client) throws IOException { + try { + Statvfs.Response response = + new Statvfs.Response( + f.getPath(), + client + .getSFTPEngine() + .request( + Statvfs.request( + client, SshClientUtils.extractRemotePathFrom(f.getPath()))) + .retrieve()); + return response.diskFreeSpace(); + } catch (SFTPException e) { + Log.e(TAG, "Error querying server", e); + return 0L; + } catch (Buffer.BufferException e) { + Log.e(TAG, "Error parsing reply", e); + return 0L; + } + } + }); + + if (returnValue == null) { + Log.e(TAG, "Error obtaining usable space over SFTP"); + return 0; + } + + return returnValue; + } + case SPACE_USABLE: + // TODO: Find total storage space of SFTP support is added + throw new NotImplementedError(); + default: + throw new IllegalStateException(); + } + } +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 0e8d6a45cd..83a1679fda 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Set; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAmazeFilesystem; @@ -135,6 +136,12 @@ public class AmazeFile implements Parcelable, Comparable { /** The FileSystem object representing the platform's local file system. */ private AmazeFilesystem fs; + private static final List filesystems = new ArrayList<>(); + + public static void addFilesystem(AmazeFilesystem amazeFilesystem) { + filesystems.add(amazeFilesystem); + } + /** * This abstract pathname's normalized pathname string. A normalized pathname string uses the * default name-separator character and does not contain any duplicate or redundant separators. @@ -312,18 +319,11 @@ public AmazeFile(@Nullable AmazeFile parent, @NonNull String child) { } private void loadFilesystem(String path) { - if (SmbAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { - fs = SmbAmazeFilesystem.INSTANCE; - } else if (FileAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { - fs = FileAmazeFilesystem.INSTANCE; - } else if (BoxAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { - fs = DropboxAmazeFilesystem.INSTANCE; - } else if (DropboxAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { - fs = DropboxAmazeFilesystem.INSTANCE; - } else if (GoogledriveAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { - fs = DropboxAmazeFilesystem.INSTANCE; - } else if (OnedriveAmazeFilesystem.INSTANCE.isPathOfThisFilesystem(path)) { - fs = DropboxAmazeFilesystem.INSTANCE; + for (AmazeFilesystem filesystem : + filesystems) { + if(filesystem.isPathOfThisFilesystem(path)) { + fs = filesystem; + } } } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java index cc939d6227..511a542a2f 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java @@ -135,7 +135,9 @@ public int prefixLength(@NonNull String path) { /** * Return the length in bytes of the file denoted by the given abstract pathname, or zero if it - * does not exist, is a directory, or some other I/O error occurs. + * does not exist, or some other I/O error occurs. + * + * Note: for directories, this *could* return the size */ public abstract long getLength(AmazeFile f) throws IOException; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java index 0d627ce580..18329c6aad 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java @@ -20,6 +20,7 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; @@ -30,6 +31,10 @@ public final class BoxAmazeFilesystem extends CloudAmazeFilesystem { public static final BoxAmazeFilesystem INSTANCE = new BoxAmazeFilesystem(); + static { + AmazeFile.addFilesystem(INSTANCE); + } + private BoxAmazeFilesystem() {} @Override diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java index 7693ebfa63..12615ac9c2 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java @@ -20,6 +20,7 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; @@ -30,6 +31,10 @@ public final class DropboxAmazeFilesystem extends CloudAmazeFilesystem { public static final DropboxAmazeFilesystem INSTANCE = new DropboxAmazeFilesystem(); + static { + AmazeFile.addFilesystem(INSTANCE); + } + private DropboxAmazeFilesystem() {} @Override diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java index 4336bb2b5a..0402bee47a 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java @@ -20,6 +20,7 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; @@ -30,6 +31,10 @@ public final class GoogledriveAmazeFilesystem extends CloudAmazeFilesystem { public static final GoogledriveAmazeFilesystem INSTANCE = new GoogledriveAmazeFilesystem(); + static { + AmazeFile.addFilesystem(INSTANCE); + } + private GoogledriveAmazeFilesystem() {} @Override diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java index 8bd06bbe16..9819fba82a 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java @@ -20,6 +20,7 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; @@ -30,6 +31,10 @@ public final class OnedriveAmazeFilesystem extends CloudAmazeFilesystem { public static final OnedriveAmazeFilesystem INSTANCE = new OnedriveAmazeFilesystem(); + static { + AmazeFile.addFilesystem(INSTANCE); + } + private OnedriveAmazeFilesystem() {} @Override diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java index 70717b2e3f..9d957f8fb5 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -53,6 +53,12 @@ public class FileAmazeFilesystem extends AmazeFilesystem { public static final String TAG = FileAmazeFilesystem.class.getSimpleName(); + static { + AmazeFile.addFilesystem(INSTANCE); + } + + private FileAmazeFilesystem() { } + @Override public boolean isPathOfThisFilesystem(@NonNull String path) { return path.charAt(0) == getSeparator(); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java index 40edd23752..e40e0733b2 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java @@ -65,6 +65,10 @@ public class SmbAmazeFilesystem extends AmazeFilesystem { public static final SmbAmazeFilesystem INSTANCE = new SmbAmazeFilesystem(); + static { + AmazeFile.addFilesystem(INSTANCE); + } + private SmbAmazeFilesystem() {} @Override From f01ac822e6a5d20c80e69ecbb7405ca388648adf Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 8 Jan 2022 21:35:43 -0300 Subject: [PATCH 22/46] Added part of otg filesystem --- .../filemanager/filesystem/HybridFile.java | 58 +++++-------- .../filesystem/cloud/CloudUtil.java | 4 +- .../filesystem/files/FileUtils.java | 15 +++- .../filesystem}/otg/OtgAmazeFilesystem.java | 81 ++++++++++++++++--- .../filesystem/ssh/SshAmazeFilesystem.java | 4 +- .../ui/fragments/MainFragment.java | 15 +++- .../com/amaze/filemanager/utils/OTGUtil.kt | 2 +- .../filesystem/filetypes/AmazeFile.java | 8 +- .../filesystem/filetypes/AmazeFilesystem.java | 4 +- .../filesystem/filetypes/ContextProvider.kt | 2 +- .../filetypes/cloud/CloudAmazeFilesystem.java | 4 +- .../filetypes/file/FileAmazeFilesystem.java | 4 +- .../filetypes/smb/SmbAmazeFilesystem.java | 4 +- .../filesystem/smbstreamer/StreamSource.java | 6 +- 14 files changed, 142 insertions(+), 69 deletions(-) rename {file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes => app/src/main/java/com/amaze/filemanager/filesystem}/otg/OtgAmazeFilesystem.java (56%) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index be719e0663..a3bc32bc31 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -32,7 +32,6 @@ import java.io.OutputStream; import java.net.MalformedURLException; import java.util.ArrayList; -import java.util.EnumSet; import java.util.concurrent.atomic.AtomicLong; import com.amaze.filemanager.R; @@ -43,6 +42,7 @@ import com.amaze.filemanager.file_operations.exceptions.ShellNotRunningException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAccount; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount; @@ -281,8 +281,14 @@ public long length(Context context) { case BOX: case ONEDRIVE: case GDRIVE: + case OTG: try { - return new AmazeFile(path).length(); + return new AmazeFile(path).length(new ContextProvider() { + @Override + public Context getContext() { + return context; + } + }); } catch (IOException e) { Log.e(TAG, "Error getting length for file", e); } @@ -293,9 +299,6 @@ public long length(Context context) { case DOCUMENT_FILE: s = getDocumentFile(false).length(); break; - case OTG: - s = OTGUtil.getDocumentFile(path, context, false).length(); - break; default: break; } @@ -521,14 +524,12 @@ public long folderSize(Context context) { case BOX: case ONEDRIVE: case GDRIVE: + case OTG: return FileUtils.folderSize(new AmazeFile(getPath())); case ROOT: HybridFileParcelable baseFile = generateBaseFileFromParent(); if (baseFile != null) size = baseFile.getSize(); break; - case OTG: - size = FileUtils.otgFolderSize(path, context); - break; case DOCUMENT_FILE: final AtomicLong totalBytes = new AtomicLong(0); OTGUtil.getDocumentFiles( @@ -778,7 +779,13 @@ public InputStream getInputStream(Context context) { case BOX: case ONEDRIVE: case GDRIVE: - return new AmazeFile(getPath()).getInputStream(); + case OTG: + return new AmazeFile(getPath()).getInputStream(new ContextProvider() { + @Override + public Context getContext() { + return context; + } + }); case DOCUMENT_FILE: ContentResolver contentResolver = context.getContentResolver(); DocumentFile documentSourceFile = getDocumentFile(false); @@ -789,16 +796,6 @@ public InputStream getInputStream(Context context) { inputStream = null; } break; - case OTG: - contentResolver = context.getContentResolver(); - documentSourceFile = OTGUtil.getDocumentFile(path, context, false); - try { - inputStream = contentResolver.openInputStream(documentSourceFile.getUri()); - } catch (FileNotFoundException e) { - e.printStackTrace(); - inputStream = null; - } - break; default: try { inputStream = new FileInputStream(path); @@ -822,6 +819,7 @@ public OutputStream getOutputStream(Context context) { case BOX: case ONEDRIVE: case GDRIVE: + case OTG: return new AmazeFile(path).getOutputStream(); case DOCUMENT_FILE: ContentResolver contentResolver = context.getContentResolver(); @@ -833,16 +831,6 @@ public OutputStream getOutputStream(Context context) { outputStream = null; } break; - case OTG: - contentResolver = context.getContentResolver(); - documentSourceFile = OTGUtil.getDocumentFile(path, context, true); - try { - outputStream = contentResolver.openOutputStream(documentSourceFile.getUri()); - } catch (FileNotFoundException e) { - e.printStackTrace(); - outputStream = null; - } - break; default: try { outputStream = FileUtil.getOutputStream(getFile(), context); @@ -857,6 +845,7 @@ public OutputStream getOutputStream(Context context) { public boolean exists() { boolean exists = false; if (isSftp()) { + // TODO use Amaze file final Boolean executionReturn = SshClientUtils.execute( new SFtpClientTemplate(path) { @@ -923,7 +912,7 @@ public boolean isSimpleFile() { } public boolean setLastModified(final long date) { - if (isSmb() || isLocal() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile() || isSftp()) { + if (isSmb() || isLocal() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile() || isSftp() || isOtgFile()) { return new AmazeFile(path).setLastModified(date); } File f = getFile(); @@ -931,15 +920,8 @@ public boolean setLastModified(final long date) { } public void mkdir(Context context) { - if (isSftp() || isSmb() || isLocal() || isRoot() || isCustomPath() || isUnknownFile() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile()) { + if (isSftp() || isSmb() || isLocal() || isRoot() || isCustomPath() || isUnknownFile() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile() || isOtgFile()) { new AmazeFile(path).mkdirs(); - } else if (isOtgFile()) { - if (!exists(context)) { - DocumentFile parentDirectory = OTGUtil.getDocumentFile(getParent(context), context, true); - if (parentDirectory.isDirectory()) { - parentDirectory.createDirectory(getName(context)); - } - } } else if (isDocumentFile()) { if (!exists(context)) { DocumentFile parentDirectory = diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java index 5a00424c62..42dd3e8dc5 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/cloud/CloudUtil.java @@ -35,6 +35,7 @@ import com.amaze.filemanager.file_operations.filesystem.OpenMode; import com.amaze.filemanager.file_operations.filesystem.cloud.CloudStreamer; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAccount; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount; @@ -260,6 +261,7 @@ public static InputStream getThumbnailInputStreamForCloud(Context context, Strin switch (hybridFile.getMode()) { case SFTP: + //TODO repace with AmazeFile's inputStream = SshClientUtils.execute( new SFtpClientTemplate(hybridFile.getPath(), false) { @@ -282,7 +284,7 @@ public void close() throws IOException { }); break; case SMB: - return new AmazeFile(hybridFile.getPath()).getInputStream(); + return new AmazeFile(hybridFile.getPath()).getInputStream(() -> context); case OTG: ContentResolver contentResolver = context.getContentResolver(); DocumentFile documentSourceFile = diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java index d5608ea545..c79d94e2fc 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java @@ -62,6 +62,7 @@ import com.amaze.filemanager.application.AppConfig; import com.amaze.filemanager.file_operations.filesystem.OpenMode; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import com.amaze.filemanager.filesystem.ExternalSdCardOperation; import com.amaze.filemanager.filesystem.HybridFile; import com.amaze.filemanager.filesystem.HybridFileParcelable; @@ -140,7 +141,12 @@ public static long folderSize(AmazeFile directory) { long naiveSize; try { - naiveSize = directory.length(); + naiveSize = directory.length(new ContextProvider() { + @Override + public Context getContext() { + return null;// TODO fix null + } + }); } catch (IOException e) { Log.e(TAG, "Unexpected error getting size from AmazeFile", e); naiveSize = 0; @@ -154,7 +160,12 @@ public static long folderSize(AmazeFile directory) { try { for (AmazeFile file : directory.listFiles()) { - if (file.isFile()) length += file.length(); + if (file.isFile()) length += file.length(new ContextProvider() { + @Override + public Context getContext() { + return null;// TODO fix null + } + }); else length += folderSize(file); } } catch (Exception e) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/otg/OtgAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java similarity index 56% rename from file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/otg/OtgAmazeFilesystem.java rename to app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java index 5263c65539..34747d3389 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/otg/OtgAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java @@ -1,4 +1,8 @@ -package com.amaze.filemanager.file_operations.filesystem.filetypes.otg; +package com.amaze.filemanager.filesystem.otg; + +import android.content.ContentResolver; +import android.content.Context; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -7,17 +11,28 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; -import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFilesystem; +import com.amaze.filemanager.utils.OTGUtil; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import kotlin.NotImplementedError; + public class OtgAmazeFilesystem extends AmazeFilesystem { public static final String TAG = OtgAmazeFilesystem.class.getSimpleName(); + public static final OtgAmazeFilesystem INSTANCE = new OtgAmazeFilesystem(); + public static final String PREFIX = "otg:/"; + static { + AmazeFile.addFilesystem(INSTANCE); + } + + private OtgAmazeFilesystem() {} + @Override public String getPrefix() { return PREFIX; @@ -48,7 +63,7 @@ public String resolve(String parent, String child) { @NonNull @Override public String getDefaultParent() { - return null; + return PREFIX + getSeparator(); } @Override @@ -89,8 +104,15 @@ public long getLastModifiedTime(AmazeFile f) { } @Override - public long getLength(AmazeFile f) throws IOException { - return 0; + public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException { + @Nullable + final Context context = contextProvider.getContext(); + + if(context == null) { + throw new IOException("Error obtaining context for OTG"); + } + + return OTGUtil.getDocumentFile(f.getPath(), context, false).length(); } @Override @@ -111,18 +133,59 @@ public String[] list(AmazeFile f) { @Nullable @Override - public InputStream getInputStream(AmazeFile f) { - return null; + public InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { + @Nullable + final Context context = contextProvider.getContext(); + ContentResolver contentResolver = context.getContentResolver(); + DocumentFile documentSourceFile = OTGUtil.getDocumentFile(f.getPath(), context, false); + try { + return contentResolver.openInputStream(documentSourceFile.getUri()); + } catch (FileNotFoundException e) { + Log.e(TAG, "Error obtaining input stream for OTG", e); + return null; + } } @Nullable @Override public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { - return null; + @Nullable + final Context context = contextProvider.getContext(); + ContentResolver contentResolver = context.getContentResolver(); + DocumentFile documentSourceFile = OTGUtil.getDocumentFile(f.getPath(), context, true); + try { + return contentResolver.openOutputStream(documentSourceFile.getUri()); + } catch (FileNotFoundException e) { + Log.e(TAG, "Error output stream for OTG", e); + return null; + } } @Override public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { + if(f.exists()) { + return true; + } + + @Nullable + final Context context = contextProvider.getContext(); + if(context == null) { + return false; + } + + final String parent = f.getParent(); + if(parent == null) { + return false; + } + + DocumentFile parentDirectory = OTGUtil.getDocumentFile(parent, context, true); + + + if (parentDirectory.isDirectory()) { + parentDirectory.createDirectory(f.getName()); + return true; + } + return false; } @@ -133,7 +196,7 @@ public boolean rename(AmazeFile f1, AmazeFile f2) { @Override public boolean setLastModifiedTime(AmazeFile f, long time) { - return false; + throw new NotImplementedError(); } @Override diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java index e8cba2b29a..8d681d3fc4 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -167,7 +167,7 @@ public Long execute(@NonNull SFTPClient client) throws IOException { } @Override - public long getLength(AmazeFile f) throws IOException { + public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException { if(f.isDirectory()) { final Long returnValue = SshClientUtils.execute( @@ -259,7 +259,7 @@ public List execute(@NonNull SFTPClient client) { @Nullable @Override - public InputStream getInputStream(AmazeFile f) { + public InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { return SshClientUtils.execute( new SFtpClientTemplate(f.getPath(), false) { @Override diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java index f3f848742d..4fefe2fb43 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java @@ -53,6 +53,7 @@ import com.amaze.filemanager.database.models.explorer.Tab; import com.amaze.filemanager.file_operations.filesystem.OpenMode; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import com.amaze.filemanager.file_operations.filesystem.smbstreamer.Streamer; import com.amaze.filemanager.filesystem.CustomFileObserver; import com.amaze.filemanager.filesystem.FileProperties; @@ -1281,8 +1282,18 @@ public ArrayList addToSmb( aMFile.getPath(), "", "", - Formatter.formatFileSize(getContext(), aMFile.length()), - aMFile.length(), + Formatter.formatFileSize(getContext(), aMFile.length(new ContextProvider() { + @Override + public Context getContext() { + return requireContext();// TODO fix retaining instance of MainFragment + } + })), + aMFile.length(new ContextProvider() { + @Override + public Context getContext() { + return requireContext();// TODO fix retaining instance of MainFragment + } + }), false, aMFile.lastModified() + "", false, diff --git a/app/src/main/java/com/amaze/filemanager/utils/OTGUtil.kt b/app/src/main/java/com/amaze/filemanager/utils/OTGUtil.kt index f13ddada1f..9a882d1fb8 100644 --- a/app/src/main/java/com/amaze/filemanager/utils/OTGUtil.kt +++ b/app/src/main/java/com/amaze/filemanager/utils/OTGUtil.kt @@ -33,7 +33,7 @@ import androidx.annotation.RequiresApi import androidx.documentfile.provider.DocumentFile import com.amaze.filemanager.exceptions.DocumentFileNotFoundException import com.amaze.filemanager.file_operations.filesystem.OpenMode -import com.amaze.filemanager.file_operations.filesystem.filetypes.otg.OtgAmazeFilesystem +import com.amaze.filemanager.filesystem.otg.OtgAmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.usb.SingletonUsbOtg import com.amaze.filemanager.file_operations.filesystem.usb.UsbOtgRepresentation import com.amaze.filemanager.filesystem.HybridFileParcelable diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 83a1679fda..701a0733e1 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -653,11 +653,11 @@ public long lastModified() { * if the file does not exist. Some operating systems may return 0L for pathnames * denoting system-dependent entities such as devices or pipes. */ - public long length() throws IOException { + public long length(@NonNull ContextProvider contextProvider) throws IOException { if (isInvalid()) { return 0L; } - return fs.getLength(this); + return fs.getLength(this, contextProvider); } /* -- File operations -- */ @@ -892,8 +892,8 @@ public AmazeFile[] listFiles(AmazeFileFilter filter) { } @NonNull - public InputStream getInputStream() { - return fs.getInputStream(this); + public InputStream getInputStream(@NonNull ContextProvider contextProvider) { + return fs.getInputStream(this, contextProvider); } @NonNull diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java index 511a542a2f..e13378416b 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java @@ -139,7 +139,7 @@ public int prefixLength(@NonNull String path) { * * Note: for directories, this *could* return the size */ - public abstract long getLength(AmazeFile f) throws IOException; + public abstract long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException; /* -- File operations -- */ @@ -165,7 +165,7 @@ public int prefixLength(@NonNull String path) { public abstract String[] list(AmazeFile f); @Nullable - public abstract InputStream getInputStream(AmazeFile f); + public abstract InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider); @Nullable public abstract OutputStream getOutputStream( diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt index 6f7e975a80..c258fdc9cf 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt @@ -29,4 +29,4 @@ interface ContextProvider { * it must return a non null ref to [Context] or crash */ fun getContext(): Context? -} +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java index 7728d4d6c5..5a19b7bdaf 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java @@ -158,7 +158,7 @@ public long getLastModifiedTime(AmazeFile f) { } @Override - public long getLength(AmazeFile f) throws IOException { + public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException { if (f.isDirectory()) { return 0; } @@ -201,7 +201,7 @@ public String[] list(AmazeFile f) { @Nullable @Override - public InputStream getInputStream(AmazeFile f) { + public InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { final CloudStorage account = getAccount().getAccount(); Objects.requireNonNull(account); final String noPrefixPath = removePrefix(f.getPath()); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java index 9d957f8fb5..70845d328d 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -177,7 +177,7 @@ public long getLastModifiedTime(AmazeFile f) { } @Override - public long getLength(AmazeFile f) throws IOException { + public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException { return new File(f.getPath()).length(); } @@ -278,7 +278,7 @@ public String[] list(AmazeFile f) { @Nullable @Override - public InputStream getInputStream(AmazeFile f) { + public InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { try { return new FileInputStream(f.getPath()); } catch (FileNotFoundException e) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java index e40e0733b2..27db128064 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java @@ -212,7 +212,7 @@ public long getLastModifiedTime(AmazeFile f) { } @Override - public long getLength(AmazeFile f) throws SmbException, MalformedURLException { + public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws SmbException, MalformedURLException { return create(f.getPath()).length(); } @@ -314,7 +314,7 @@ public String[] list(AmazeFile f) { @Nullable @Override - public InputStream getInputStream(AmazeFile f) { + public InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { try { return create(f.getPath()).getInputStream(); } catch (IOException e) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java index b73ad2ef4e..7df0fe458b 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java @@ -25,10 +25,14 @@ import java.io.InputStream; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import com.amaze.filemanager.file_operations.filesystem.streams.RandomAccessStream; +import android.content.Context; import android.webkit.MimeTypeMap; +import org.jetbrains.annotations.Nullable; + public class StreamSource extends RandomAccessStream { protected String mime; @@ -65,7 +69,7 @@ public StreamSource(AmazeFile file, long l) { */ public void open() throws IOException { try { - input = file.getInputStream(); // new SmbFileInputStream(file, bufferSize, 1); + input = file.getInputStream(() -> null); // new SmbFileInputStream(file, bufferSize, 1); if (fp > 0) input.skip(fp); } catch (Exception e) { throw new IOException(e); From ded7dcd295dada50f6007c1874977b3afd06dc7b Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 8 Jan 2022 22:22:13 -0300 Subject: [PATCH 23/46] Fixed some abstract functions in AmazeFilesystem --- .../filesystem/otg/OtgAmazeFilesystem.java | 55 ++++- .../filesystem/ssh/SshAmazeFilesystem.java | 226 ++++++++++-------- .../filesystem/filetypes/AmazeFilesystem.java | 90 ++++++- .../filetypes/cloud/CloudAmazeFilesystem.java | 105 ++++---- .../filetypes/file/FileAmazeFilesystem.java | 103 ++++---- .../filetypes/smb/SmbAmazeFilesystem.java | 135 ++++++----- 6 files changed, 446 insertions(+), 268 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java index 34747d3389..9b0d0fb425 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java @@ -83,19 +83,43 @@ public String canonicalize(String path) throws IOException { return null; } - @Override - public int getBooleanAttributes(AmazeFile f) { - return 0; + public boolean exists(AmazeFile f) { + return false;//TODO + } + public boolean isFile(AmazeFile f) { + return false;//TODO + } + public boolean isDirectory(AmazeFile f) { + return false;//TODO + } + public boolean isHidden(AmazeFile f) { + return false;//TODO } - @Override - public boolean checkAccess(AmazeFile f, int access) { - return false; + public boolean canExecute(AmazeFile f) { + return false; //TODO + } + public boolean canWrite(AmazeFile f) { + return false; //TODO + } + public boolean canRead(AmazeFile f) { + return false; //TODO + } + public boolean canAccess(AmazeFile f) { + return false; //TODO } - @Override - public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { - return false; + public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly) { + return false; //TODO + } + public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly) { + return false; //TODO + } + public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { + return false; //TODO + } + public boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly) { + return false; //TODO } @Override @@ -209,9 +233,16 @@ public AmazeFile[] listRoots() { return new AmazeFile[0]; } - @Override - public long getSpace(AmazeFile f, int t) { - return 0; + public long getTotalSpace(AmazeFile f) { + return 0; //TODO + } + + public long getFreeSpace(AmazeFile f) { + return 0; //TODO + } + + public long getUsableSpace(AmazeFile f) { + return 0; //TODO } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java index 8d681d3fc4..5eb56dfe4f 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -103,47 +103,81 @@ public String execute(SFTPClient client) throws IOException { return returnValue; } - @Override - public int getBooleanAttributes(AmazeFile f) { - final SFtpClientTemplate template = new SFtpClientTemplate(f.getPath()) { + public boolean exists(AmazeFile f) { + final SFtpClientTemplate template = new SFtpClientTemplate(f.getPath()) { @Override - public Integer execute(@NonNull SFTPClient client) throws IOException { - Integer r = 0; + public Boolean execute(@NonNull SFTPClient client) throws IOException { + return client.statExistence(f.getPath()) != null; + } + }; - if (client.statExistence(f.getPath()) != null) { - r |= BA_EXISTS; + final Boolean returnValue = SshClientUtils.execute(template); - if (client.lstat(f.getPath()).getType() == REGULAR) { - r |= BA_REGULAR; - } + if(returnValue == null) { + return false; + } + + return returnValue; + } + public boolean isFile(AmazeFile f) { + final SFtpClientTemplate template = new SFtpClientTemplate(f.getPath()) { + @Override + public Boolean execute(@NonNull SFTPClient client) throws IOException { + return client.lstat(f.getPath()).getType() == REGULAR; + } + }; - if (client.lstat(f.getPath()).getType() == DIRECTORY) { - r |= BA_DIRECTORY; - } + final Boolean returnValue = SshClientUtils.execute(template); - //Assume its not hidden - } + if(returnValue == null) { + return false; + } - return r; + return returnValue; + } + public boolean isDirectory(AmazeFile f) { + final SFtpClientTemplate template = new SFtpClientTemplate(f.getPath()) { + @Override + public Boolean execute(@NonNull SFTPClient client) throws IOException { + return client.lstat(f.getPath()).getType() == DIRECTORY; } }; - final Integer returnValue = SshClientUtils.execute(template); + final Boolean returnValue = SshClientUtils.execute(template); if(returnValue == null) { - return 0; + return false; } return returnValue; } + public boolean isHidden(AmazeFile f) { + return false; //Assume its not hidden + } - @Override - public boolean checkAccess(AmazeFile f, int access) { + public boolean canExecute(AmazeFile f) { + throw new NotImplementedError(); + } + public boolean canWrite(AmazeFile f) { + throw new NotImplementedError(); + } + public boolean canRead(AmazeFile f) { + throw new NotImplementedError(); + } + public boolean canAccess(AmazeFile f) { throw new NotImplementedError(); } - @Override - public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { + public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly) { + throw new NotImplementedError(); + } + public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly) { + throw new NotImplementedError(); + } + public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { + throw new NotImplementedError(); + } + public boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly) { throw new NotImplementedError(); } @@ -372,83 +406,77 @@ public AmazeFile[] listRoots() { return new AmazeFile[] { new AmazeFile(getDefaultParent()) }; } - @Override - public long getSpace(AmazeFile f, int t) { - switch (t) { - case SPACE_TOTAL: { - final Long returnValue = - SshClientUtils.execute( - new SFtpClientTemplate(f.getPath()) { - @Override - public Long execute(@NonNull SFTPClient client) throws IOException { - try { - Statvfs.Response response = - new Statvfs.Response( - f.getPath(), - client - .getSFTPEngine() - .request( - Statvfs.request( - client, SshClientUtils.extractRemotePathFrom(f.getPath()))) - .retrieve()); - return response.diskSize(); - } catch (SFTPException e) { - Log.e(TAG, "Error querying SFTP server", e); - return 0L; - } catch (Buffer.BufferException e) { - Log.e(TAG, "Error parsing SFTP reply", e); - return 0L; - } - } - }); - - if (returnValue == null) { - Log.e(TAG, "Error obtaining total space over SFTP"); - - return 0; - } - - return returnValue; - } - case SPACE_FREE: { - final Long returnValue = - SshClientUtils.execute( - new SFtpClientTemplate(f.getPath()) { - @Override - public Long execute(@NonNull SFTPClient client) throws IOException { - try { - Statvfs.Response response = - new Statvfs.Response( - f.getPath(), - client - .getSFTPEngine() - .request( - Statvfs.request( - client, SshClientUtils.extractRemotePathFrom(f.getPath()))) - .retrieve()); - return response.diskFreeSpace(); - } catch (SFTPException e) { - Log.e(TAG, "Error querying server", e); - return 0L; - } catch (Buffer.BufferException e) { - Log.e(TAG, "Error parsing reply", e); - return 0L; - } - } - }); - - if (returnValue == null) { - Log.e(TAG, "Error obtaining usable space over SFTP"); - return 0; - } - - return returnValue; - } - case SPACE_USABLE: - // TODO: Find total storage space of SFTP support is added - throw new NotImplementedError(); - default: - throw new IllegalStateException(); + public long getTotalSpace(AmazeFile f) { + final Long returnValue = + SshClientUtils.execute( + new SFtpClientTemplate(f.getPath()) { + @Override + public Long execute(@NonNull SFTPClient client) throws IOException { + try { + Statvfs.Response response = + new Statvfs.Response( + f.getPath(), + client + .getSFTPEngine() + .request( + Statvfs.request( + client, SshClientUtils.extractRemotePathFrom(f.getPath()))) + .retrieve()); + return response.diskSize(); + } catch (SFTPException e) { + Log.e(TAG, "Error querying SFTP server", e); + return 0L; + } catch (Buffer.BufferException e) { + Log.e(TAG, "Error parsing SFTP reply", e); + return 0L; + } + } + }); + + if (returnValue == null) { + Log.e(TAG, "Error obtaining total space over SFTP"); + + return 0; + } + + return returnValue; + } + public long getFreeSpace(AmazeFile f) { + final Long returnValue = + SshClientUtils.execute( + new SFtpClientTemplate(f.getPath()) { + @Override + public Long execute(@NonNull SFTPClient client) throws IOException { + try { + Statvfs.Response response = + new Statvfs.Response( + f.getPath(), + client + .getSFTPEngine() + .request( + Statvfs.request( + client, SshClientUtils.extractRemotePathFrom(f.getPath()))) + .retrieve()); + return response.diskFreeSpace(); + } catch (SFTPException e) { + Log.e(TAG, "Error querying server", e); + return 0L; + } catch (Buffer.BufferException e) { + Log.e(TAG, "Error parsing reply", e); + return 0L; + } + } + }); + + if (returnValue == null) { + Log.e(TAG, "Error obtaining usable space over SFTP"); + return 0; } + + return returnValue; + } + public long getUsableSpace(AmazeFile f) { + // TODO: Find total storage space of SFTP support is added + throw new NotImplementedError(); } } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java index e13378416b..7679c65e9a 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java @@ -20,6 +20,7 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -107,7 +108,34 @@ public int prefixLength(@NonNull String path) { * Return the simple boolean attributes for the file or directory denoted by the given abstract * pathname, or zero if it does not exist or some other I/O error occurs. */ - public abstract int getBooleanAttributes(AmazeFile f); + public int getBooleanAttributes(AmazeFile f) { + File file = new File(f.getPath()); + int r = 0; + + if (exists(f)) { + r |= BA_EXISTS; + + if (isFile(f)) { + r |= BA_REGULAR; + } + + if (isDirectory(f)) { + r |= BA_DIRECTORY; + } + + if (isHidden(f)) { + r |= BA_HIDDEN; + } + } + + return r; + } + + public abstract boolean exists(AmazeFile f); + public abstract boolean isFile(AmazeFile f); + public abstract boolean isDirectory(AmazeFile f); + public abstract boolean isHidden(AmazeFile f); + @Native public static final int ACCESS_READ = 0x04; @Native public static final int ACCESS_WRITE = 0x02; @@ -120,12 +148,49 @@ public int prefixLength(@NonNull String path) { * this process. The second argument specifies which access, ACCESS_READ, ACCESS_WRITE or * ACCESS_EXECUTE, to check. Return false if access is denied or an I/O error occurs */ - public abstract boolean checkAccess(AmazeFile f, int access); + public boolean checkAccess(AmazeFile f, int access) { + switch (access) { + case ACCESS_EXECUTE: + return canExecute(f); + case ACCESS_WRITE: + return canWrite(f); + case ACCESS_READ: + return canRead(f); + case ACCESS_CHECK_EXISTS: + return canAccess(f); + default: + throw new IllegalStateException(); + } + } + + public abstract boolean canExecute(AmazeFile f); + public abstract boolean canWrite(AmazeFile f); + public abstract boolean canRead(AmazeFile f); + public abstract boolean canAccess(AmazeFile f); + /** * Set on or off the access permission (to owner only or to all) to the file or directory denoted * by the given abstract pathname, based on the parameters enable, access and oweronly. */ - public abstract boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly); + public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { + switch (access) { + case ACCESS_EXECUTE: + return setExecutable(f, enable, owneronly); + case ACCESS_WRITE: + return setWritable(f, enable, owneronly); + case ACCESS_READ: + return setReadable(f, enable, owneronly); + case ACCESS_CHECK_EXISTS: + return setCheckExists(f, enable, owneronly); + default: + throw new IllegalStateException(); + } + } + + public abstract boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly); + public abstract boolean setWritable(AmazeFile f, boolean enable, boolean owneronly); + public abstract boolean setReadable(AmazeFile f, boolean enable, boolean owneronly); + public abstract boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly); /** * Return the time at which the file or directory denoted by the given abstract pathname was last @@ -209,7 +274,24 @@ protected final String removePrefix(@NonNull String path) { public static final int SPACE_FREE = 1; public static final int SPACE_USABLE = 2; - public abstract long getSpace(AmazeFile f, int t); + public long getSpace(AmazeFile f, int t) { + switch (t) { + case SPACE_TOTAL: + return getTotalSpace(f); + case SPACE_FREE: + return getFreeSpace(f); + case SPACE_USABLE: + return getUsableSpace(f); + default: + throw new IllegalStateException(); + } + } + + public abstract long getTotalSpace(AmazeFile f); + + public abstract long getFreeSpace(AmazeFile f); + + public abstract long getUsableSpace(AmazeFile f); /* -- Basic infrastructure -- */ diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java index 5a19b7bdaf..bf5121f7f3 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java @@ -102,49 +102,64 @@ public String canonicalize(String path) throws IOException { return getPrefix() + new File(removePrefix(path)).getCanonicalPath(); } - public final int getBooleanAttributes(AmazeFile f) { + public boolean exists(AmazeFile f) { final CloudStorage account = getAccount().getAccount(); Objects.requireNonNull(account); final String noPrefixPath = removePrefix(f.getPath()); final CloudMetaData metadata = getAccount().getAccount().getMetadata(noPrefixPath); - int r = 0; + return account.exists(noPrefixPath); + } - if (account.exists(noPrefixPath)) { - r |= BA_EXISTS; + public boolean isFile(AmazeFile f) { + return true; // all files are regular (probably) + } - r |= BA_REGULAR; // all files are regular (probably) + public boolean isDirectory(AmazeFile f) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + final String noPrefixPath = removePrefix(f.getPath()); + final CloudMetaData metadata = getAccount().getAccount().getMetadata(noPrefixPath); + return metadata.getFolder(); + } - if (metadata.getFolder()) { - r |= BA_DIRECTORY; - } + public boolean isHidden(AmazeFile f) { + return false; // No way to know if its hidden + } - // No way to know if its hidden - } + public boolean canExecute(AmazeFile f) { + return false; // You aren't executing anything at the cloud + } + public boolean canWrite(AmazeFile f) { + return true; // Probably, can't check + } + public boolean canRead(AmazeFile f) { + return true; // Probably, can't check + } + public boolean canAccess(AmazeFile f) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + final String noPrefixPath = removePrefix(f.getPath()); - return r; - } - - public final boolean checkAccess(AmazeFile f, int access) { - switch (access) { - case ACCESS_EXECUTE: - return false; // You aren't executing anything at the cloud - case ACCESS_WRITE: - return true; // Probably, can't check - case ACCESS_READ: - return true; // Probably, can't check - case ACCESS_CHECK_EXISTS: - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - final String noPrefixPath = removePrefix(f.getPath()); - - return account.exists(noPrefixPath); - default: - throw new IllegalStateException(); - } + return account.exists(noPrefixPath); + } + + @Override + public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly) { + throw new NotImplementedError(); } @Override - public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { + public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly) { + throw new NotImplementedError(); + } + + @Override + public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { + throw new NotImplementedError(); + } + + @Override + public boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly) { throw new NotImplementedError(); } @@ -252,20 +267,26 @@ public AmazeFile[] listRoots() { } @Override - public long getSpace(AmazeFile f, int t) { + public long getTotalSpace(AmazeFile f) { final CloudStorage account = getAccount().getAccount(); Objects.requireNonNull(account); SpaceAllocation spaceAllocation = account.getAllocation(); + return spaceAllocation.getTotal(); + } - switch (t) { - case SPACE_TOTAL: - return spaceAllocation.getTotal(); - case SPACE_FREE: - case SPACE_USABLE: - // The assumption is made that all free space is usable - return spaceAllocation.getTotal() - spaceAllocation.getUsed(); - default: - throw new IllegalStateException(); - } + @Override + public long getFreeSpace(AmazeFile f) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + SpaceAllocation spaceAllocation = account.getAllocation(); + return spaceAllocation.getTotal() - spaceAllocation.getUsed(); + } + + @Override + public long getUsableSpace(AmazeFile f) { + final CloudStorage account = getAccount().getAccount(); + Objects.requireNonNull(account); + SpaceAllocation spaceAllocation = account.getAllocation(); + return spaceAllocation.getTotal() - spaceAllocation.getUsed(); } } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java index 70845d328d..a707ca6f00 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -115,62 +115,55 @@ public String canonicalize(String path) throws IOException { return new File(path).getCanonicalPath(); } - @Override - public int getBooleanAttributes(AmazeFile f) { - File file = new File(f.getPath()); - int r = 0; + public boolean exists(AmazeFile f) { + return f.exists(); + } - if (file.exists()) { - r |= BA_EXISTS; + public boolean isFile(AmazeFile f) { + return f.isFile(); + } - if (file.isFile()) { - r |= BA_REGULAR; - } + public boolean isDirectory(AmazeFile f) { + return f.isDirectory(); + } - if (file.isDirectory()) { - r |= BA_DIRECTORY; - } + public boolean isHidden(AmazeFile f) { + return f.isHidden(); + } - if (file.isHidden()) { - r |= BA_HIDDEN; - } - } + public boolean canExecute(AmazeFile f){ + return new File(f.getPath()).canExecute(); + } - return r; + public boolean canWrite(AmazeFile f){ + return new File(f.getPath()).canWrite(); } - @Override - public boolean checkAccess(AmazeFile f, int access) { - switch (access) { - case ACCESS_EXECUTE: - return new File(f.getPath()).canExecute(); - case ACCESS_WRITE: - return new File(f.getPath()).canWrite(); - case ACCESS_READ: - return new File(f.getPath()).canRead(); - case ACCESS_CHECK_EXISTS: - return new File(f.getPath()).exists(); - default: - throw new IllegalStateException(); - } + public boolean canRead(AmazeFile f){ + return new File(f.getPath()).canRead(); } - @Override - public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { - switch (access) { - case ACCESS_EXECUTE: - return new File(f.getPath()).setExecutable(enable, owneronly); - case ACCESS_WRITE: - return new File(f.getPath()).setWritable(enable, owneronly); - case ACCESS_READ: - return new File(f.getPath()).setReadable(enable, owneronly); - case ACCESS_CHECK_EXISTS: - throw new IllegalArgumentException("This properties cannot be set!"); - default: - throw new IllegalStateException(); - } + public boolean canAccess(AmazeFile f){ + return new File(f.getPath()).exists(); } + public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly) { + return new File(f.getPath()).setExecutable(enable, owneronly); + } + + public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly) { + return new File(f.getPath()).setWritable(enable, owneronly); + } + + public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { + return new File(f.getPath()).setReadable(enable, owneronly); + } + + public boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly) { + throw new IllegalArgumentException("This properties cannot be set!"); + } + + @Override public long getLastModifiedTime(AmazeFile f) { return new File(f.getPath()).lastModified(); @@ -385,18 +378,14 @@ public AmazeFile[] listRoots() { return amazeRoots; } - @Override - public long getSpace(AmazeFile f, int t) { - switch (t) { - case SPACE_TOTAL: - return new File(f.getPath()).getTotalSpace(); - case SPACE_FREE: - return new File(f.getPath()).getFreeSpace(); - case SPACE_USABLE: - return new File(f.getPath()).getUsableSpace(); - default: - throw new IllegalStateException(); - } + public long getTotalSpace(AmazeFile f) { + return new File(f.getPath()).getTotalSpace(); + } + public long getFreeSpace(AmazeFile f) { + return new File(f.getPath()).getFreeSpace(); + } + public long getUsableSpace(AmazeFile f) { + return new File(f.getPath()).getUsableSpace(); } @Override diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java index 27db128064..e831109801 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java @@ -144,60 +144,89 @@ public String canonicalize(String path) throws MalformedURLException { return create(path).getCanonicalPath(); } - @Override - public int getBooleanAttributes(AmazeFile f) { + public boolean exists(AmazeFile f) { + try { + SmbFile smbFile = create(f.getPath()); + return smbFile.exists(); + } catch (MalformedURLException | SmbException e) { + Log.e(TAG, "Failed to get attributes for SMB file", e); + return false; + } + } + + public boolean isFile(AmazeFile f) { try { SmbFile smbFile = create(f.getPath()); - int r = 0; + return smbFile.getType() == SmbConstants.TYPE_FILESYSTEM; + } catch (MalformedURLException | SmbException e) { + Log.e(TAG, "Failed to get attributes for SMB file", e); + return false; + } + } - if (smbFile.exists()) { - r |= BA_EXISTS; + public boolean isDirectory(AmazeFile f) { + try { + SmbFile smbFile = create(f.getPath()); + return smbFile.isDirectory(); + } catch (MalformedURLException | SmbException e) { + Log.e(TAG, "Failed to get attributes for SMB file", e); + return false; + } + } - if (smbFile.getType() == SmbConstants.TYPE_FILESYSTEM) { - r |= BA_REGULAR; - } + public boolean isHidden(AmazeFile f) { + try { + SmbFile smbFile = create(f.getPath()); + return smbFile.isHidden(); + } catch (MalformedURLException | SmbException e) { + Log.e(TAG, "Failed to get attributes for SMB file", e); + return false; + } + } - if (smbFile.isDirectory()) { - r |= BA_DIRECTORY; - } + public boolean canExecute(AmazeFile f) { + throw new NotImplementedError(); + } - if (smbFile.isHidden()) { - r |= BA_HIDDEN; - } - } + public boolean canWrite(AmazeFile f) { + try { + return create(f.getPath()).canWrite(); + } catch (MalformedURLException | SmbException e) { + Log.e(TAG, "Error getting SMB file to check access", e); + return false; + } + } - return r; + public boolean canRead(AmazeFile f) { + try { + return create(f.getPath()).canRead(); } catch (MalformedURLException | SmbException e) { - Log.e(TAG, "Failed to get attributes for SMB file", e); - return 0; + Log.e(TAG, "Error getting SMB file to check access", e); + return false; } } - @Override - public boolean checkAccess(AmazeFile f, int access) { + public boolean canAccess(AmazeFile f) { try { - switch (access) { - case ACCESS_EXECUTE: - throw new NotImplementedError(); - case ACCESS_WRITE: - return create(f.getPath()).canWrite(); - case ACCESS_READ: - return create(f.getPath()).canRead(); - case ACCESS_CHECK_EXISTS: - SmbFile file = create(f.getPath()); - file.setConnectTimeout(2000); - return file.exists(); - default: - throw new IllegalStateException(); - } + SmbFile file = create(f.getPath()); + file.setConnectTimeout(2000); + return file.exists(); } catch (MalformedURLException | SmbException e) { Log.e(TAG, "Error getting SMB file to check access", e); return false; } } - @Override - public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { + public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly){ + throw new NotImplementedError(); + } + public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly){ + throw new NotImplementedError(); + } + public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly){ + throw new NotImplementedError(); + } + public boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly){ throw new NotImplementedError(); } @@ -383,24 +412,22 @@ public AmazeFile[] listRoots() { throw new NotImplementedError(); } - @Override - public long getSpace(AmazeFile f, int t) { - switch (t) { - case SPACE_TOTAL: - // TODO: Find total storage space of SMB when JCIFS adds support - throw new NotImplementedError(); - case SPACE_FREE: - try { - return create(f.getPath()).getDiskFreeSpace(); - } catch (SmbException | MalformedURLException e) { - Log.e(TAG, "Error getting SMB file to read free volume space", e); - return 0; - } - case SPACE_USABLE: - // TODO: Find total storage space of SMB when JCIFS adds support - throw new NotImplementedError(); - default: - throw new IllegalStateException(); + public long getTotalSpace(AmazeFile f) { + // TODO: Find total storage space of SMB when JCIFS adds support + throw new NotImplementedError(); + } + + public long getFreeSpace(AmazeFile f) { + try { + return create(f.getPath()).getDiskFreeSpace(); + } catch (SmbException | MalformedURLException e) { + Log.e(TAG, "Error getting SMB file to read free volume space", e); + return 0; } } + + public long getUsableSpace(AmazeFile f) { + // TODO: Find total storage space of SMB when JCIFS adds support + throw new NotImplementedError(); + } } From f294cc96d10412dc4592dafc4b5ac0363f096efa Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 9 Jan 2022 14:06:29 -0300 Subject: [PATCH 24/46] Added part of otg filesystem --- .../filemanager/filesystem/HybridFile.java | 12 +++-------- .../filesystem/otg/OtgAmazeFilesystem.java | 14 ++++++++++--- .../filesystem/ssh/SshAmazeFilesystem.java | 2 +- .../filesystem/filetypes/AmazeFile.java | 8 ++++---- .../filesystem/filetypes/AmazeFilesystem.java | 20 +------------------ .../filetypes/cloud/CloudAmazeFilesystem.java | 2 +- .../filetypes/file/FileAmazeFilesystem.java | 2 +- .../filetypes/smb/SmbAmazeFilesystem.java | 2 +- 8 files changed, 23 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index a3bc32bc31..416c7ed115 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -557,14 +557,12 @@ public long getUsableSpace() { case ONEDRIVE: case GDRIVE: case SFTP: + case OTG: return new AmazeFile(path).getUsableSpace(); case DOCUMENT_FILE: size = FileProperties.getDeviceStorageRemainingSpace(SafRootHolder.INSTANCE.getVolumeLabel()); break; - case OTG: - // TODO: Get free space from OTG when {@link DocumentFile} API adds support - break; } return size; } @@ -581,12 +579,8 @@ public long getTotal(Context context) { case ONEDRIVE: case GDRIVE: case SFTP: - return new AmazeFile(path).getTotalSpace(); - case OTG: - // TODO: Find total storage space of OTG when {@link DocumentFile} API adds support - DocumentFile documentFile = OTGUtil.getDocumentFile(path, context, false); - size = documentFile.length(); - break; + case OTG: + return new AmazeFile(path).getTotalSpace(() -> context); case DOCUMENT_FILE: size = getDocumentFile(false).length(); break; diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java index 9b0d0fb425..c994065691 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java @@ -233,16 +233,24 @@ public AmazeFile[] listRoots() { return new AmazeFile[0]; } - public long getTotalSpace(AmazeFile f) { - return 0; //TODO + @Override + public long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider) { + @Nullable + final Context context = contextProvider.getContext(); + // TODO: Find total storage space of OTG when {@link DocumentFile} API adds support + DocumentFile documentFile = OTGUtil.getDocumentFile(f.getPath(), context, false); + return documentFile.length(); } + @Override public long getFreeSpace(AmazeFile f) { return 0; //TODO } + @Override public long getUsableSpace(AmazeFile f) { - return 0; //TODO + // TODO: Get free space from OTG when {@link DocumentFile} API adds support + return 0; } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java index 5eb56dfe4f..644888c1f8 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -406,7 +406,7 @@ public AmazeFile[] listRoots() { return new AmazeFile[] { new AmazeFile(getDefaultParent()) }; } - public long getTotalSpace(AmazeFile f) { + public long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider) { final Long returnValue = SshClientUtils.execute( new SFtpClientTemplate(f.getPath()) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 701a0733e1..bb38fa31f1 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -1188,12 +1188,12 @@ public AmazeFile[] listRoots() { * @return The size, in bytes, of the partition or 0L if this abstract pathname does not * name a partition If there is no way to determine, total space is -1 */ - public long getTotalSpace() { + public long getTotalSpace(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return 0L; } try { - return fs.getSpace(this, AmazeFilesystem.SPACE_TOTAL); + return fs.getTotalSpace(this, contextProvider); } catch (NotImplementedError e) { Log.w(TAG, "Call to unimplemented fuction", e); return -1; @@ -1218,7 +1218,7 @@ public long getFreeSpace() { if (isInvalid()) { return 0L; } - return fs.getSpace(this, AmazeFilesystem.SPACE_FREE); + return fs.getFreeSpace(this); } // Android-added: Replaced generic platform info with Android specific one. @@ -1249,7 +1249,7 @@ public long getUsableSpace() { return 0L; } try { - return fs.getSpace(this, AmazeFilesystem.SPACE_USABLE); + return fs.getUsableSpace(this); } catch (NotImplementedError e) { Log.w(TAG, "Call to unimplemented fuction", e); return -1; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java index 7679c65e9a..b058c17e8c 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java @@ -269,25 +269,7 @@ protected final String removePrefix(@NonNull String path) { /** List the available filesystem roots. */ public abstract AmazeFile[] listRoots(); - /* -- Disk usage -- */ - public static final int SPACE_TOTAL = 0; - public static final int SPACE_FREE = 1; - public static final int SPACE_USABLE = 2; - - public long getSpace(AmazeFile f, int t) { - switch (t) { - case SPACE_TOTAL: - return getTotalSpace(f); - case SPACE_FREE: - return getFreeSpace(f); - case SPACE_USABLE: - return getUsableSpace(f); - default: - throw new IllegalStateException(); - } - } - - public abstract long getTotalSpace(AmazeFile f); + public abstract long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider); public abstract long getFreeSpace(AmazeFile f); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java index bf5121f7f3..11ea160f1d 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java @@ -267,7 +267,7 @@ public AmazeFile[] listRoots() { } @Override - public long getTotalSpace(AmazeFile f) { + public long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider) { final CloudStorage account = getAccount().getAccount(); Objects.requireNonNull(account); SpaceAllocation spaceAllocation = account.getAllocation(); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java index a707ca6f00..d74d0798f4 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -378,7 +378,7 @@ public AmazeFile[] listRoots() { return amazeRoots; } - public long getTotalSpace(AmazeFile f) { + public long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider) { return new File(f.getPath()).getTotalSpace(); } public long getFreeSpace(AmazeFile f) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java index e831109801..4347d42075 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java @@ -412,7 +412,7 @@ public AmazeFile[] listRoots() { throw new NotImplementedError(); } - public long getTotalSpace(AmazeFile f) { + public long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider) { // TODO: Find total storage space of SMB when JCIFS adds support throw new NotImplementedError(); } From 07db4813cb2745197de9e84da677e1ac7de62674 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 22 Jan 2022 12:50:37 -0300 Subject: [PATCH 25/46] Add otg filesystem --- .../asynctasks/LoadFilesListTask.java | 2 +- .../filemanager/filesystem/HybridFile.java | 81 ++++------- .../filesystem/MakeDirectoryOperation.kt | 5 +- .../filemanager/filesystem/Operations.java | 6 +- .../filesystem/files/FileUtils.java | 20 +-- .../filesystem/otg/OtgAmazeFilesystem.java | 126 +++++++++++++----- .../filesystem/ssh/SshAmazeFilesystem.java | 32 ++--- .../ui/fragments/MainFragment.java | 8 +- .../filesystem/filetypes/AmazeFile.java | 76 +++++------ .../filesystem/filetypes/AmazeFilesystem.java | 41 +++--- .../filetypes/cloud/CloudAmazeFilesystem.java | 25 ++-- .../filetypes/file/FileAmazeFilesystem.java | 40 +++--- .../filetypes/file/MediaStoreHack.java | 6 +- .../filetypes/smb/SmbAmazeFilesystem.java | 21 ++- 14 files changed, 244 insertions(+), 245 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java index e325c81898..89c0b558ee 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/LoadFilesListTask.java @@ -141,7 +141,7 @@ public LoadFilesListTask( hFile.setPath(hFile.getPath() + "/"); } try { - AmazeFile[] smbFile = new AmazeFile(hFile.getPath()).listFiles(); + AmazeFile[] smbFile = new AmazeFile(hFile.getPath()).listFiles(() -> context); list = mainFragment.addToSmb(smbFile, path, showHiddenFiles); openmode = OpenMode.SMB; } catch (SmbAuthException e) { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 416c7ed115..8200248c9e 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -281,14 +281,9 @@ public long length(Context context) { case BOX: case ONEDRIVE: case GDRIVE: - case OTG: + case OTG: try { - return new AmazeFile(path).length(new ContextProvider() { - @Override - public Context getContext() { - return context; - } - }); + return new AmazeFile(path).length(() -> context); } catch (IOException e) { Log.e(TAG, "Error getting length for file", e); } @@ -331,14 +326,10 @@ public String getName(Context context) { case BOX: case ONEDRIVE: case GDRIVE: + case OTG: return new AmazeFile(path).getName(); case ROOT: return getFile().getName(); - case OTG: - if (!Utils.isNullOrEmpty(name)) { - return name; - } - return OTGUtil.getDocumentFile(path, context, false).getName(); case DOCUMENT_FILE: if (!Utils.isNullOrEmpty(name)) { return name; @@ -382,8 +373,9 @@ public String getParent(Context context) { case BOX: case ONEDRIVE: case GDRIVE: - return new AmazeFile(path).getParent(); case SFTP: + case OTG: + return new AmazeFile(path).getParent(); default: if (path.length() == getName(context).length()) { return null; @@ -396,14 +388,6 @@ public String getParent(Context context) { } } - public String getParentName() { - StringBuilder builder = new StringBuilder(path); - StringBuilder parentPath = - new StringBuilder(builder.substring(0, builder.length() - (getSimpleName().length() + 1))); - String parentName = parentPath.substring(parentPath.lastIndexOf("/") + 1, parentPath.length()); - return parentName; - } - /** * Whether this object refers to a directory or file, handles all types of files * @@ -420,17 +404,11 @@ public boolean isDirectory() { case BOX: case ONEDRIVE: case GDRIVE: - return new AmazeFile(path).isDirectory(); + case OTG: + return new AmazeFile(path).isDirectory(() -> null); case ROOT: isDirectory = NativeOperations.isDirectory(path); break; - case DOCUMENT_FILE: - return getDocumentFile(false).isDirectory(); - case OTG: - // TODO: support for this method in OTG on-the-fly - // you need to manually call {@link RootHelper#getDocumentFile() method - isDirectory = false; - break; default: isDirectory = getFile().isDirectory(); break; @@ -474,16 +452,14 @@ public Boolean execute(SFTPClient client) { case BOX: case ONEDRIVE: case GDRIVE: - return new AmazeFile(path).isDirectory(); + case OTG: + return new AmazeFile(path).isDirectory(() -> context); case ROOT: isDirectory = NativeOperations.isDirectory(path); break; case DOCUMENT_FILE: isDirectory = getDocumentFile(false).isDirectory(); break; - case OTG: - isDirectory = OTGUtil.getDocumentFile(path, context, false).isDirectory(); - break; default: isDirectory = getFile().isDirectory(); break; @@ -500,7 +476,7 @@ public long folderSize() { return folderSize(AppConfig.getInstance()); case SMB: case FILE: - return FileUtils.folderSize(new AmazeFile(getPath())); + return FileUtils.folderSize(new AmazeFile(getPath()), () -> null); case ROOT: HybridFileParcelable baseFile = generateBaseFileFromParent(); if (baseFile != null) size = baseFile.getSize(); @@ -525,7 +501,7 @@ public long folderSize(Context context) { case ONEDRIVE: case GDRIVE: case OTG: - return FileUtils.folderSize(new AmazeFile(getPath())); + return FileUtils.folderSize(new AmazeFile(getPath()), () -> context); case ROOT: HybridFileParcelable baseFile = generateBaseFileFromParent(); if (baseFile != null) size = baseFile.getSize(); @@ -710,8 +686,9 @@ public ArrayList execute(SFTPClient client) { case BOX: case ONEDRIVE: case GDRIVE: + case OTG: ArrayList result = new ArrayList<>(); - for (AmazeFile smbFile1 : new AmazeFile(getPath()).listFiles()) { + for (AmazeFile smbFile1 : new AmazeFile(getPath()).listFiles(() -> context)) { try { HybridFileParcelable baseFile = new HybridFileParcelable(create(smbFile1.getPath())); result.add(baseFile); @@ -721,9 +698,6 @@ public ArrayList execute(SFTPClient client) { } } return result; - case OTG: - arrayList = OTGUtil.getDocumentFilesList(path, context); - break; case DOCUMENT_FILE: final ArrayList hybridFileParcelables = new ArrayList<>(); OTGUtil.getDocumentFiles( @@ -774,12 +748,7 @@ public InputStream getInputStream(Context context) { case ONEDRIVE: case GDRIVE: case OTG: - return new AmazeFile(getPath()).getInputStream(new ContextProvider() { - @Override - public Context getContext() { - return context; - } - }); + return new AmazeFile(getPath()).getInputStream(() -> context); case DOCUMENT_FILE: ContentResolver contentResolver = context.getContentResolver(); DocumentFile documentSourceFile = getDocumentFile(false); @@ -813,8 +782,8 @@ public OutputStream getOutputStream(Context context) { case BOX: case ONEDRIVE: case GDRIVE: - case OTG: - return new AmazeFile(path).getOutputStream(); + case OTG: + return new AmazeFile(path).getOutputStream(() -> context); case DOCUMENT_FILE: ContentResolver contentResolver = context.getContentResolver(); DocumentFile documentSourceFile = getDocumentFile(true); @@ -860,7 +829,7 @@ public Boolean execute(SFTPClient client) throws IOException { //noinspection SimplifiableConditionalExpression exists = executionReturn == null ? false : executionReturn; } else if (isSmb() || isLocal() || isDropBoxFile() || isBoxFile() || isGoogleDriveFile() || isOneDriveFile()) { - return new AmazeFile(path).exists(); + return new AmazeFile(path).exists(() -> null); } else if (isRoot()) { return RootHelper.fileExists(path); } @@ -872,14 +841,17 @@ public Boolean execute(SFTPClient client) throws IOException { public boolean exists(Context context) { boolean exists = false; try { - if (isOtgFile()) { - exists = OTGUtil.getDocumentFile(path, context, false) != null; + if (isSmb() || isLocal() || isDropBoxFile() || isBoxFile() || isGoogleDriveFile() + || isOneDriveFile() || isOtgFile() || isSftp()) { + return new AmazeFile(path).exists(() -> context); } else if (isDocumentFile()) { exists = OTGUtil.getDocumentFile( path, SafRootHolder.getUriRoot(), context, OpenMode.DOCUMENT_FILE, false) != null; - } else return (exists()); + } else { + return (exists()); + } } catch (Exception e) { Log.i(getClass().getSimpleName(), "Failed to find file", e); } @@ -915,7 +887,7 @@ public boolean setLastModified(final long date) { public void mkdir(Context context) { if (isSftp() || isSmb() || isLocal() || isRoot() || isCustomPath() || isUnknownFile() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile() || isOtgFile()) { - new AmazeFile(path).mkdirs(); + new AmazeFile(path).mkdirs(() -> context); } else if (isDocumentFile()) { if (!exists(context)) { DocumentFile parentDirectory = @@ -936,8 +908,9 @@ public void mkdir(Context context) { public boolean delete(Context context, boolean rootmode) throws ShellNotRunningException, SmbException { - if (isSftp() || isSmb() || isLocal() || (isRoot() && !rootmode) || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile()) { - return new AmazeFile(path).delete(); + if (isSftp() || isSmb() || isLocal() || (isRoot() && !rootmode) || isOneDriveFile() + || isBoxFile() || isGoogleDriveFile() || isDropBoxFile() || isOtgFile()) { + return new AmazeFile(path).delete(() -> context); } else if (isRoot() && rootmode) { setMode(OpenMode.ROOT); DeleteFileCommand.INSTANCE.deleteFile(getPath()); diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt b/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt index 79575a84c8..1aceb6a6a8 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/MakeDirectoryOperation.kt @@ -24,6 +24,7 @@ import android.content.Context import android.os.Build import com.amaze.filemanager.file_operations.filesystem.OpenMode import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider import com.amaze.filemanager.utils.OTGUtil import java.io.File import java.io.IOException @@ -76,7 +77,9 @@ object MakeDirectoryOperation { var isSuccessful = true when (file.mode) { OpenMode.SMB, OpenMode.FILE -> - return AmazeFile(file.path).mkdirs() + return AmazeFile(file.path).mkdirs(object : ContextProvider { + override fun getContext() = context + }) OpenMode.OTG -> { val documentFile = OTGUtil.getDocumentFile(file.getPath(), context, true) isSuccessful = documentFile != null diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java index de126c73b1..6b943d4cf8 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java @@ -151,7 +151,7 @@ protected Void doInBackground(Void... params) { return null; } if (file.isSmb()) { - new AmazeFile(file.getPath()).mkdirs(); + new AmazeFile(file.getPath()).mkdirs(() -> context); errorCallBack.done(file, file.exists()); return null; } else if (file.isOtgFile()) { @@ -459,8 +459,8 @@ protected Void doInBackground(Void... params) { errorCallBack.exists(newFile); return null; } - smbFile.renameTo(smbFile1); - if (!smbFile.exists() && smbFile1.exists()) { + smbFile.renameTo(smbFile1, () -> context); + if (!smbFile.exists(() -> context) && smbFile1.exists(() -> context)) { errorCallBack.done(newFile, true); return null; } else { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java index c79d94e2fc..2f89fdbbaa 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java @@ -137,16 +137,11 @@ public static long folderSize(HybridFile directory, OnProgressUpdate updat else return directory.folderSize(AppConfig.getInstance()); } - public static long folderSize(AmazeFile directory) { + public static long folderSize(AmazeFile directory, @NonNull ContextProvider contextProvider) { long naiveSize; try { - naiveSize = directory.length(new ContextProvider() { - @Override - public Context getContext() { - return null;// TODO fix null - } - }); + naiveSize = directory.length(contextProvider); } catch (IOException e) { Log.e(TAG, "Unexpected error getting size from AmazeFile", e); naiveSize = 0; @@ -158,15 +153,10 @@ public Context getContext() { long length = 0; try { - for (AmazeFile file : directory.listFiles()) { + for (AmazeFile file : directory.listFiles(contextProvider)) { - if (file.isFile()) length += file.length(new ContextProvider() { - @Override - public Context getContext() { - return null;// TODO fix null - } - }); - else length += folderSize(file); + if (file.isFile(contextProvider)) length += file.length(contextProvider); + else length += folderSize(file, contextProvider); } } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java index c994065691..50e347e483 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java @@ -13,10 +13,12 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import com.amaze.filemanager.utils.OTGUtil; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; import kotlin.NotImplementedError; @@ -51,13 +53,17 @@ public int prefixLength(@NonNull String path) { @NonNull @Override public String normalize(@NonNull String path) { - return null; + return simpleUnixNormalize(path); } @NonNull @Override public String resolve(String parent, String child) { - return null; + final String prefix = parent.substring(0, prefixLength(parent)); + final String simplePathParent = parent.substring(prefixLength(parent)); + final String simplePathChild = child.substring(prefixLength(child)); + + return prefix + basicUnixResolve(simplePathParent, simplePathChild); } @NonNull @@ -74,52 +80,79 @@ public boolean isAbsolute(AmazeFile f) { @NonNull @Override public String resolve(AmazeFile f) { - return null; + return f.getPath(); } @NonNull @Override public String canonicalize(String path) throws IOException { - return null; + return normalize(path); } - public boolean exists(AmazeFile f) { - return false;//TODO + public boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider) { + @Nullable + final Context context = contextProvider.getContext(); + if(context == null) { + return false; + } + + return OTGUtil.getDocumentFile(f.getPath(), context, false) != null; } - public boolean isFile(AmazeFile f) { - return false;//TODO + public boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider) { + @Nullable + final Context context = contextProvider.getContext(); + if(context == null) { + return false; + } + + return OTGUtil.getDocumentFile(f.getPath(), context, false).isFile(); } - public boolean isDirectory(AmazeFile f) { - return false;//TODO + public boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { + @Nullable + final Context context = contextProvider.getContext(); + if(context == null) { + return false; + } + + return OTGUtil.getDocumentFile(f.getPath(), context, false).isDirectory(); } public boolean isHidden(AmazeFile f) { - return false;//TODO + return false;// There doesn't seem to be hidden files for OTG } - public boolean canExecute(AmazeFile f) { - return false; //TODO + public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider) { + throw new NotImplementedError(); // No way to check } - public boolean canWrite(AmazeFile f) { - return false; //TODO + public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider) { + @Nullable + final Context context = contextProvider.getContext(); + if(context == null) { + return false; + } + + return OTGUtil.getDocumentFile(f.getPath(), context, false).canWrite(); } - public boolean canRead(AmazeFile f) { - return false; //TODO + public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider) { + @Nullable + final Context context = contextProvider.getContext(); + if(context == null) { + return false; + } + + return OTGUtil.getDocumentFile(f.getPath(), context, false).canRead(); } - public boolean canAccess(AmazeFile f) { - return false; //TODO + public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) { + return exists(f, contextProvider); //If the system says it exists, we can access it } public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly) { - return false; //TODO + return false; //TODO find way to set } public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly) { - return false; //TODO + return false; //TODO find way to set } public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { - return false; //TODO - } - public boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly) { - return false; //TODO + return false; //TODO find way to set } @Override @@ -146,13 +179,31 @@ public boolean createFileExclusively(String pathname) throws IOException { @Override public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { - return false; + @Nullable + final Context context = contextProvider.getContext(); + + if(context == null) { + return false; + } + + return OTGUtil.getDocumentFile(f.getPath(), context, false).delete(); } @Nullable @Override - public String[] list(AmazeFile f) { - return new String[0]; + public String[] list(AmazeFile f, @NonNull ContextProvider contextProvider) { + @Nullable final Context context = contextProvider.getContext(); + if(context == null) { + return null; + } + + ArrayList list = new ArrayList<>(); + DocumentFile rootUri = OTGUtil.getDocumentFile(f.getPath(), context, false); + for (DocumentFile file : rootUri.listFiles()) { + list.add(file.getUri().getPath()); + } + + return list.toArray(new String[0]); } @Nullable @@ -187,7 +238,7 @@ public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contex @Override public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { - if(f.exists()) { + if(f.exists(contextProvider)) { return true; } @@ -214,8 +265,12 @@ public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProv } @Override - public boolean rename(AmazeFile f1, AmazeFile f2) { - return false; + public boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider) { + @Nullable + final Context context = contextProvider.getContext(); + ContentResolver contentResolver = context.getContentResolver(); + DocumentFile documentSourceFile = OTGUtil.getDocumentFile(f1.getPath(), context, true); + return documentSourceFile.renameTo(f2.getPath()); } @Override @@ -230,7 +285,14 @@ public boolean setReadOnly(AmazeFile f) { @Override public AmazeFile[] listRoots() { - return new AmazeFile[0]; + File[] roots = File.listRoots(); + AmazeFile[] amazeRoots = new AmazeFile[roots.length]; + + for (int i = 0; i < roots.length; i++) { + amazeRoots[i] = new AmazeFile(roots[i].getPath()); + } + + return new AmazeFile[] { new AmazeFile(getDefaultParent()) }; } @Override diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java index 644888c1f8..4603ee0208 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -8,15 +8,12 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.amaze.filemanager.application.AppConfig; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; -import com.amaze.filemanager.filesystem.HybridFileParcelable; import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.common.Buffer; -import net.schmizz.sshj.sftp.FileAttributes; import net.schmizz.sshj.sftp.RemoteFile; import net.schmizz.sshj.sftp.RemoteResourceInfo; import net.schmizz.sshj.sftp.SFTPClient; @@ -26,15 +23,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.List; -import jcifs.SmbConstants; -import jcifs.smb.SmbException; -import jcifs.smb.SmbFile; import kotlin.NotImplementedError; public class SshAmazeFilesystem extends AmazeFilesystem { @@ -103,7 +96,7 @@ public String execute(SFTPClient client) throws IOException { return returnValue; } - public boolean exists(AmazeFile f) { + public boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider) { final SFtpClientTemplate template = new SFtpClientTemplate(f.getPath()) { @Override public Boolean execute(@NonNull SFTPClient client) throws IOException { @@ -119,7 +112,7 @@ public Boolean execute(@NonNull SFTPClient client) throws IOException { return returnValue; } - public boolean isFile(AmazeFile f) { + public boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider) { final SFtpClientTemplate template = new SFtpClientTemplate(f.getPath()) { @Override public Boolean execute(@NonNull SFTPClient client) throws IOException { @@ -135,7 +128,7 @@ public Boolean execute(@NonNull SFTPClient client) throws IOException { return returnValue; } - public boolean isDirectory(AmazeFile f) { + public boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { final SFtpClientTemplate template = new SFtpClientTemplate(f.getPath()) { @Override public Boolean execute(@NonNull SFTPClient client) throws IOException { @@ -155,16 +148,16 @@ public boolean isHidden(AmazeFile f) { return false; //Assume its not hidden } - public boolean canExecute(AmazeFile f) { + public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider) { throw new NotImplementedError(); } - public boolean canWrite(AmazeFile f) { + public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider) { throw new NotImplementedError(); } - public boolean canRead(AmazeFile f) { + public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider) { throw new NotImplementedError(); } - public boolean canAccess(AmazeFile f) { + public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) { throw new NotImplementedError(); } @@ -177,9 +170,6 @@ public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly) { public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { throw new NotImplementedError(); } - public boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly) { - throw new NotImplementedError(); - } @Override public long getLastModifiedTime(AmazeFile f) { @@ -202,7 +192,7 @@ public Long execute(@NonNull SFTPClient client) throws IOException { @Override public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException { - if(f.isDirectory()) { + if(f.isDirectory(contextProvider)) { final Long returnValue = SshClientUtils.execute( new SFtpClientTemplate(f.getPath()) { @@ -248,7 +238,7 @@ public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { @Override public Boolean execute(@NonNull SFTPClient client) throws IOException { String _path = SshClientUtils.extractRemotePathFrom(f.getPath()); - if (f.isDirectory()) { + if (f.isDirectory(contextProvider)) { client.rmdir(_path); } else { client.rm(_path); @@ -261,7 +251,7 @@ public Boolean execute(@NonNull SFTPClient client) throws IOException { @Nullable @Override - public String[] list(AmazeFile f) { + public String[] list(AmazeFile f, @NonNull ContextProvider contextProvider) { List fileNameList = SshClientUtils.execute( new SFtpClientTemplate>(f.getPath()) { @Override @@ -366,7 +356,7 @@ public Void execute(@NonNull SFTPClient client) { } @Override - public boolean rename(AmazeFile f1, AmazeFile f2) { + public boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider) { Boolean retval = SshClientUtils.execute( new SFtpClientTemplate(f1.getPath()) { diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java index 4fefe2fb43..7b35e2b973 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java @@ -1234,22 +1234,22 @@ public ArrayList addToSmb( mainFragmentViewModel.getSearchHelper().clear(); } for (AmazeFile aMFile : mFile) { - if (!aMFile.isDirectory() && !aMFile.isFile()) { + if (!aMFile.isDirectory(this::requireContext) && !aMFile.isFile(this::requireContext)) { continue; } - if ((DataUtils.getInstance().isFileHidden(aMFile.getPath()) || aMFile.isHidden()) + if ((DataUtils.getInstance().isFileHidden(aMFile.getPath()) || aMFile.isHidden(this::requireContext)) && !showHiddenFiles) { continue; } String name = aMFile.getName(); name = - (aMFile.isDirectory() && name.endsWith("/")) + (aMFile.isDirectory(this::requireContext) && name.endsWith("/")) ? name.substring(0, name.length() - 1) : name; if (path.equals(mainFragmentViewModel.getSmbPath())) { if (name.endsWith("$")) continue; } - if (aMFile.isDirectory()) { + if (aMFile.isDirectory(this::requireContext)) { mainFragmentViewModel.setFolderCount(mainFragmentViewModel.getFolderCount() + 1); Uri.Builder aMFilePathBuilder = Uri.parse(aMFile.getPath()).buildUpon(); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index bb38fa31f1..f5de063a97 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -131,8 +131,6 @@ public class AmazeFile implements Parcelable, Comparable { public static final String TAG = AmazeFile.class.getSimpleName(); - private static ContextProvider contextProvider; - /** The FileSystem object representing the platform's local file system. */ private AmazeFilesystem fs; @@ -527,11 +525,11 @@ private static String slashify(String path, boolean isDirectory) { * @return true if and only if the file specified by this abstract pathname exists * and can be read by the application; false otherwise */ - public boolean canRead() { + public boolean canRead(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } - return fs.checkAccess(this, AmazeFilesystem.ACCESS_READ); + return fs.checkAccess(this, AmazeFilesystem.ACCESS_READ, contextProvider); } // Android-changed. Removed javadoc comment about special privileges @@ -543,11 +541,11 @@ public boolean canRead() { * this abstract pathname and the application is allowed to write to the file; * false otherwise. */ - public boolean canWrite() { + public boolean canWrite(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } - return fs.checkAccess(this, AmazeFilesystem.ACCESS_WRITE); + return fs.checkAccess(this, AmazeFilesystem.ACCESS_WRITE, contextProvider); } /** @@ -556,13 +554,13 @@ public boolean canWrite() { * @return true if and only if the file or directory denoted by this abstract * pathname exists; false otherwise */ - public boolean exists() { + public boolean exists(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } // Android-changed: b/25878034 work around SELinux stat64 denial. - return fs.checkAccess(this, AmazeFilesystem.ACCESS_CHECK_EXISTS); + return fs.checkAccess(this, AmazeFilesystem.ACCESS_CHECK_EXISTS, contextProvider); } /** @@ -576,11 +574,11 @@ public boolean exists() { * @return true if and only if the file denoted by this abstract pathname exists * and is a directory; false otherwise */ - public boolean isDirectory() { + public boolean isDirectory(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } - return ((fs.getBooleanAttributes(this) & AmazeFilesystem.BA_DIRECTORY) != 0); + return ((fs.getBooleanAttributes(this, contextProvider) & AmazeFilesystem.BA_DIRECTORY) != 0); } /** @@ -597,11 +595,11 @@ public boolean isDirectory() { * @return true if and only if the file denoted by this abstract pathname exists * and is a normal file; false otherwise */ - public boolean isFile() { + public boolean isFile(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } - return ((fs.getBooleanAttributes(this) & AmazeFilesystem.BA_REGULAR) != 0); + return ((fs.getBooleanAttributes(this, contextProvider) & AmazeFilesystem.BA_REGULAR) != 0); } /** @@ -613,11 +611,11 @@ public boolean isFile() { * @return true if and only if the file denoted by this abstract pathname is hidden * according to the conventions of the underlying platform */ - public boolean isHidden() { + public boolean isHidden(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } - return ((fs.getBooleanAttributes(this) & AmazeFilesystem.BA_HIDDEN) != 0); + return ((fs.getBooleanAttributes(this, contextProvider) & AmazeFilesystem.BA_HIDDEN) != 0); } /** @@ -694,7 +692,7 @@ public boolean createNewFile() throws IOException { * @return true if and only if the file or directory is successfully deleted; * false otherwise */ - public boolean delete() { + public boolean delete(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } @@ -760,11 +758,11 @@ public void deleteOnExit() { * if this abstract pathname does not denote a directory, or if an I/O error occurs. */ @Nullable - public String[] list() { + public String[] list(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return null; } - return fs.list(this); + return fs.list(this, contextProvider); } /** @@ -785,8 +783,8 @@ public String[] list() { * @see java.nio.file.Files#newDirectoryStream(Path,String) */ @Nullable - public String[] list(AmazeFilenameFilter filter) { - String names[] = list(); + public String[] list(AmazeFilenameFilter filter, @NonNull ContextProvider contextProvider) { + String names[] = list(contextProvider); if ((names == null) || (filter == null)) { return names; } @@ -825,8 +823,8 @@ public String[] list(AmazeFilenameFilter filter) { * error occurs. */ @Nullable - public AmazeFile[] listFiles() { - String[] ss = list(); + public AmazeFile[] listFiles(@NonNull ContextProvider contextProvider) { + String[] ss = list(contextProvider); if (ss == null) return null; int n = ss.length; AmazeFile[] fs = new AmazeFile[n]; @@ -854,8 +852,8 @@ public AmazeFile[] listFiles() { * @see java.nio.file.Files#newDirectoryStream(Path,String) */ @Nullable - public AmazeFile[] listFiles(AmazeFilenameFilter filter) { - String ss[] = list(); + public AmazeFile[] listFiles(AmazeFilenameFilter filter, @NonNull ContextProvider contextProvider) { + String ss[] = list(contextProvider); if (ss == null) return null; ArrayList files = new ArrayList<>(); for (String s : ss) @@ -880,8 +878,8 @@ public AmazeFile[] listFiles(AmazeFilenameFilter filter) { * @see java.nio.file.Files#newDirectoryStream(Path,java.nio.file.DirectoryStream.Filter) */ @Nullable - public AmazeFile[] listFiles(AmazeFileFilter filter) { - String ss[] = list(); + public AmazeFile[] listFiles(AmazeFileFilter filter, @NonNull ContextProvider contextProvider) { + String ss[] = list(contextProvider); if (ss == null) return null; ArrayList files = new ArrayList<>(); for (String s : ss) { @@ -897,7 +895,7 @@ public InputStream getInputStream(@NonNull ContextProvider contextProvider) { } @NonNull - public OutputStream getOutputStream() { + public OutputStream getOutputStream(@NonNull ContextProvider contextProvider) { return fs.getOutputStream(this, contextProvider); } @@ -907,7 +905,7 @@ public OutputStream getOutputStream() { * @return true if and only if the directory was created; false * otherwise */ - public boolean mkdir() { + public boolean mkdir(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } @@ -922,11 +920,11 @@ public boolean mkdir() { * @return true if and only if the directory was created, along with all necessary * parent directories; false otherwise */ - public boolean mkdirs() { - if (exists()) { + public boolean mkdirs(@NonNull ContextProvider contextProvider) { + if (exists(contextProvider)) { return false; } - if (mkdir()) { + if (mkdir(contextProvider)) { return true; } AmazeFile canonFile = null; @@ -937,7 +935,7 @@ public boolean mkdirs() { } AmazeFile parent = canonFile.getParentFile(); - return (parent != null && (parent.mkdirs() || parent.exists()) && canonFile.mkdir()); + return (parent != null && (parent.mkdirs(contextProvider) || parent.exists(contextProvider)) && canonFile.mkdir(contextProvider)); } // Android-changed: Replaced generic platform info with Android specific one. @@ -963,11 +961,11 @@ public boolean mkdirs() { * @param dest The new abstract pathname for the named file * @return true if and only if the renaming succeeded; false otherwise */ - public boolean renameTo(@NonNull AmazeFile dest) { + public boolean renameTo(@NonNull AmazeFile dest, @NonNull ContextProvider contextProvider) { if (this.isInvalid() || dest.isInvalid()) { return false; } - return fs.rename(this, dest); + return fs.rename(this, dest, contextProvider); } /** @@ -1162,11 +1160,11 @@ public boolean setExecutable(boolean executable) { * @return true if and only if the abstract pathname exists and the * application is allowed to execute the file */ - public boolean canExecute() { + public boolean canExecute(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } - return fs.checkAccess(this, AmazeFilesystem.ACCESS_EXECUTE); + return fs.checkAccess(this, AmazeFilesystem.ACCESS_EXECUTE, contextProvider); } /* -- Filesystem interface -- */ @@ -1337,7 +1335,7 @@ static AmazeFile generateFile(String prefix, String suffix, AmazeFile dir) throw * @throws IOException If a file could not be created */ @NonNull - public AmazeFile createTempFile( + public AmazeFile createTempFile(@NonNull ContextProvider contextProvider, @NonNull String prefix, @Nullable String suffix, @Nullable AmazeFile directory) throws IOException { if (prefix.length() < 3) { @@ -1350,7 +1348,7 @@ public AmazeFile createTempFile( AmazeFile f; do { f = TempDirectory.generateFile(prefix, suffix != null ? suffix : ".tmp", tmpdir); - } while ((fs.getBooleanAttributes(f) & AmazeFilesystem.BA_EXISTS) != 0); + } while ((fs.getBooleanAttributes(f, contextProvider) & AmazeFilesystem.BA_EXISTS) != 0); if (!fs.createFileExclusively(f.getPath())) throw new IOException("Unable to create temporary file"); @@ -1383,8 +1381,8 @@ public AmazeFile createTempFile( * @see java.nio.file.Files#createTempDirectory(String,FileAttribute[]) */ @NonNull - public AmazeFile createTempFile(String prefix, String suffix) throws IOException { - return createTempFile(prefix, suffix, null); + public AmazeFile createTempFile(@NonNull ContextProvider contextProvider, String prefix, String suffix) throws IOException { + return createTempFile(contextProvider, prefix, suffix, null); } /* -- Basic infrastructure -- */ diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java index b058c17e8c..56f805f1ba 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java @@ -29,8 +29,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFilesystem; - public abstract class AmazeFilesystem { public static final char STANDARD_SEPARATOR = '/'; @@ -108,18 +106,18 @@ public int prefixLength(@NonNull String path) { * Return the simple boolean attributes for the file or directory denoted by the given abstract * pathname, or zero if it does not exist or some other I/O error occurs. */ - public int getBooleanAttributes(AmazeFile f) { + public int getBooleanAttributes(AmazeFile f, @NonNull ContextProvider contextProvider) { File file = new File(f.getPath()); int r = 0; - if (exists(f)) { + if (exists(f, contextProvider)) { r |= BA_EXISTS; - if (isFile(f)) { + if (isFile(f, contextProvider)) { r |= BA_REGULAR; } - if (isDirectory(f)) { + if (isDirectory(f, contextProvider)) { r |= BA_DIRECTORY; } @@ -131,9 +129,9 @@ public int getBooleanAttributes(AmazeFile f) { return r; } - public abstract boolean exists(AmazeFile f); - public abstract boolean isFile(AmazeFile f); - public abstract boolean isDirectory(AmazeFile f); + public abstract boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider); + public abstract boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider); + public abstract boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider); public abstract boolean isHidden(AmazeFile f); @@ -148,25 +146,25 @@ public int getBooleanAttributes(AmazeFile f) { * this process. The second argument specifies which access, ACCESS_READ, ACCESS_WRITE or * ACCESS_EXECUTE, to check. Return false if access is denied or an I/O error occurs */ - public boolean checkAccess(AmazeFile f, int access) { + public boolean checkAccess(AmazeFile f, int access, @NonNull ContextProvider contextProvider) { switch (access) { case ACCESS_EXECUTE: - return canExecute(f); + return canExecute(f, contextProvider); case ACCESS_WRITE: - return canWrite(f); + return canWrite(f, contextProvider); case ACCESS_READ: - return canRead(f); + return canRead(f, contextProvider); case ACCESS_CHECK_EXISTS: - return canAccess(f); + return canAccess(f, contextProvider); default: throw new IllegalStateException(); } } - public abstract boolean canExecute(AmazeFile f); - public abstract boolean canWrite(AmazeFile f); - public abstract boolean canRead(AmazeFile f); - public abstract boolean canAccess(AmazeFile f); + public abstract boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider); + public abstract boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider); + public abstract boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider); + public abstract boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider); /** * Set on or off the access permission (to owner only or to all) to the file or directory denoted @@ -180,8 +178,6 @@ public boolean setPermission(AmazeFile f, int access, boolean enable, boolean ow return setWritable(f, enable, owneronly); case ACCESS_READ: return setReadable(f, enable, owneronly); - case ACCESS_CHECK_EXISTS: - return setCheckExists(f, enable, owneronly); default: throw new IllegalStateException(); } @@ -190,7 +186,6 @@ public boolean setPermission(AmazeFile f, int access, boolean enable, boolean ow public abstract boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly); public abstract boolean setWritable(AmazeFile f, boolean enable, boolean owneronly); public abstract boolean setReadable(AmazeFile f, boolean enable, boolean owneronly); - public abstract boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly); /** * Return the time at which the file or directory denoted by the given abstract pathname was last @@ -227,7 +222,7 @@ public boolean setPermission(AmazeFile f, int access, boolean enable, boolean ow * . */ @Nullable - public abstract String[] list(AmazeFile f); + public abstract String[] list(AmazeFile f, @NonNull ContextProvider contextProvider); @Nullable public abstract InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider); @@ -246,7 +241,7 @@ public abstract OutputStream getOutputStream( * Rename the file or directory denoted by the first abstract pathname to the second abstract * pathname, returning true if and only if the operation succeeds. */ - public abstract boolean rename(AmazeFile f1, AmazeFile f2); + public abstract boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider); /** * Set the last-modified time of the file or directory denoted by the given abstract pathname, diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java index 11ea160f1d..74569d0072 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java @@ -102,7 +102,7 @@ public String canonicalize(String path) throws IOException { return getPrefix() + new File(removePrefix(path)).getCanonicalPath(); } - public boolean exists(AmazeFile f) { + public boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider) { final CloudStorage account = getAccount().getAccount(); Objects.requireNonNull(account); final String noPrefixPath = removePrefix(f.getPath()); @@ -110,11 +110,11 @@ public boolean exists(AmazeFile f) { return account.exists(noPrefixPath); } - public boolean isFile(AmazeFile f) { + public boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider) { return true; // all files are regular (probably) } - public boolean isDirectory(AmazeFile f) { + public boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { final CloudStorage account = getAccount().getAccount(); Objects.requireNonNull(account); final String noPrefixPath = removePrefix(f.getPath()); @@ -126,16 +126,16 @@ public boolean isHidden(AmazeFile f) { return false; // No way to know if its hidden } - public boolean canExecute(AmazeFile f) { + public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider) { return false; // You aren't executing anything at the cloud } - public boolean canWrite(AmazeFile f) { + public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider) { return true; // Probably, can't check } - public boolean canRead(AmazeFile f) { + public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider) { return true; // Probably, can't check } - public boolean canAccess(AmazeFile f) { + public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) { final CloudStorage account = getAccount().getAccount(); Objects.requireNonNull(account); final String noPrefixPath = removePrefix(f.getPath()); @@ -158,11 +158,6 @@ public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { throw new NotImplementedError(); } - @Override - public boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly) { - throw new NotImplementedError(); - } - @Override public long getLastModifiedTime(AmazeFile f) { final CloudStorage account = getAccount().getAccount(); @@ -174,7 +169,7 @@ public long getLastModifiedTime(AmazeFile f) { @Override public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException { - if (f.isDirectory()) { + if (f.isDirectory(contextProvider)) { return 0; } @@ -201,7 +196,7 @@ public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { @Nullable @Override - public String[] list(AmazeFile f) { + public String[] list(AmazeFile f, @NonNull ContextProvider contextProvider) { final CloudStorage account = getAccount().getAccount(); Objects.requireNonNull(account); final String noPrefixPath = removePrefix(f.getPath()); @@ -239,7 +234,7 @@ public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProv } @Override - public boolean rename(AmazeFile f1, AmazeFile f2) { + public boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider) { final CloudStorage account = getAccount().getAccount(); Objects.requireNonNull(account); account.move(removePrefix(f1.getPath()), removePrefix(f2.getPath())); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java index d74d0798f4..494dbdaafd 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -115,35 +115,35 @@ public String canonicalize(String path) throws IOException { return new File(path).getCanonicalPath(); } - public boolean exists(AmazeFile f) { - return f.exists(); + public boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider) { + return f.exists(contextProvider); } - public boolean isFile(AmazeFile f) { - return f.isFile(); + public boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider) { + return new File(f.getPath()).isFile(); } - public boolean isDirectory(AmazeFile f) { - return f.isDirectory(); + public boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { + return new File(f.getPath()).isDirectory(); } public boolean isHidden(AmazeFile f) { - return f.isHidden(); + return new File(f.getPath()).isHidden(); } - public boolean canExecute(AmazeFile f){ + public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider){ return new File(f.getPath()).canExecute(); } - public boolean canWrite(AmazeFile f){ + public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider){ return new File(f.getPath()).canWrite(); } - public boolean canRead(AmazeFile f){ + public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider){ return new File(f.getPath()).canRead(); } - public boolean canAccess(AmazeFile f){ + public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider){ return new File(f.getPath()).exists(); } @@ -159,10 +159,6 @@ public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { return new File(f.getPath()).setReadable(enable, owneronly); } - public boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly) { - throw new IllegalArgumentException("This properties cannot be set!"); - } - @Override public long getLastModifiedTime(AmazeFile f) { @@ -181,8 +177,8 @@ public boolean createFileExclusively(String pathname) throws IOException { @Override public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { - if (f.isDirectory()) { - AmazeFile[] children = f.listFiles(); + if (f.isDirectory(contextProvider)) { + AmazeFile[] children = f.listFiles(contextProvider); if (children != null) { for (AmazeFile child : children) { delete(child, contextProvider); @@ -220,7 +216,7 @@ public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { new String[] {f.getAbsolutePath()}); } - return !f.exists(); + return !f.exists(contextProvider); } if (new File(f.getPath()).delete()) { @@ -254,7 +250,7 @@ public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { return false; } resolver.delete(uri, null, null); - return !f.exists(); + return !f.exists(contextProvider); } catch (SecurityException e) { Log.e(TAG, "Security exception when checking for file " + f.getAbsolutePath(), e); } @@ -265,7 +261,7 @@ public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { @Nullable @Override - public String[] list(AmazeFile f) { + public String[] list(AmazeFile f, @NonNull ContextProvider contextProvider) { return new File(f.getPath()).list(); } @@ -284,7 +280,7 @@ public InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextP @Override public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { try { - if (f.canWrite()) { + if (f.canWrite(contextProvider)) { return new FileOutputStream(f.getPath()); } else { final Context context = contextProvider.getContext(); @@ -352,7 +348,7 @@ public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProv } @Override - public boolean rename(AmazeFile f1, AmazeFile f2) { + public boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider) { return new File(f1.getPath()).renameTo(new File(f2.getPath())); } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java index 23e1e11cf9..aec2325300 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/MediaStoreHack.java @@ -262,8 +262,8 @@ private static File installTemporaryTrack(final Context context) throws IOExcept } public static boolean mkdir(final Context context, final AmazeFile file) throws IOException { - if (file.exists()) { - return file.isDirectory(); + if (file.exists(() -> context)) { + return file.isDirectory(() -> context); } final File tmpFile = new File(new File(file.getPath()), ".MediaWriteTemp"); final int albumId = getTemporaryAlbumId(context); @@ -284,7 +284,7 @@ public static boolean mkdir(final Context context, final AmazeFile file) throws } finally { delete(context, tmpFile); } - return file.exists(); + return file.exists(() -> context); } public static boolean mkfile(final Context context, final File file) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java index 4347d42075..b7abd63690 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java @@ -144,7 +144,7 @@ public String canonicalize(String path) throws MalformedURLException { return create(path).getCanonicalPath(); } - public boolean exists(AmazeFile f) { + public boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider) { try { SmbFile smbFile = create(f.getPath()); return smbFile.exists(); @@ -154,7 +154,7 @@ public boolean exists(AmazeFile f) { } } - public boolean isFile(AmazeFile f) { + public boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider) { try { SmbFile smbFile = create(f.getPath()); return smbFile.getType() == SmbConstants.TYPE_FILESYSTEM; @@ -164,7 +164,7 @@ public boolean isFile(AmazeFile f) { } } - public boolean isDirectory(AmazeFile f) { + public boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { try { SmbFile smbFile = create(f.getPath()); return smbFile.isDirectory(); @@ -184,11 +184,11 @@ public boolean isHidden(AmazeFile f) { } } - public boolean canExecute(AmazeFile f) { + public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider) { throw new NotImplementedError(); } - public boolean canWrite(AmazeFile f) { + public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider) { try { return create(f.getPath()).canWrite(); } catch (MalformedURLException | SmbException e) { @@ -197,7 +197,7 @@ public boolean canWrite(AmazeFile f) { } } - public boolean canRead(AmazeFile f) { + public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider) { try { return create(f.getPath()).canRead(); } catch (MalformedURLException | SmbException e) { @@ -206,7 +206,7 @@ public boolean canRead(AmazeFile f) { } } - public boolean canAccess(AmazeFile f) { + public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) { try { SmbFile file = create(f.getPath()); file.setConnectTimeout(2000); @@ -226,9 +226,6 @@ public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly){ public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly){ throw new NotImplementedError(); } - public boolean setCheckExists(AmazeFile f, boolean enable, boolean owneronly){ - throw new NotImplementedError(); - } @Override public long getLastModifiedTime(AmazeFile f) { @@ -324,7 +321,7 @@ public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { } @Override - public String[] list(AmazeFile f) { + public String[] list(AmazeFile f, @NonNull ContextProvider contextProvider) { String[] list; try { list = create(f.getPath()).list(); @@ -375,7 +372,7 @@ public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProv } @Override - public boolean rename(AmazeFile f1, AmazeFile f2) { + public boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider) { try { create(f1.getPath()).renameTo(create(f2.getPath())); return true; From 8cc44e8c01bb15b552cfcca03ff0947012c48149 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 22 Jan 2022 12:51:27 -0300 Subject: [PATCH 26/46] Apply spotless --- .../filesystem/filetypes/AmazeFile.java | 38 +++++++++---------- .../filesystem/filetypes/AmazeFilesystem.java | 21 ++++++---- .../filesystem/filetypes/ContextProvider.kt | 2 +- .../filetypes/cloud/CloudAmazeFilesystem.java | 5 ++- .../filetypes/file/FileAmazeFilesystem.java | 13 ++++--- .../filetypes/smb/SmbAmazeFilesystem.java | 19 ++++++---- .../filesystem/smbstreamer/StreamSource.java | 4 -- 7 files changed, 55 insertions(+), 47 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index f5de063a97..477d3ee875 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -30,14 +30,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.Set; - -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.file.FileAmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFilesystem; import android.os.Parcel; import android.os.Parcelable; @@ -317,9 +309,8 @@ public AmazeFile(@Nullable AmazeFile parent, @NonNull String child) { } private void loadFilesystem(String path) { - for (AmazeFilesystem filesystem : - filesystems) { - if(filesystem.isPathOfThisFilesystem(path)) { + for (AmazeFilesystem filesystem : filesystems) { + if (filesystem.isPathOfThisFilesystem(path)) { fs = filesystem; } } @@ -341,8 +332,8 @@ public String getName() { if (index < prefixLength) { return path.substring(prefixLength); } - if(path.endsWith("/")) { - int newIndex = path.substring(0, path.length()-2).lastIndexOf(separatorChar); + if (path.endsWith("/")) { + int newIndex = path.substring(0, path.length() - 2).lastIndexOf(separatorChar); if (newIndex < prefixLength) { return path.substring(prefixLength); } @@ -371,8 +362,8 @@ public String getParent() { } return null; } - if(path.endsWith("/")) { - int newIndex = path.substring(0, path.length()-2).lastIndexOf(separatorChar); + if (path.endsWith("/")) { + int newIndex = path.substring(0, path.length() - 2).lastIndexOf(separatorChar); if (newIndex < prefixLength) { if ((prefixLength > 0) && (path.length() > prefixLength)) { return path.substring(0, prefixLength); @@ -852,7 +843,8 @@ public AmazeFile[] listFiles(@NonNull ContextProvider contextProvider) { * @see java.nio.file.Files#newDirectoryStream(Path,String) */ @Nullable - public AmazeFile[] listFiles(AmazeFilenameFilter filter, @NonNull ContextProvider contextProvider) { + public AmazeFile[] listFiles( + AmazeFilenameFilter filter, @NonNull ContextProvider contextProvider) { String ss[] = list(contextProvider); if (ss == null) return null; ArrayList files = new ArrayList<>(); @@ -935,7 +927,9 @@ public boolean mkdirs(@NonNull ContextProvider contextProvider) { } AmazeFile parent = canonFile.getParentFile(); - return (parent != null && (parent.mkdirs(contextProvider) || parent.exists(contextProvider)) && canonFile.mkdir(contextProvider)); + return (parent != null + && (parent.mkdirs(contextProvider) || parent.exists(contextProvider)) + && canonFile.mkdir(contextProvider)); } // Android-changed: Replaced generic platform info with Android specific one. @@ -1335,8 +1329,11 @@ static AmazeFile generateFile(String prefix, String suffix, AmazeFile dir) throw * @throws IOException If a file could not be created */ @NonNull - public AmazeFile createTempFile(@NonNull ContextProvider contextProvider, - @NonNull String prefix, @Nullable String suffix, @Nullable AmazeFile directory) + public AmazeFile createTempFile( + @NonNull ContextProvider contextProvider, + @NonNull String prefix, + @Nullable String suffix, + @Nullable AmazeFile directory) throws IOException { if (prefix.length() < 3) { throw new IllegalArgumentException("Prefix string too short"); @@ -1381,7 +1378,8 @@ public AmazeFile createTempFile(@NonNull ContextProvider contextProvider, * @see java.nio.file.Files#createTempDirectory(String,FileAttribute[]) */ @NonNull - public AmazeFile createTempFile(@NonNull ContextProvider contextProvider, String prefix, String suffix) throws IOException { + public AmazeFile createTempFile( + @NonNull ContextProvider contextProvider, String prefix, String suffix) throws IOException { return createTempFile(contextProvider, prefix, suffix, null); } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java index 56f805f1ba..e3908dfec1 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java @@ -35,9 +35,7 @@ public abstract class AmazeFilesystem { /* -- Normalization and construction -- */ - /** - * filesystem prefix - */ + /** filesystem prefix */ public abstract String getPrefix(); /** Is the path of this filesystem? */ @@ -130,10 +128,12 @@ public int getBooleanAttributes(AmazeFile f, @NonNull ContextProvider contextPro } public abstract boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider); + public abstract boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider); + public abstract boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider); - public abstract boolean isHidden(AmazeFile f); + public abstract boolean isHidden(AmazeFile f); @Native public static final int ACCESS_READ = 0x04; @Native public static final int ACCESS_WRITE = 0x02; @@ -162,8 +162,11 @@ public boolean checkAccess(AmazeFile f, int access, @NonNull ContextProvider con } public abstract boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider); + public abstract boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider); + public abstract boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider); + public abstract boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider); /** @@ -184,7 +187,9 @@ public boolean setPermission(AmazeFile f, int access, boolean enable, boolean ow } public abstract boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly); + public abstract boolean setWritable(AmazeFile f, boolean enable, boolean owneronly); + public abstract boolean setReadable(AmazeFile f, boolean enable, boolean owneronly); /** @@ -197,9 +202,10 @@ public boolean setPermission(AmazeFile f, int access, boolean enable, boolean ow * Return the length in bytes of the file denoted by the given abstract pathname, or zero if it * does not exist, or some other I/O error occurs. * - * Note: for directories, this *could* return the size + *

Note: for directories, this *could* return the size */ - public abstract long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException; + public abstract long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) + throws IOException; /* -- File operations -- */ @@ -241,7 +247,8 @@ public abstract OutputStream getOutputStream( * Rename the file or directory denoted by the first abstract pathname to the second abstract * pathname, returning true if and only if the operation succeeds. */ - public abstract boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider); + public abstract boolean rename( + AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider); /** * Set the last-modified time of the file or directory denoted by the given abstract pathname, diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt index c258fdc9cf..6f7e975a80 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/ContextProvider.kt @@ -29,4 +29,4 @@ interface ContextProvider { * it must return a non null ref to [Context] or crash */ fun getContext(): Context? -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java index 74569d0072..072edf1139 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java @@ -50,7 +50,7 @@ public abstract class CloudAmazeFilesystem extends AmazeFilesystem { public int prefixLength(@NonNull String path) { if (path.length() == 0) { throw new IllegalArgumentException( - "This should never happen, all paths must start with OTG prefix"); + "This should never happen, all paths must start with OTG prefix"); } return super.prefixLength(path); @@ -129,12 +129,15 @@ public boolean isHidden(AmazeFile f) { public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider) { return false; // You aren't executing anything at the cloud } + public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider) { return true; // Probably, can't check } + public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider) { return true; // Probably, can't check } + public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) { final CloudStorage account = getAccount().getAccount(); Objects.requireNonNull(account); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java index 494dbdaafd..2ea17d1a26 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java @@ -57,7 +57,7 @@ public class FileAmazeFilesystem extends AmazeFilesystem { AmazeFile.addFilesystem(INSTANCE); } - private FileAmazeFilesystem() { } + private FileAmazeFilesystem() {} @Override public boolean isPathOfThisFilesystem(@NonNull String path) { @@ -131,19 +131,19 @@ public boolean isHidden(AmazeFile f) { return new File(f.getPath()).isHidden(); } - public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider){ + public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider) { return new File(f.getPath()).canExecute(); } - public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider){ + public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider) { return new File(f.getPath()).canWrite(); } - public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider){ + public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider) { return new File(f.getPath()).canRead(); } - public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider){ + public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) { return new File(f.getPath()).exists(); } @@ -159,7 +159,6 @@ public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { return new File(f.getPath()).setReadable(enable, owneronly); } - @Override public long getLastModifiedTime(AmazeFile f) { return new File(f.getPath()).lastModified(); @@ -377,9 +376,11 @@ public AmazeFile[] listRoots() { public long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider) { return new File(f.getPath()).getTotalSpace(); } + public long getFreeSpace(AmazeFile f) { return new File(f.getPath()).getFreeSpace(); } + public long getUsableSpace(AmazeFile f) { return new File(f.getPath()).getUsableSpace(); } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java index b7abd63690..eab9847576 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java @@ -146,8 +146,8 @@ public String canonicalize(String path) throws MalformedURLException { public boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider) { try { - SmbFile smbFile = create(f.getPath()); - return smbFile.exists(); + SmbFile smbFile = create(f.getPath()); + return smbFile.exists(); } catch (MalformedURLException | SmbException e) { Log.e(TAG, "Failed to get attributes for SMB file", e); return false; @@ -217,13 +217,15 @@ public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) } } - public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly){ + public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly) { throw new NotImplementedError(); } - public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly){ + + public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly) { throw new NotImplementedError(); } - public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly){ + + public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { throw new NotImplementedError(); } @@ -238,7 +240,8 @@ public long getLastModifiedTime(AmazeFile f) { } @Override - public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws SmbException, MalformedURLException { + public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) + throws SmbException, MalformedURLException { return create(f.getPath()).length(); } @@ -261,8 +264,8 @@ public static SmbFile create(String path) throws MalformedURLException { noExtraInfoPath = path; } - final CIFSContext context = CifsContexts - .createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) + final CIFSContext context = + CifsContexts.createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) .withCredentials(createFrom(userInfo)); return new SmbFile(noExtraInfoPath, context); diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java index 7df0fe458b..e5f14769b5 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/smbstreamer/StreamSource.java @@ -25,14 +25,10 @@ import java.io.InputStream; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import com.amaze.filemanager.file_operations.filesystem.streams.RandomAccessStream; -import android.content.Context; import android.webkit.MimeTypeMap; -import org.jetbrains.annotations.Nullable; - public class StreamSource extends RandomAccessStream { protected String mime; From 66809154b7b598e749c1438e60468cd648500881 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 22 Jan 2022 13:01:37 -0300 Subject: [PATCH 27/46] Simplified some code with filesystems --- .../filesystem/filetypes/AmazeFile.java | 14 ++--- .../filesystem/filetypes/AmazeFilesystem.java | 59 ++++++++----------- 2 files changed, 30 insertions(+), 43 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java index 477d3ee875..ba49efcb2b 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java @@ -520,7 +520,7 @@ public boolean canRead(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } - return fs.checkAccess(this, AmazeFilesystem.ACCESS_READ, contextProvider); + return fs.canRead(this, contextProvider); } // Android-changed. Removed javadoc comment about special privileges @@ -536,7 +536,7 @@ public boolean canWrite(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } - return fs.checkAccess(this, AmazeFilesystem.ACCESS_WRITE, contextProvider); + return fs.canWrite(this, contextProvider); } /** @@ -551,7 +551,7 @@ public boolean exists(@NonNull ContextProvider contextProvider) { } // Android-changed: b/25878034 work around SELinux stat64 denial. - return fs.checkAccess(this, AmazeFilesystem.ACCESS_CHECK_EXISTS, contextProvider); + return fs.canAccess(this, contextProvider); } /** @@ -1024,7 +1024,7 @@ public boolean setWritable(boolean writable, boolean ownerOnly) { if (isInvalid()) { return false; } - return fs.setPermission(this, AmazeFilesystem.ACCESS_WRITE, writable, ownerOnly); + return fs.setWritable(this, writable, ownerOnly); } // Android-changed. Removed javadoc comment about special privileges @@ -1072,7 +1072,7 @@ public boolean setReadable(boolean readable, boolean ownerOnly) { if (isInvalid()) { return false; } - return fs.setPermission(this, AmazeFilesystem.ACCESS_READ, readable, ownerOnly); + return fs.setReadable(this, readable, ownerOnly); } // Android-changed. Removed javadoc comment about special privileges @@ -1121,7 +1121,7 @@ public boolean setExecutable(boolean executable, boolean ownerOnly) { if (isInvalid()) { return false; } - return fs.setPermission(this, AmazeFilesystem.ACCESS_EXECUTE, executable, ownerOnly); + return fs.setExecutable(this, executable, ownerOnly); } // Android-changed. Removed javadoc comment about special privileges @@ -1158,7 +1158,7 @@ public boolean canExecute(@NonNull ContextProvider contextProvider) { if (isInvalid()) { return false; } - return fs.checkAccess(this, AmazeFilesystem.ACCESS_EXECUTE, contextProvider); + return fs.canExecute(this, contextProvider); } /* -- Filesystem interface -- */ diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java index e3908dfec1..cfdc196bdc 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java @@ -135,61 +135,48 @@ public int getBooleanAttributes(AmazeFile f, @NonNull ContextProvider contextPro public abstract boolean isHidden(AmazeFile f); - @Native public static final int ACCESS_READ = 0x04; - @Native public static final int ACCESS_WRITE = 0x02; - @Native public static final int ACCESS_EXECUTE = 0x01; - // Android-added: b/25878034, to support F.exists() reimplementation. - public static final int ACCESS_CHECK_EXISTS = 0x08; - /** * Check whether the file or directory denoted by the given abstract pathname may be accessed by - * this process. The second argument specifies which access, ACCESS_READ, ACCESS_WRITE or - * ACCESS_EXECUTE, to check. Return false if access is denied or an I/O error occurs + * this process. Return false if access is denied or an I/O error occurs */ - public boolean checkAccess(AmazeFile f, int access, @NonNull ContextProvider contextProvider) { - switch (access) { - case ACCESS_EXECUTE: - return canExecute(f, contextProvider); - case ACCESS_WRITE: - return canWrite(f, contextProvider); - case ACCESS_READ: - return canRead(f, contextProvider); - case ACCESS_CHECK_EXISTS: - return canAccess(f, contextProvider); - default: - throw new IllegalStateException(); - } - } - public abstract boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider); + /** + * Check whether the file or directory denoted by the given abstract pathname may be accessed by + * this process. Return false if access is denied or an I/O error occurs + */ public abstract boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider); + /** + * Check whether the file or directory denoted by the given abstract pathname may be accessed by + * this process. Return false if access is denied or an I/O error occurs + */ public abstract boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider); + /** + * Check whether the file or directory denoted by the given abstract pathname may be accessed by + * this process. Return false if access is denied or an I/O error occurs + * + * Android-added: b/25878034, to support F.exists() reimplementation. + */ public abstract boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider); /** * Set on or off the access permission (to owner only or to all) to the file or directory denoted * by the given abstract pathname, based on the parameters enable, access and oweronly. */ - public boolean setPermission(AmazeFile f, int access, boolean enable, boolean owneronly) { - switch (access) { - case ACCESS_EXECUTE: - return setExecutable(f, enable, owneronly); - case ACCESS_WRITE: - return setWritable(f, enable, owneronly); - case ACCESS_READ: - return setReadable(f, enable, owneronly); - default: - throw new IllegalStateException(); - } - } - public abstract boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly); + /** + * Set on or off the access permission (to owner only or to all) to the file or directory denoted + * by the given abstract pathname, based on the parameters enable, access and oweronly. + */ public abstract boolean setWritable(AmazeFile f, boolean enable, boolean owneronly); + /** + * Set on or off the access permission (to owner only or to all) to the file or directory denoted + * by the given abstract pathname, based on the parameters enable, access and oweronly. + */ public abstract boolean setReadable(AmazeFile f, boolean enable, boolean owneronly); /** From a89b4f9c0775fa0cfb2e02167c760a601b6443db Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Wed, 26 Jan 2022 21:50:29 -0300 Subject: [PATCH 28/46] Convert AmazeFilesystem to kotlin --- .../filesystem/filetypes/AmazeFilesystem.java | 398 ------------------ .../filesystem/filetypes/AmazeFilesystem.kt | 367 ++++++++++++++++ 2 files changed, 367 insertions(+), 398 deletions(-) delete mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java deleted file mode 100644 index cfdc196bdc..0000000000 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.java +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2014-2014 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.annotation.Native; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public abstract class AmazeFilesystem { - - public static final char STANDARD_SEPARATOR = '/'; - - /* -- Normalization and construction -- */ - - /** filesystem prefix */ - public abstract String getPrefix(); - - /** Is the path of this filesystem? */ - public boolean isPathOfThisFilesystem(@NonNull String path) { - return path.startsWith(getPrefix()); - } - - /** Return the local filesystem's name-separator character. */ - public char getSeparator() { - return STANDARD_SEPARATOR; - } - - /** - * Convert the given pathname string to normal form. If the string is already in normal form then - * it is simply returned. - */ - @NonNull - public abstract String normalize(@NonNull String path); - - /** - * Compute the length of this pathname string's prefix. The pathname string must be in normal - * form. - */ - public int prefixLength(@NonNull String path) { - return getPrefix().length(); - } - - /** - * Resolve the child pathname string against the parent. Both strings must be in normal form, and - * the result will be in normal form. - */ - @NonNull - public abstract String resolve(String parent, String child); - - /** - * Return the parent pathname string to be used when the parent-directory argument in one of the - * two-argument File constructors is the empty pathname. - */ - @NonNull - public abstract String getDefaultParent(); - - /* -- Path operations -- */ - - /** Tell whether or not the given abstract pathname is absolute. */ - public abstract boolean isAbsolute(AmazeFile f); - - /** - * Resolve the given abstract pathname into absolute form. Invoked by the getAbsolutePath and - * getCanonicalPath methods in the {@link AmazeFile} class. - */ - @NonNull - public abstract String resolve(AmazeFile f); - - @NonNull - public abstract String canonicalize(String path) throws IOException; - - /* -- Attribute accessors -- */ - - /* Constants for simple boolean attributes */ - @Native public static final int BA_EXISTS = 0x01; - @Native public static final int BA_REGULAR = 0x02; - @Native public static final int BA_DIRECTORY = 0x04; - @Native public static final int BA_HIDDEN = 0x08; - - /** - * Return the simple boolean attributes for the file or directory denoted by the given abstract - * pathname, or zero if it does not exist or some other I/O error occurs. - */ - public int getBooleanAttributes(AmazeFile f, @NonNull ContextProvider contextProvider) { - File file = new File(f.getPath()); - int r = 0; - - if (exists(f, contextProvider)) { - r |= BA_EXISTS; - - if (isFile(f, contextProvider)) { - r |= BA_REGULAR; - } - - if (isDirectory(f, contextProvider)) { - r |= BA_DIRECTORY; - } - - if (isHidden(f)) { - r |= BA_HIDDEN; - } - } - - return r; - } - - public abstract boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider); - - public abstract boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider); - - public abstract boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider); - - public abstract boolean isHidden(AmazeFile f); - - /** - * Check whether the file or directory denoted by the given abstract pathname may be accessed by - * this process. Return false if access is denied or an I/O error occurs - */ - public abstract boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider); - - /** - * Check whether the file or directory denoted by the given abstract pathname may be accessed by - * this process. Return false if access is denied or an I/O error occurs - */ - public abstract boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider); - - /** - * Check whether the file or directory denoted by the given abstract pathname may be accessed by - * this process. Return false if access is denied or an I/O error occurs - */ - public abstract boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider); - - /** - * Check whether the file or directory denoted by the given abstract pathname may be accessed by - * this process. Return false if access is denied or an I/O error occurs - * - * Android-added: b/25878034, to support F.exists() reimplementation. - */ - public abstract boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider); - - /** - * Set on or off the access permission (to owner only or to all) to the file or directory denoted - * by the given abstract pathname, based on the parameters enable, access and oweronly. - */ - public abstract boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly); - - /** - * Set on or off the access permission (to owner only or to all) to the file or directory denoted - * by the given abstract pathname, based on the parameters enable, access and oweronly. - */ - public abstract boolean setWritable(AmazeFile f, boolean enable, boolean owneronly); - - /** - * Set on or off the access permission (to owner only or to all) to the file or directory denoted - * by the given abstract pathname, based on the parameters enable, access and oweronly. - */ - public abstract boolean setReadable(AmazeFile f, boolean enable, boolean owneronly); - - /** - * Return the time at which the file or directory denoted by the given abstract pathname was last - * modified, or zero if it does not exist or some other I/O error occurs. - */ - public abstract long getLastModifiedTime(AmazeFile f); - - /** - * Return the length in bytes of the file denoted by the given abstract pathname, or zero if it - * does not exist, or some other I/O error occurs. - * - *

Note: for directories, this *could* return the size - */ - public abstract long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) - throws IOException; - - /* -- File operations -- */ - - /** - * Create a new empty file with the given pathname. Return true if the file was - * created and false if a file or directory with the given pathname already exists. - * Throw an IOException if an I/O error occurs. - */ - public abstract boolean createFileExclusively(String pathname) throws IOException; - - /** - * Delete the file or directory denoted by the given abstract pathname, returning true - * if and only if the operation succeeds. - */ - public abstract boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider); - - /** - * List the elements of the directory denoted by the given abstract pathname. Return an array of - * strings naming the elements of the directory if successful; otherwise, return null - * . - */ - @Nullable - public abstract String[] list(AmazeFile f, @NonNull ContextProvider contextProvider); - - @Nullable - public abstract InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider); - - @Nullable - public abstract OutputStream getOutputStream( - AmazeFile f, @NonNull ContextProvider contextProvider); - - /** - * Create a new directory denoted by the given abstract pathname, returning true if - * and only if the operation succeeds. - */ - public abstract boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider); - - /** - * Rename the file or directory denoted by the first abstract pathname to the second abstract - * pathname, returning true if and only if the operation succeeds. - */ - public abstract boolean rename( - AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider); - - /** - * Set the last-modified time of the file or directory denoted by the given abstract pathname, - * returning true if and only if the operation succeeds. - */ - public abstract boolean setLastModifiedTime(AmazeFile f, long time); - - /** - * Mark the file or directory denoted by the given abstract pathname as read-only, returning - * true if and only if the operation succeeds. - */ - public abstract boolean setReadOnly(AmazeFile f); - - protected final String removePrefix(@NonNull String path) { - return path.substring(prefixLength(path)); - } - - /* -- Filesystem interface -- */ - - /** List the available filesystem roots. */ - public abstract AmazeFile[] listRoots(); - - public abstract long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider); - - public abstract long getFreeSpace(AmazeFile f); - - public abstract long getUsableSpace(AmazeFile f); - - /* -- Basic infrastructure -- */ - - /** Compare two abstract pathnames lexicographically. */ - public int compare(AmazeFile f1, AmazeFile f2) { - return f1.getPath().compareTo(f2.getPath()); - } - - /** Compute the hash code of an abstract pathname. */ - public int hashCode(AmazeFile f) { - return basicUnixHashCode(f.getPath()); - } - - // Flags for enabling/disabling performance optimizations for file - // name canonicalization - // Android-changed: Disabled caches for security reasons (b/62301183) - // static boolean useCanonCaches = true; - // static boolean useCanonPrefixCache = true; - static boolean useCanonCaches = false; - static boolean useCanonPrefixCache = false; - - private static boolean getBooleanProperty(String prop, boolean defaultVal) { - String val = System.getProperty(prop); - if (val == null) { - return defaultVal; - } - return val.equalsIgnoreCase("true"); - } - - static { - useCanonCaches = getBooleanProperty("sun.io.useCanonCaches", useCanonCaches); - useCanonPrefixCache = getBooleanProperty("sun.io.useCanonPrefixCache", useCanonPrefixCache); - } - - /* - * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - * A normal Unix pathname does not contain consecutive slashes and does not end - * with a slash. The empty string and "/" are special cases that are also - * considered normal. - */ - public static String simpleUnixNormalize(String pathname) { - int n = pathname.length(); - char[] normalized = pathname.toCharArray(); - int index = 0; - char prevChar = 0; - for (int i = 0; i < n; i++) { - char current = normalized[i]; - // Remove duplicate slashes. - if (!(current == '/' && prevChar == '/')) { - normalized[index++] = current; - } - - prevChar = current; - } - - // Omit the trailing slash, except when pathname == "/". - if (prevChar == '/' && n > 1) { - index--; - } - - return (index != n) ? new String(normalized, 0, index) : pathname; - } - - /* - * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - // Invariant: Both |parent| and |child| are normalized paths. - @NonNull - public static String basicUnixResolve(@NonNull String parent, @NonNull String child) { - if (child.isEmpty() || child.equals("/")) { - return parent; - } - - if (child.charAt(0) == '/') { - if (parent.equals("/")) { - return child; - } - return parent + child; - } - - if (parent.equals("/")) { - return parent + child; - } - return parent + '/' + child; - } - - public static int basicUnixHashCode(String path) { - return path.hashCode() ^ 1234321; - } -} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt new file mode 100644 index 0000000000..5459b9eb54 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2014-2014 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes + +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider +import java.io.File +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.lang.annotation.Native + +abstract class AmazeFilesystem { + /* -- Normalization and construction -- */ + /** filesystem prefix */ + abstract val prefix: String + + /** Is the path of this filesystem? */ + open fun isPathOfThisFilesystem(path: String): Boolean { + return path.startsWith(prefix) + } + + open fun getSeparator(): Char = STANDARD_SEPARATOR + + /** + * Convert the given pathname string to normal form. If the string is already in normal form then + * it is simply returned. + */ + abstract fun normalize(path: String): String + + /** + * Compute the length of this pathname string's prefix. The pathname string must be in normal + * form. + */ + open fun prefixLength(path: String): Int { + return prefix.length + } + + /** + * Resolve the child pathname string against the parent. Both strings must be in normal form, and + * the result will be in normal form. + */ + abstract fun resolve(parent: String?, child: String?): String + + /** + * Return the parent pathname string to be used when the parent-directory argument in one of the + * two-argument File constructors is the empty pathname. + */ + abstract val defaultParent: String + /* -- Path operations -- */ + /** Tell whether or not the given abstract pathname is absolute. */ + abstract fun isAbsolute(f: AmazeFile?): Boolean + + /** + * Resolve the given abstract pathname into absolute form. Invoked by the getAbsolutePath and + * getCanonicalPath methods in the [AmazeFile] class. + */ + abstract fun resolve(f: AmazeFile?): String + @Throws(IOException::class) + abstract fun canonicalize(path: String?): String + + /** + * Return the simple boolean attributes for the file or directory denoted by the given abstract + * pathname, or zero if it does not exist or some other I/O error occurs. + */ + fun getBooleanAttributes(f: AmazeFile, contextProvider: ContextProvider): Int { + val file = File(f.path) + var r = 0 + if (exists(f, contextProvider)) { + r = r or BA_EXISTS + if (isFile(f, contextProvider)) { + r = r or BA_REGULAR + } + if (isDirectory(f, contextProvider)) { + r = r or BA_DIRECTORY + } + if (isHidden(f)) { + r = r or BA_HIDDEN + } + } + return r + } + + abstract fun exists(f: AmazeFile?, contextProvider: ContextProvider): Boolean + abstract fun isFile(f: AmazeFile?, contextProvider: ContextProvider): Boolean + abstract fun isDirectory(f: AmazeFile?, contextProvider: ContextProvider): Boolean + abstract fun isHidden(f: AmazeFile?): Boolean + + /** + * Check whether the file or directory denoted by the given abstract pathname may be accessed by + * this process. Return false if access is denied or an I/O error occurs + */ + abstract fun canExecute(f: AmazeFile?, contextProvider: ContextProvider): Boolean + + /** + * Check whether the file or directory denoted by the given abstract pathname may be accessed by + * this process. Return false if access is denied or an I/O error occurs + */ + abstract fun canWrite(f: AmazeFile?, contextProvider: ContextProvider): Boolean + + /** + * Check whether the file or directory denoted by the given abstract pathname may be accessed by + * this process. Return false if access is denied or an I/O error occurs + */ + abstract fun canRead(f: AmazeFile?, contextProvider: ContextProvider): Boolean + + /** + * Check whether the file or directory denoted by the given abstract pathname may be accessed by + * this process. Return false if access is denied or an I/O error occurs + * + * Android-added: b/25878034, to support F.exists() reimplementation. + */ + abstract fun canAccess(f: AmazeFile?, contextProvider: ContextProvider): Boolean + + /** + * Set on or off the access permission (to owner only or to all) to the file or directory denoted + * by the given abstract pathname, based on the parameters enable, access and oweronly. + */ + abstract fun setExecutable(f: AmazeFile?, enable: Boolean, owneronly: Boolean): Boolean + + /** + * Set on or off the access permission (to owner only or to all) to the file or directory denoted + * by the given abstract pathname, based on the parameters enable, access and oweronly. + */ + abstract fun setWritable(f: AmazeFile?, enable: Boolean, owneronly: Boolean): Boolean + + /** + * Set on or off the access permission (to owner only or to all) to the file or directory denoted + * by the given abstract pathname, based on the parameters enable, access and oweronly. + */ + abstract fun setReadable(f: AmazeFile?, enable: Boolean, owneronly: Boolean): Boolean + + /** + * Return the time at which the file or directory denoted by the given abstract pathname was last + * modified, or zero if it does not exist or some other I/O error occurs. + */ + abstract fun getLastModifiedTime(f: AmazeFile?): Long + + /** + * Return the length in bytes of the file denoted by the given abstract pathname, or zero if it + * does not exist, or some other I/O error occurs. + * + * + * Note: for directories, this *could* return the size + */ + @Throws(IOException::class) + abstract fun getLength(f: AmazeFile?, contextProvider: ContextProvider): Long + /* -- File operations -- */ + /** + * Create a new empty file with the given pathname. Return `true` if the file was + * created and `false` if a file or directory with the given pathname already exists. + * Throw an IOException if an I/O error occurs. + */ + @Throws(IOException::class) + abstract fun createFileExclusively(pathname: String?): Boolean + + /** + * Delete the file or directory denoted by the given abstract pathname, returning `true + ` * if and only if the operation succeeds. + */ + abstract fun delete(f: AmazeFile?, contextProvider: ContextProvider): Boolean + + /** + * List the elements of the directory denoted by the given abstract pathname. Return an array of + * strings naming the elements of the directory if successful; otherwise, return `null` + * . + */ + abstract fun list(f: AmazeFile?, contextProvider: ContextProvider): Array? + abstract fun getInputStream(f: AmazeFile?, contextProvider: ContextProvider): InputStream? + abstract fun getOutputStream( + f: AmazeFile?, contextProvider: ContextProvider): OutputStream? + + /** + * Create a new directory denoted by the given abstract pathname, returning `true` if + * and only if the operation succeeds. + */ + abstract fun createDirectory(f: AmazeFile?, contextProvider: ContextProvider): Boolean + + /** + * Rename the file or directory denoted by the first abstract pathname to the second abstract + * pathname, returning `true` if and only if the operation succeeds. + */ + abstract fun rename( + f1: AmazeFile?, f2: AmazeFile?, contextProvider: ContextProvider): Boolean + + /** + * Set the last-modified time of the file or directory denoted by the given abstract pathname, + * returning `true` if and only if the operation succeeds. + */ + abstract fun setLastModifiedTime(f: AmazeFile?, time: Long): Boolean + + /** + * Mark the file or directory denoted by the given abstract pathname as read-only, returning + * `true` if and only if the operation succeeds. + */ + abstract fun setReadOnly(f: AmazeFile?): Boolean + protected fun removePrefix(path: String): String { + return path.substring(prefixLength(path)) + } + /* -- Filesystem interface -- */ + /** List the available filesystem roots. */ + abstract fun listRoots(): Array? + abstract fun getTotalSpace(f: AmazeFile?, contextProvider: ContextProvider): Long + abstract fun getFreeSpace(f: AmazeFile?): Long + abstract fun getUsableSpace(f: AmazeFile?): Long + /* -- Basic infrastructure -- */ + /** Compare two abstract pathnames lexicographically. */ + open fun compare(f1: AmazeFile, f2: AmazeFile): Int { + return f1.path.compareTo(f2.path) + } + + /** Compute the hash code of an abstract pathname. */ + open fun hashCode(f: AmazeFile): Int { + return basicUnixHashCode(f.path) + } + + companion object { + /** Return the local filesystem's name-separator character. */ + const val STANDARD_SEPARATOR = '/' + + /* -- Attribute accessors -- */ /* Constants for simple boolean attributes */ + @JvmField + @Native + val BA_EXISTS = 0x01 + + @JvmField + @Native + val BA_REGULAR = 0x02 + + @JvmField + @Native + val BA_DIRECTORY = 0x04 + + @JvmField + @Native + val BA_HIDDEN = 0x08 + + // Flags for enabling/disabling performance optimizations for file + // name canonicalization + // Android-changed: Disabled caches for security reasons (b/62301183) + // static boolean useCanonCaches = true; + // static boolean useCanonPrefixCache = true; + var useCanonCaches = false + var useCanonPrefixCache = false + private fun getBooleanProperty(prop: String, defaultVal: Boolean): Boolean { + val `val` = System.getProperty(prop) ?: return defaultVal + return `val`.equals("true", ignoreCase = true) + } + + /* + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + * A normal Unix pathname does not contain consecutive slashes and does not end + * with a slash. The empty string and "/" are special cases that are also + * considered normal. + */ + @JvmStatic + fun simpleUnixNormalize(pathname: String): String { + val n = pathname.length + val normalized = pathname.toCharArray() + var index = 0 + var prevChar = 0.toChar() + for (i in 0 until n) { + val current = normalized[i] + // Remove duplicate slashes. + if (!(current == '/' && prevChar == '/')) { + normalized[index++] = current + } + prevChar = current + } + + // Omit the trailing slash, except when pathname == "/". + if (prevChar == '/' && n > 1) { + index-- + } + return if (index != n) String(normalized, 0, index) else pathname + } + + /* + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + // Invariant: Both |parent| and |child| are normalized paths. + @JvmStatic + fun basicUnixResolve(parent: String, child: String): String { + if (child.isEmpty() || child == "/") { + return parent + } + if (child[0] == '/') { + return if (parent == "/") { + child + } else parent + child + } + return if (parent == "/") { + parent + child + } else "$parent/$child" + } + + fun basicUnixHashCode(path: String): Int { + return path.hashCode() xor 1234321 + } + + init { + useCanonCaches = getBooleanProperty("sun.io.useCanonCaches", useCanonCaches) + useCanonPrefixCache = getBooleanProperty("sun.io.useCanonPrefixCache", useCanonPrefixCache) + } + } +} \ No newline at end of file From dcde3ad9dbbda9a8d1882137c85217f891123338 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Wed, 26 Jan 2022 22:26:50 -0300 Subject: [PATCH 29/46] Converted most AmazeFilesystem subclasses to kt --- .../filesystem/otg/OtgAmazeFilesystem.java | 319 ------------- .../filesystem/otg/OtgAmazeFilesystem.kt | 215 +++++++++ .../filesystem/ssh/SshAmazeFilesystem.java | 5 +- .../filesystem/filetypes/AmazeFilesystem.kt | 58 ++- .../filetypes/cloud/CloudAmazeFilesystem.java | 290 ------------ .../filetypes/cloud/CloudAmazeFilesystem.kt | 237 ++++++++++ .../BoxAmazeFilesystem.kt} | 42 +- .../DropboxAmazeFilesystem.kt} | 54 +-- .../GoogledriveAmazeFilesystem.kt} | 54 +-- .../OnedriveAmazeFilesystem.kt} | 42 +- .../filetypes/file/FileAmazeFilesystem.java | 397 ---------------- .../filetypes/file/FileAmazeFilesystem.kt | 312 +++++++++++++ .../filetypes/smb/SmbAmazeFilesystem.java | 433 ------------------ .../filetypes/smb/SmbAmazeFilesystem.kt | 428 +++++++++++++++++ 14 files changed, 1308 insertions(+), 1578 deletions(-) delete mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt delete mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt rename file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/{gdrive/GoogledriveAmazeFilesystem.java => box/BoxAmazeFilesystem.kt} (63%) rename file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/{onedrive/OnedriveAmazeFilesystem.java => dropbox/DropboxAmazeFilesystem.kt} (62%) rename file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/{dropbox/DropboxAmazeFilesystem.java => gdrive/GoogledriveAmazeFilesystem.kt} (62%) rename file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/{box/BoxAmazeFilesystem.java => onedrive/OnedriveAmazeFilesystem.kt} (66%) delete mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt delete mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java deleted file mode 100644 index 50e347e483..0000000000 --- a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.java +++ /dev/null @@ -1,319 +0,0 @@ -package com.amaze.filemanager.filesystem.otg; - -import android.content.ContentResolver; -import android.content.Context; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.documentfile.provider.DocumentFile; - -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; -import com.amaze.filemanager.utils.OTGUtil; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; - -import kotlin.NotImplementedError; - -public class OtgAmazeFilesystem extends AmazeFilesystem { - public static final String TAG = OtgAmazeFilesystem.class.getSimpleName(); - - public static final OtgAmazeFilesystem INSTANCE = new OtgAmazeFilesystem(); - - public static final String PREFIX = "otg:/"; - - static { - AmazeFile.addFilesystem(INSTANCE); - } - - private OtgAmazeFilesystem() {} - - @Override - public String getPrefix() { - return PREFIX; - } - - @Override - public int prefixLength(@NonNull String path) { - if (path.length() == 0) { - throw new IllegalArgumentException( - "This should never happen, all paths must start with OTG prefix"); - } - - return super.prefixLength(path); - } - - @NonNull - @Override - public String normalize(@NonNull String path) { - return simpleUnixNormalize(path); - } - - @NonNull - @Override - public String resolve(String parent, String child) { - final String prefix = parent.substring(0, prefixLength(parent)); - final String simplePathParent = parent.substring(prefixLength(parent)); - final String simplePathChild = child.substring(prefixLength(child)); - - return prefix + basicUnixResolve(simplePathParent, simplePathChild); - } - - @NonNull - @Override - public String getDefaultParent() { - return PREFIX + getSeparator(); - } - - @Override - public boolean isAbsolute(AmazeFile f) { - return true; // Relative paths are not supported - } - - @NonNull - @Override - public String resolve(AmazeFile f) { - return f.getPath(); - } - - @NonNull - @Override - public String canonicalize(String path) throws IOException { - return normalize(path); - } - - public boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider) { - @Nullable - final Context context = contextProvider.getContext(); - if(context == null) { - return false; - } - - return OTGUtil.getDocumentFile(f.getPath(), context, false) != null; - } - public boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider) { - @Nullable - final Context context = contextProvider.getContext(); - if(context == null) { - return false; - } - - return OTGUtil.getDocumentFile(f.getPath(), context, false).isFile(); - } - public boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { - @Nullable - final Context context = contextProvider.getContext(); - if(context == null) { - return false; - } - - return OTGUtil.getDocumentFile(f.getPath(), context, false).isDirectory(); - } - public boolean isHidden(AmazeFile f) { - return false;// There doesn't seem to be hidden files for OTG - } - - public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider) { - throw new NotImplementedError(); // No way to check - } - public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider) { - @Nullable - final Context context = contextProvider.getContext(); - if(context == null) { - return false; - } - - return OTGUtil.getDocumentFile(f.getPath(), context, false).canWrite(); - } - public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider) { - @Nullable - final Context context = contextProvider.getContext(); - if(context == null) { - return false; - } - - return OTGUtil.getDocumentFile(f.getPath(), context, false).canRead(); - } - public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) { - return exists(f, contextProvider); //If the system says it exists, we can access it - } - - public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly) { - return false; //TODO find way to set - } - public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly) { - return false; //TODO find way to set - } - public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { - return false; //TODO find way to set - } - - @Override - public long getLastModifiedTime(AmazeFile f) { - return 0; - } - - @Override - public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException { - @Nullable - final Context context = contextProvider.getContext(); - - if(context == null) { - throw new IOException("Error obtaining context for OTG"); - } - - return OTGUtil.getDocumentFile(f.getPath(), context, false).length(); - } - - @Override - public boolean createFileExclusively(String pathname) throws IOException { - return false; - } - - @Override - public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { - @Nullable - final Context context = contextProvider.getContext(); - - if(context == null) { - return false; - } - - return OTGUtil.getDocumentFile(f.getPath(), context, false).delete(); - } - - @Nullable - @Override - public String[] list(AmazeFile f, @NonNull ContextProvider contextProvider) { - @Nullable final Context context = contextProvider.getContext(); - if(context == null) { - return null; - } - - ArrayList list = new ArrayList<>(); - DocumentFile rootUri = OTGUtil.getDocumentFile(f.getPath(), context, false); - for (DocumentFile file : rootUri.listFiles()) { - list.add(file.getUri().getPath()); - } - - return list.toArray(new String[0]); - } - - @Nullable - @Override - public InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { - @Nullable - final Context context = contextProvider.getContext(); - ContentResolver contentResolver = context.getContentResolver(); - DocumentFile documentSourceFile = OTGUtil.getDocumentFile(f.getPath(), context, false); - try { - return contentResolver.openInputStream(documentSourceFile.getUri()); - } catch (FileNotFoundException e) { - Log.e(TAG, "Error obtaining input stream for OTG", e); - return null; - } - } - - @Nullable - @Override - public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { - @Nullable - final Context context = contextProvider.getContext(); - ContentResolver contentResolver = context.getContentResolver(); - DocumentFile documentSourceFile = OTGUtil.getDocumentFile(f.getPath(), context, true); - try { - return contentResolver.openOutputStream(documentSourceFile.getUri()); - } catch (FileNotFoundException e) { - Log.e(TAG, "Error output stream for OTG", e); - return null; - } - } - - @Override - public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { - if(f.exists(contextProvider)) { - return true; - } - - @Nullable - final Context context = contextProvider.getContext(); - if(context == null) { - return false; - } - - final String parent = f.getParent(); - if(parent == null) { - return false; - } - - DocumentFile parentDirectory = OTGUtil.getDocumentFile(parent, context, true); - - - if (parentDirectory.isDirectory()) { - parentDirectory.createDirectory(f.getName()); - return true; - } - - return false; - } - - @Override - public boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider) { - @Nullable - final Context context = contextProvider.getContext(); - ContentResolver contentResolver = context.getContentResolver(); - DocumentFile documentSourceFile = OTGUtil.getDocumentFile(f1.getPath(), context, true); - return documentSourceFile.renameTo(f2.getPath()); - } - - @Override - public boolean setLastModifiedTime(AmazeFile f, long time) { - throw new NotImplementedError(); - } - - @Override - public boolean setReadOnly(AmazeFile f) { - return false; - } - - @Override - public AmazeFile[] listRoots() { - File[] roots = File.listRoots(); - AmazeFile[] amazeRoots = new AmazeFile[roots.length]; - - for (int i = 0; i < roots.length; i++) { - amazeRoots[i] = new AmazeFile(roots[i].getPath()); - } - - return new AmazeFile[] { new AmazeFile(getDefaultParent()) }; - } - - @Override - public long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider) { - @Nullable - final Context context = contextProvider.getContext(); - // TODO: Find total storage space of OTG when {@link DocumentFile} API adds support - DocumentFile documentFile = OTGUtil.getDocumentFile(f.getPath(), context, false); - return documentFile.length(); - } - - @Override - public long getFreeSpace(AmazeFile f) { - return 0; //TODO - } - - @Override - public long getUsableSpace(AmazeFile f) { - // TODO: Get free space from OTG when {@link DocumentFile} API adds support - return 0; - } - - -} diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt new file mode 100644 index 0000000000..4a9d2aaa64 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt @@ -0,0 +1,215 @@ +package com.amaze.filemanager.filesystem.otg + +import android.util.Log +import com.amaze.filemanager.utils.OTGUtil.getDocumentFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider +import java.io.* +import java.util.ArrayList + +class OtgAmazeFilesystem private constructor() : AmazeFilesystem() { + companion object { + val TAG = OtgAmazeFilesystem::class.java.simpleName + + val INSTANCE = OtgAmazeFilesystem() + + const val PREFIX = "otg:/" + + init { + AmazeFile.addFilesystem(INSTANCE) + } + } + + override val prefix = PREFIX + + override fun prefixLength(path: String): Int { + require(path.length != 0) { "This should never happen, all paths must start with OTG prefix" } + return super.prefixLength(path) + } + + override fun normalize(path: String): String { + return simpleUnixNormalize(path) + } + + override fun resolve(parent: String?, child: String?): String { + val prefix = parent!!.substring(0, prefixLength(parent)) + val simplePathParent = parent.substring(prefixLength(parent)) + val simplePathChild = child!!.substring(prefixLength(child)) + return prefix + basicUnixResolve(simplePathParent, simplePathChild) + } + + override val defaultParent: String + get() = prefix + getSeparator() + + override fun isAbsolute(f: AmazeFile): Boolean { + return true // Relative paths are not supported + } + + override fun resolve(f: AmazeFile): String { + return f.path + } + + @Throws(IOException::class) + override fun canonicalize(path: String?): String { + return normalize(path!!) + } + + override fun exists(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val context = contextProvider.getContext() ?: return false + return getDocumentFile(f.path, context, false) != null + } + + override fun isFile(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val context = contextProvider.getContext() ?: return false + return getDocumentFile(f.path, context, false)!!.isFile + } + + override fun isDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val context = contextProvider.getContext() ?: return false + return getDocumentFile(f.path, context, false)!!.isDirectory + } + + override fun isHidden(f: AmazeFile): Boolean { + return false // There doesn't seem to be hidden files for OTG + } + + override fun canExecute(f: AmazeFile, contextProvider: ContextProvider): Boolean { + throw NotImplementedError() // No way to check + } + + override fun canWrite(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val context = contextProvider.getContext() ?: return false + return getDocumentFile(f.path, context, false)!!.canWrite() + } + + override fun canRead(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val context = contextProvider.getContext() ?: return false + return getDocumentFile(f.path, context, false)!!.canRead() + } + + override fun canAccess(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return exists(f, contextProvider) //If the system says it exists, we can access it + } + + override fun setExecutable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + return false //TODO find way to set + } + + override fun setWritable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + return false //TODO find way to set + } + + override fun setReadable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + return false //TODO find way to set + } + + override fun getLastModifiedTime(f: AmazeFile): Long { + return 0 + } + + @Throws(IOException::class) + override fun getLength(f: AmazeFile, contextProvider: ContextProvider): Long { + val context = contextProvider.getContext() + ?: throw IOException("Error obtaining context for OTG") + return getDocumentFile(f.path, context, false)!!.length() + } + + @Throws(IOException::class) + override fun createFileExclusively(pathname: String?): Boolean { + return false + } + + override fun delete(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val context = contextProvider.getContext() ?: return false + return getDocumentFile(f.path, context, false)!!.delete() + } + + override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { + val context = contextProvider.getContext() ?: return null + val list = ArrayList() + val rootUri = getDocumentFile(f.path, context, false) + for (file in rootUri!!.listFiles()) { + list.add(file.uri.path) + } + return list.toTypedArray() + } + + override fun getInputStream(f: AmazeFile, contextProvider: ContextProvider): InputStream? { + val context = contextProvider.getContext() + val contentResolver = context!!.contentResolver + val documentSourceFile = getDocumentFile(f.path, context, false) + return try { + contentResolver.openInputStream(documentSourceFile!!.uri) + } catch (e: FileNotFoundException) { + Log.e(TAG, "Error obtaining input stream for OTG", e) + null + } + } + + override fun getOutputStream(f: AmazeFile, contextProvider: ContextProvider): OutputStream? { + val context = contextProvider.getContext() + val contentResolver = context!!.contentResolver + val documentSourceFile = getDocumentFile(f.path, context, true) + return try { + contentResolver.openOutputStream(documentSourceFile!!.uri) + } catch (e: FileNotFoundException) { + Log.e(TAG, "Error output stream for OTG", e) + null + } + } + + override fun createDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { + if (f.exists(contextProvider)) { + return true + } + val context = contextProvider.getContext() ?: return false + val parent = f.parent ?: return false + val parentDirectory = getDocumentFile(parent, context, true) + if (parentDirectory!!.isDirectory) { + parentDirectory.createDirectory(f.name) + return true + } + return false + } + + override fun rename(f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean { + val context = contextProvider.getContext() + val contentResolver = context!!.contentResolver + val documentSourceFile = getDocumentFile(f1.path, context, true) + return documentSourceFile!!.renameTo(f2.path) + } + + override fun setLastModifiedTime(f: AmazeFile, time: Long): Boolean { + throw NotImplementedError() + } + + override fun setReadOnly(f: AmazeFile): Boolean { + return false + } + + override fun listRoots(): Array { + val roots = File.listRoots() + val amazeRoots = arrayOfNulls(roots.size) + for (i in roots.indices) { + amazeRoots[i] = AmazeFile(roots[i].path) + } + return arrayOf(AmazeFile(defaultParent)) + } + + override fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long { + val context = contextProvider.getContext() + // TODO: Find total storage space of OTG when {@link DocumentFile} API adds support + val documentFile = getDocumentFile(f.path, context!!, false) + return documentFile!!.length() + } + + override fun getFreeSpace(f: AmazeFile): Long { + return 0 //TODO + } + + override fun getUsableSpace(f: AmazeFile): Long { + // TODO: Get free space from OTG when {@link DocumentFile} API adds support + return 0 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java index 4603ee0208..07a37d9bab 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -19,6 +19,8 @@ import net.schmizz.sshj.sftp.SFTPClient; import net.schmizz.sshj.sftp.SFTPException; +import org.jetbrains.annotations.NotNull; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -67,7 +69,7 @@ public String getDefaultParent() { } @Override - public boolean isAbsolute(AmazeFile f) { + public boolean isAbsolute(@NotNull AmazeFile f) { return true; // Always absolute } @@ -391,6 +393,7 @@ public boolean setReadOnly(AmazeFile f) { return false; } + @NotNull @Override public AmazeFile[] listRoots() { return new AmazeFile[] { new AmazeFile(getDefaultParent()) }; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt index 5459b9eb54..675937025a 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt @@ -19,9 +19,6 @@ */ package com.amaze.filemanager.file_operations.filesystem.filetypes -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile -import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider import java.io.File import java.io.IOException import java.io.InputStream @@ -67,13 +64,14 @@ abstract class AmazeFilesystem { abstract val defaultParent: String /* -- Path operations -- */ /** Tell whether or not the given abstract pathname is absolute. */ - abstract fun isAbsolute(f: AmazeFile?): Boolean + abstract fun isAbsolute(f: AmazeFile): Boolean /** * Resolve the given abstract pathname into absolute form. Invoked by the getAbsolutePath and * getCanonicalPath methods in the [AmazeFile] class. */ - abstract fun resolve(f: AmazeFile?): String + abstract fun resolve(f: AmazeFile): String + @Throws(IOException::class) abstract fun canonicalize(path: String?): String @@ -99,28 +97,28 @@ abstract class AmazeFilesystem { return r } - abstract fun exists(f: AmazeFile?, contextProvider: ContextProvider): Boolean - abstract fun isFile(f: AmazeFile?, contextProvider: ContextProvider): Boolean - abstract fun isDirectory(f: AmazeFile?, contextProvider: ContextProvider): Boolean - abstract fun isHidden(f: AmazeFile?): Boolean + abstract fun exists(f: AmazeFile, contextProvider: ContextProvider): Boolean + abstract fun isFile(f: AmazeFile, contextProvider: ContextProvider): Boolean + abstract fun isDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean + abstract fun isHidden(f: AmazeFile): Boolean /** * Check whether the file or directory denoted by the given abstract pathname may be accessed by * this process. Return false if access is denied or an I/O error occurs */ - abstract fun canExecute(f: AmazeFile?, contextProvider: ContextProvider): Boolean + abstract fun canExecute(f: AmazeFile, contextProvider: ContextProvider): Boolean /** * Check whether the file or directory denoted by the given abstract pathname may be accessed by * this process. Return false if access is denied or an I/O error occurs */ - abstract fun canWrite(f: AmazeFile?, contextProvider: ContextProvider): Boolean + abstract fun canWrite(f: AmazeFile, contextProvider: ContextProvider): Boolean /** * Check whether the file or directory denoted by the given abstract pathname may be accessed by * this process. Return false if access is denied or an I/O error occurs */ - abstract fun canRead(f: AmazeFile?, contextProvider: ContextProvider): Boolean + abstract fun canRead(f: AmazeFile, contextProvider: ContextProvider): Boolean /** * Check whether the file or directory denoted by the given abstract pathname may be accessed by @@ -128,31 +126,31 @@ abstract class AmazeFilesystem { * * Android-added: b/25878034, to support F.exists() reimplementation. */ - abstract fun canAccess(f: AmazeFile?, contextProvider: ContextProvider): Boolean + abstract fun canAccess(f: AmazeFile, contextProvider: ContextProvider): Boolean /** * Set on or off the access permission (to owner only or to all) to the file or directory denoted * by the given abstract pathname, based on the parameters enable, access and oweronly. */ - abstract fun setExecutable(f: AmazeFile?, enable: Boolean, owneronly: Boolean): Boolean + abstract fun setExecutable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean /** * Set on or off the access permission (to owner only or to all) to the file or directory denoted * by the given abstract pathname, based on the parameters enable, access and oweronly. */ - abstract fun setWritable(f: AmazeFile?, enable: Boolean, owneronly: Boolean): Boolean + abstract fun setWritable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean /** * Set on or off the access permission (to owner only or to all) to the file or directory denoted * by the given abstract pathname, based on the parameters enable, access and oweronly. */ - abstract fun setReadable(f: AmazeFile?, enable: Boolean, owneronly: Boolean): Boolean + abstract fun setReadable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean /** * Return the time at which the file or directory denoted by the given abstract pathname was last * modified, or zero if it does not exist or some other I/O error occurs. */ - abstract fun getLastModifiedTime(f: AmazeFile?): Long + abstract fun getLastModifiedTime(f: AmazeFile): Long /** * Return the length in bytes of the file denoted by the given abstract pathname, or zero if it @@ -162,7 +160,7 @@ abstract class AmazeFilesystem { * Note: for directories, this *could* return the size */ @Throws(IOException::class) - abstract fun getLength(f: AmazeFile?, contextProvider: ContextProvider): Long + abstract fun getLength(f: AmazeFile, contextProvider: ContextProvider): Long /* -- File operations -- */ /** * Create a new empty file with the given pathname. Return `true` if the file was @@ -176,51 +174,51 @@ abstract class AmazeFilesystem { * Delete the file or directory denoted by the given abstract pathname, returning `true ` * if and only if the operation succeeds. */ - abstract fun delete(f: AmazeFile?, contextProvider: ContextProvider): Boolean + abstract fun delete(f: AmazeFile, contextProvider: ContextProvider): Boolean /** * List the elements of the directory denoted by the given abstract pathname. Return an array of * strings naming the elements of the directory if successful; otherwise, return `null` * . */ - abstract fun list(f: AmazeFile?, contextProvider: ContextProvider): Array? - abstract fun getInputStream(f: AmazeFile?, contextProvider: ContextProvider): InputStream? + abstract fun list(f: AmazeFile, contextProvider: ContextProvider): Array? + abstract fun getInputStream(f: AmazeFile, contextProvider: ContextProvider): InputStream? abstract fun getOutputStream( - f: AmazeFile?, contextProvider: ContextProvider): OutputStream? + f: AmazeFile, contextProvider: ContextProvider): OutputStream? /** * Create a new directory denoted by the given abstract pathname, returning `true` if * and only if the operation succeeds. */ - abstract fun createDirectory(f: AmazeFile?, contextProvider: ContextProvider): Boolean + abstract fun createDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean /** * Rename the file or directory denoted by the first abstract pathname to the second abstract * pathname, returning `true` if and only if the operation succeeds. */ abstract fun rename( - f1: AmazeFile?, f2: AmazeFile?, contextProvider: ContextProvider): Boolean + f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean /** * Set the last-modified time of the file or directory denoted by the given abstract pathname, * returning `true` if and only if the operation succeeds. */ - abstract fun setLastModifiedTime(f: AmazeFile?, time: Long): Boolean + abstract fun setLastModifiedTime(f: AmazeFile, time: Long): Boolean /** * Mark the file or directory denoted by the given abstract pathname as read-only, returning * `true` if and only if the operation succeeds. */ - abstract fun setReadOnly(f: AmazeFile?): Boolean + abstract fun setReadOnly(f: AmazeFile): Boolean protected fun removePrefix(path: String): String { return path.substring(prefixLength(path)) } /* -- Filesystem interface -- */ /** List the available filesystem roots. */ - abstract fun listRoots(): Array? - abstract fun getTotalSpace(f: AmazeFile?, contextProvider: ContextProvider): Long - abstract fun getFreeSpace(f: AmazeFile?): Long - abstract fun getUsableSpace(f: AmazeFile?): Long + abstract fun listRoots(): Array + abstract fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long + abstract fun getFreeSpace(f: AmazeFile): Long + abstract fun getUsableSpace(f: AmazeFile): Long /* -- Basic infrastructure -- */ /** Compare two abstract pathnames lexicographically. */ open fun compare(f1: AmazeFile, f2: AmazeFile): Int { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java deleted file mode 100644 index 072edf1139..0000000000 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.cloud; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.List; -import java.util.Objects; - -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; -import com.cloudrail.si.interfaces.CloudStorage; -import com.cloudrail.si.types.CloudMetaData; -import com.cloudrail.si.types.SpaceAllocation; - -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import kotlin.NotImplementedError; - -public abstract class CloudAmazeFilesystem extends AmazeFilesystem { - public static final String TAG = CloudAmazeFilesystem.class.getSimpleName(); - - public abstract Account getAccount(); - - @Override - public int prefixLength(@NonNull String path) { - if (path.length() == 0) { - throw new IllegalArgumentException( - "This should never happen, all paths must start with OTG prefix"); - } - - return super.prefixLength(path); - } - - @NonNull - @Override - public String normalize(@NonNull String path) { - String canonical; - try { - canonical = canonicalize(path); - } catch (IOException e) { - Log.e(TAG, "Error getting Dropbox file canonical path", e); - canonical = path + "/"; - } - return canonical.substring(0, canonical.length() - 1); - } - - @NonNull - @Override - public String resolve(String parent, String child) { - return getPrefix() + new File(removePrefix(parent), child); - } - - @NonNull - @Override - public String getDefaultParent() { - return getPrefix() + "/"; - } - - @Override - public boolean isAbsolute(AmazeFile f) { - return true; // We don't accept relative paths for cloud - } - - @NonNull - @Override - public String resolve(AmazeFile f) { - if (isAbsolute(f)) { - return f.getPath(); - } - - throw new IllegalArgumentException("Relative paths are not supported"); - } - - @NonNull - @Override - public String canonicalize(String path) throws IOException { - return getPrefix() + new File(removePrefix(path)).getCanonicalPath(); - } - - public boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - final String noPrefixPath = removePrefix(f.getPath()); - final CloudMetaData metadata = getAccount().getAccount().getMetadata(noPrefixPath); - return account.exists(noPrefixPath); - } - - public boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider) { - return true; // all files are regular (probably) - } - - public boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - final String noPrefixPath = removePrefix(f.getPath()); - final CloudMetaData metadata = getAccount().getAccount().getMetadata(noPrefixPath); - return metadata.getFolder(); - } - - public boolean isHidden(AmazeFile f) { - return false; // No way to know if its hidden - } - - public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider) { - return false; // You aren't executing anything at the cloud - } - - public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider) { - return true; // Probably, can't check - } - - public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider) { - return true; // Probably, can't check - } - - public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - final String noPrefixPath = removePrefix(f.getPath()); - - return account.exists(noPrefixPath); - } - - @Override - public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly) { - throw new NotImplementedError(); - } - - @Override - public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly) { - throw new NotImplementedError(); - } - - @Override - public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { - throw new NotImplementedError(); - } - - @Override - public long getLastModifiedTime(AmazeFile f) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - final String noPrefixPath = removePrefix(f.getPath()); - // TODO check that this actually returns seconds since epoch - return account.getMetadata(noPrefixPath).getContentModifiedAt(); - } - - @Override - public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException { - if (f.isDirectory(contextProvider)) { - return 0; - } - - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - final String noPrefixPath = removePrefix(f.getPath()); - - return account.getMetadata(noPrefixPath).getSize(); - } - - @Override - public boolean createFileExclusively(String pathname) throws IOException { - return false; - } - - @Override - public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - final String noPrefixPath = removePrefix(f.getPath()); - account.delete(noPrefixPath); - return true; // This seems to never fail - } - - @Nullable - @Override - public String[] list(AmazeFile f, @NonNull ContextProvider contextProvider) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - final String noPrefixPath = removePrefix(f.getPath()); - final List metadatas = account.getChildren(noPrefixPath); - - String[] list = new String[metadatas.size()]; - for (int i = 0; i < list.length; i++) { - list[i] = normalize(getPrefix() + metadatas.get(i).getPath()); - } - return list; - } - - @Nullable - @Override - public InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - final String noPrefixPath = removePrefix(f.getPath()); - return account.download(noPrefixPath); - } - - @Nullable - @Override - public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { - throw new NotImplementedError(); - } - - @Override - public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - final String noPrefixPath = removePrefix(f.getPath()); - account.createFolder(noPrefixPath); - return true; // This seems to never fail - } - - @Override - public boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - account.move(removePrefix(f1.getPath()), removePrefix(f2.getPath())); - return true; // This seems to never fail - } - - @Override - public boolean setLastModifiedTime(AmazeFile f, long time) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - final String noPrefixPath = removePrefix(f.getPath()); - // TODO check that this actually returns seconds since epoch - account.getMetadata(noPrefixPath).setContentModifiedAt(time); - return true; // This seems to never fail - } - - @Override - public boolean setReadOnly(AmazeFile f) { - return false; // This doesn't seem possible - } - - @Override - public AmazeFile[] listRoots() { - return new AmazeFile[] {new AmazeFile(getPrefix() + "/")}; - } - - @Override - public long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - SpaceAllocation spaceAllocation = account.getAllocation(); - return spaceAllocation.getTotal(); - } - - @Override - public long getFreeSpace(AmazeFile f) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - SpaceAllocation spaceAllocation = account.getAllocation(); - return spaceAllocation.getTotal() - spaceAllocation.getUsed(); - } - - @Override - public long getUsableSpace(AmazeFile f) { - final CloudStorage account = getAccount().getAccount(); - Objects.requireNonNull(account); - SpaceAllocation spaceAllocation = account.getAllocation(); - return spaceAllocation.getTotal() - spaceAllocation.getUsed(); - } -} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt new file mode 100644 index 0000000000..d4b24babba --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.cloud + +import android.util.Log +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider +import com.cloudrail.si.types.CloudMetaData +import java.io.File +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.lang.IllegalArgumentException +import java.util.* + +abstract class CloudAmazeFilesystem : AmazeFilesystem() { + abstract val account: Account + override fun prefixLength(path: String): Int { + require(path.length != 0) { "This should never happen, all paths must start with OTG prefix" } + return super.prefixLength(path) + } + + override fun normalize(path: String): String { + val canonical: String + canonical = try { + canonicalize(path) + } catch (e: IOException) { + Log.e(TAG, "Error getting Dropbox file canonical path", e) + "$path/" + } + return canonical.substring(0, canonical.length - 1) + } + + override fun resolve(parent: String?, child: String?): String { + return prefix + File(removePrefix(parent!!), child) + } + + override val defaultParent: String + get() = "$prefix/" + + override fun isAbsolute(f: AmazeFile): Boolean { + return true // We don't accept relative paths for cloud + } + + override fun resolve(f: AmazeFile): String { + if (isAbsolute(f)) { + return f.path + } + throw IllegalArgumentException("Relative paths are not supported") + } + + @Throws(IOException::class) + override fun canonicalize(path: String?): String { + return prefix + File(removePrefix(path!!)).canonicalPath + } + + override fun exists(f: AmazeFile, contextProvider: ContextProvider): Boolean { + Objects.requireNonNull(account) + val noPrefixPath = removePrefix(f.path) + return account.account?.exists(noPrefixPath) ?: false + } + + override fun isFile(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return true // all files are regular (probably) + } + + override fun isDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val noPrefixPath = removePrefix(f.path) + val metadata: CloudMetaData? = account.account?.getMetadata(noPrefixPath) + return metadata?.folder ?: false + } + + override fun isHidden(f: AmazeFile): Boolean { + return false // No way to know if its hidden + } + + override fun canExecute(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return false // You aren't executing anything at the cloud + } + + override fun canWrite(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return true // Probably, can't check + } + + override fun canRead(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return true // Probably, can't check + } + + override fun canAccess(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val account = account.account + Objects.requireNonNull(account) + val noPrefixPath = removePrefix(f.path) + return account!!.exists(noPrefixPath) + } + + override fun setExecutable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + throw NotImplementedError() + } + + override fun setWritable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + throw NotImplementedError() + } + + override fun setReadable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + throw NotImplementedError() + } + + override fun getLastModifiedTime(f: AmazeFile): Long { + val account = account.account + Objects.requireNonNull(account) + val noPrefixPath = removePrefix(f.path) + // TODO check that this actually returns seconds since epoch + return account!!.getMetadata(noPrefixPath).contentModifiedAt + } + + @Throws(IOException::class) + override fun getLength(f: AmazeFile, contextProvider: ContextProvider): Long { + if (f.isDirectory(contextProvider)) { + return 0 + } + val account = account.account + Objects.requireNonNull(account) + val noPrefixPath = removePrefix(f.path) + return account!!.getMetadata(noPrefixPath).size.toLong() + } + + @Throws(IOException::class) + override fun createFileExclusively(pathname: String?): Boolean { + return false + } + + override fun delete(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val account = account.account + Objects.requireNonNull(account) + val noPrefixPath = removePrefix(f.path) + account!!.delete(noPrefixPath) + return true // This seems to never fail + } + + override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { + val account = account.account + Objects.requireNonNull(account) + val noPrefixPath = removePrefix(f.path) + val metadatas = account!!.getChildren(noPrefixPath) + val list = arrayOfNulls(metadatas.size) + for (i in list.indices) { + list[i] = normalize(prefix + metadatas[i].path) + } + return list + } + + override fun getInputStream(f: AmazeFile, contextProvider: ContextProvider): InputStream? { + val account = account.account + Objects.requireNonNull(account) + val noPrefixPath = removePrefix(f.path) + return account!!.download(noPrefixPath) + } + + override fun getOutputStream(f: AmazeFile, contextProvider: ContextProvider): OutputStream? { + throw NotImplementedError() + } + + override fun createDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val account = account.account + Objects.requireNonNull(account) + val noPrefixPath = removePrefix(f.path) + account!!.createFolder(noPrefixPath) + return true // This seems to never fail + } + + override fun rename(f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean { + val account = account.account + Objects.requireNonNull(account) + account!!.move(removePrefix(f1.path), removePrefix(f2.path)) + return true // This seems to never fail + } + + override fun setLastModifiedTime(f: AmazeFile, time: Long): Boolean { + val account = account.account + Objects.requireNonNull(account) + val noPrefixPath = removePrefix(f.path) + // TODO check that this actually returns seconds since epoch + account!!.getMetadata(noPrefixPath).contentModifiedAt = time + return true // This seems to never fail + } + + override fun setReadOnly(f: AmazeFile): Boolean { + return false // This doesn't seem possible + } + + override fun listRoots(): Array { + return arrayOf(AmazeFile("$prefix/")) + } + + override fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long { + val account = account.account + Objects.requireNonNull(account) + val spaceAllocation = account!!.allocation + return spaceAllocation.total + } + + override fun getFreeSpace(f: AmazeFile): Long { + val account = account.account + Objects.requireNonNull(account) + val spaceAllocation = account!!.allocation + return spaceAllocation.total - spaceAllocation.used + } + + override fun getUsableSpace(f: AmazeFile): Long { + val account = account.account + Objects.requireNonNull(account) + val spaceAllocation = account!!.allocation + return spaceAllocation.total - spaceAllocation.used + } + + companion object { + val TAG = CloudAmazeFilesystem::class.java.simpleName + } +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.kt similarity index 63% rename from file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java rename to file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.kt index 0402bee47a..a36a0f42c4 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.kt @@ -17,33 +17,29 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box -package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAccount -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; +class BoxAmazeFilesystem private constructor() : CloudAmazeFilesystem() { + companion object { + val TAG = BoxAmazeFilesystem::class.java.simpleName -public final class GoogledriveAmazeFilesystem extends CloudAmazeFilesystem { - public static final String TAG = GoogledriveAmazeFilesystem.class.getSimpleName(); + const val PREFIX = "box:/" - public static final String PREFIX = "gdrive:/"; + val INSTANCE = BoxAmazeFilesystem() - public static final GoogledriveAmazeFilesystem INSTANCE = new GoogledriveAmazeFilesystem(); + init { + AmazeFile.addFilesystem(INSTANCE) + } + } - static { - AmazeFile.addFilesystem(INSTANCE); - } + override val prefix: String = PREFIX - private GoogledriveAmazeFilesystem() {} - - @Override - public String getPrefix() { - return PREFIX; - } - - @Override - public Account getAccount() { - return GoogledriveAccount.INSTANCE; - } -} + override val account: Account + get() = BoxAccount +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.kt similarity index 62% rename from file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java rename to file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.kt index 9819fba82a..bbf547d0e8 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.kt @@ -17,33 +17,27 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - -package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive; - -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; - -public final class OnedriveAmazeFilesystem extends CloudAmazeFilesystem { - public static final String TAG = OnedriveAmazeFilesystem.class.getSimpleName(); - - public static final String PREFIX = "onedrive:/"; - - public static final OnedriveAmazeFilesystem INSTANCE = new OnedriveAmazeFilesystem(); - - static { - AmazeFile.addFilesystem(INSTANCE); - } - - private OnedriveAmazeFilesystem() {} - - @Override - public String getPrefix() { - return PREFIX; - } - - @Override - public Account getAccount() { - return OnedriveAccount.INSTANCE; - } -} +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox + +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount + +class DropboxAmazeFilesystem private constructor() : CloudAmazeFilesystem() { + companion object { + val TAG = DropboxAmazeFilesystem::class.java.simpleName + const val PREFIX = "dropbox:/" + val INSTANCE = DropboxAmazeFilesystem() + + init { + AmazeFile.addFilesystem(INSTANCE) + } + } + + override val prefix: String = PREFIX + + override val account: Account + get() = DropboxAccount +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.kt similarity index 62% rename from file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java rename to file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.kt index 12615ac9c2..fdb2c0da32 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.kt @@ -17,33 +17,27 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - -package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox; - -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; - -public final class DropboxAmazeFilesystem extends CloudAmazeFilesystem { - public static final String TAG = DropboxAmazeFilesystem.class.getSimpleName(); - - public static final String PREFIX = "dropbox:/"; - - public static final DropboxAmazeFilesystem INSTANCE = new DropboxAmazeFilesystem(); - - static { - AmazeFile.addFilesystem(INSTANCE); - } - - private DropboxAmazeFilesystem() {} - - @Override - public String getPrefix() { - return PREFIX; - } - - @Override - public Account getAccount() { - return DropboxAccount.INSTANCE; - } -} +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive + +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount + +class GoogledriveAmazeFilesystem private constructor() : CloudAmazeFilesystem() { + companion object { + val TAG = GoogledriveAmazeFilesystem::class.java.simpleName + const val PREFIX = "gdrive:/" + val INSTANCE = GoogledriveAmazeFilesystem() + + init { + AmazeFile.addFilesystem(INSTANCE) + } + } + + override val prefix = PREFIX + + override val account: Account + get() = GoogledriveAccount +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.kt similarity index 66% rename from file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java rename to file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.kt index 18329c6aad..5c8f0453dd 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.java +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.kt @@ -17,33 +17,25 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive -package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box; +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem; +class OnedriveAmazeFilesystem private constructor() : CloudAmazeFilesystem() { + companion object { + val TAG = OnedriveAmazeFilesystem::class.java.simpleName + const val PREFIX = "onedrive:/" + val INSTANCE = OnedriveAmazeFilesystem() -public final class BoxAmazeFilesystem extends CloudAmazeFilesystem { - public static final String TAG = BoxAmazeFilesystem.class.getSimpleName(); + init { + AmazeFile.addFilesystem(INSTANCE) + } + } - public static final String PREFIX = "box:/"; + override val prefix: String = PREFIX - public static final BoxAmazeFilesystem INSTANCE = new BoxAmazeFilesystem(); - - static { - AmazeFile.addFilesystem(INSTANCE); - } - - private BoxAmazeFilesystem() {} - - @Override - public String getPrefix() { - return PREFIX; - } - - @Override - public Account getAccount() { - return BoxAccount.INSTANCE; - } -} + override val account: Account + get() = OnedriveAccount +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java deleted file mode 100644 index 2ea17d1a26..0000000000 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.java +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.file; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.net.Uri; -import android.os.Build; -import android.provider.MediaStore; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.documentfile.provider.DocumentFile; - -/** - * This is in in essence calls to UnixFilesystem, but that class is not public so all calls must go - * through java.io.File - */ -public class FileAmazeFilesystem extends AmazeFilesystem { - public static final FileAmazeFilesystem INSTANCE = new FileAmazeFilesystem(); - - public static final String TAG = FileAmazeFilesystem.class.getSimpleName(); - - static { - AmazeFile.addFilesystem(INSTANCE); - } - - private FileAmazeFilesystem() {} - - @Override - public boolean isPathOfThisFilesystem(@NonNull String path) { - return path.charAt(0) == getSeparator(); - } - - @Override - public String getPrefix() { - return null; - } - - @Override - public char getSeparator() { - return File.separatorChar; - } - - @NonNull - @Override - public String normalize(@NonNull String path) { - return new File(path).getPath(); - } - - @Override - public int prefixLength(@NonNull String path) { - if (path.length() == 0) return 0; - return (path.charAt(0) == '/') ? 1 : 0; - } - - @NonNull - @Override - public String resolve(String parent, String child) { - return new File(parent, child).getPath(); - } - - @NonNull - @Override - public String getDefaultParent() { - return new File(new File(""), "").getPath(); - } - - @Override - public boolean isAbsolute(AmazeFile f) { - return new File(f.getPath()).isAbsolute(); - } - - @NonNull - @Override - public String resolve(AmazeFile f) { - return new File(f.getPath()).getAbsolutePath(); - } - - @NonNull - @Override - public String canonicalize(String path) throws IOException { - return new File(path).getCanonicalPath(); - } - - public boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider) { - return f.exists(contextProvider); - } - - public boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider) { - return new File(f.getPath()).isFile(); - } - - public boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { - return new File(f.getPath()).isDirectory(); - } - - public boolean isHidden(AmazeFile f) { - return new File(f.getPath()).isHidden(); - } - - public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider) { - return new File(f.getPath()).canExecute(); - } - - public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider) { - return new File(f.getPath()).canWrite(); - } - - public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider) { - return new File(f.getPath()).canRead(); - } - - public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) { - return new File(f.getPath()).exists(); - } - - public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly) { - return new File(f.getPath()).setExecutable(enable, owneronly); - } - - public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly) { - return new File(f.getPath()).setWritable(enable, owneronly); - } - - public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { - return new File(f.getPath()).setReadable(enable, owneronly); - } - - @Override - public long getLastModifiedTime(AmazeFile f) { - return new File(f.getPath()).lastModified(); - } - - @Override - public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException { - return new File(f.getPath()).length(); - } - - @Override - public boolean createFileExclusively(String pathname) throws IOException { - return new File(pathname).createNewFile(); - } - - @Override - public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { - if (f.isDirectory(contextProvider)) { - AmazeFile[] children = f.listFiles(contextProvider); - if (children != null) { - for (AmazeFile child : children) { - delete(child, contextProvider); - } - } - - // Try the normal way - if (new File(f.getPath()).delete()) { - return true; - } - - final Context context = contextProvider.getContext(); - - // Try with Storage Access Framework. - if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - DocumentFile document = - ExternalSdCardOperation.getDocumentFile( - f, true, context, UriForSafPersistance.get(context)); - if (document != null && document.delete()) { - return true; - } - } - - // Try the Kitkat workaround. - if (context != null && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { - ContentResolver resolver = context.getContentResolver(); - ContentValues values = new ContentValues(); - values.put(MediaStore.MediaColumns.DATA, f.getAbsolutePath()); - resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); - - // Delete the created entry, such that content provider will delete the file. - resolver.delete( - MediaStore.Files.getContentUri("external"), - MediaStore.MediaColumns.DATA + "=?", - new String[] {f.getAbsolutePath()}); - } - - return !f.exists(contextProvider); - } - - if (new File(f.getPath()).delete()) { - return true; - } - - final Context context = contextProvider.getContext(); - - // Try with Storage Access Framework. - if (context != null - && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP - && ExternalSdCardOperation.isOnExtSdCard(f, context)) { - DocumentFile document = - ExternalSdCardOperation.getDocumentFile( - f, false, context, UriForSafPersistance.get(context)); - if (document == null) { - return true; - } - - if (document.delete()) { - return true; - } - } - - // Try the Kitkat workaround. - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { - ContentResolver resolver = context.getContentResolver(); - try { - Uri uri = MediaStoreHack.getUriFromFile(f.getAbsolutePath(), context); - if (uri == null) { - return false; - } - resolver.delete(uri, null, null); - return !f.exists(contextProvider); - } catch (SecurityException e) { - Log.e(TAG, "Security exception when checking for file " + f.getAbsolutePath(), e); - } - } - - return false; - } - - @Nullable - @Override - public String[] list(AmazeFile f, @NonNull ContextProvider contextProvider) { - return new File(f.getPath()).list(); - } - - @Nullable - @Override - public InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - return new FileInputStream(f.getPath()); - } catch (FileNotFoundException e) { - Log.e(TAG, "Cannot find file", e); - return null; - } - } - - @Nullable - @Override - public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - if (f.canWrite(contextProvider)) { - return new FileOutputStream(f.getPath()); - } else { - final Context context = contextProvider.getContext(); - if (context == null) { - return null; - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // Storage Access Framework - DocumentFile targetDocument = - ExternalSdCardOperation.getDocumentFile( - f, false, context, UriForSafPersistance.get(context)); - - if (targetDocument == null) { - return null; - } - - return context.getContentResolver().openOutputStream(targetDocument.getUri()); - } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { - // Workaround for Kitkat ext SD card - return MediaStoreHack.getOutputStream(context, f.getPath()); - } - } - - return null; - } catch (FileNotFoundException e) { - Log.e(TAG, "Cannot find file", e); - return null; - } - } - - @Override - public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { - if (new File(f.getPath()).mkdir()) { - return true; - } - - final Context context = contextProvider.getContext(); - - // Try with Storage Access Framework. - if (context != null - && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP - && ExternalSdCardOperation.isOnExtSdCard(f, context)) { - String preferenceUri = UriForSafPersistance.get(context); - - DocumentFile document = - ExternalSdCardOperation.getDocumentFile(f, true, context, preferenceUri); - if (document == null) { - return false; - } - // getDocumentFile implicitly creates the directory. - return document.exists(); - } - - // Try the Kitkat workaround. - if (context != null && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { - try { - return MediaStoreHack.mkdir(context, f); - } catch (IOException e) { - return false; - } - } - - return false; - } - - @Override - public boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider) { - return new File(f1.getPath()).renameTo(new File(f2.getPath())); - } - - @Override - public boolean setLastModifiedTime(AmazeFile f, long time) { - return new File(f.getPath()).setLastModified(time); - } - - @Override - public boolean setReadOnly(AmazeFile f) { - return new File(f.getPath()).setReadOnly(); - } - - @Override - public AmazeFile[] listRoots() { - File[] roots = File.listRoots(); - AmazeFile[] amazeRoots = new AmazeFile[roots.length]; - - for (int i = 0; i < roots.length; i++) { - amazeRoots[i] = new AmazeFile(roots[i].getPath()); - } - - return amazeRoots; - } - - public long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider) { - return new File(f.getPath()).getTotalSpace(); - } - - public long getFreeSpace(AmazeFile f) { - return new File(f.getPath()).getFreeSpace(); - } - - public long getUsableSpace(AmazeFile f) { - return new File(f.getPath()).getUsableSpace(); - } - - @Override - public int compare(AmazeFile f1, AmazeFile f2) { - return new File(f1.getPath()).compareTo(new File(f2.getPath())); - } - - @Override - public int hashCode(AmazeFile f) { - return new File(f.getPath()).hashCode(); - } -} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt new file mode 100644 index 0000000000..d6b23d96c6 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.file + +import android.content.ContentValues +import android.os.Build +import android.provider.MediaStore +import android.util.Log +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider +import com.amaze.filemanager.file_operations.filesystem.filetypes.file.ExternalSdCardOperation.getDocumentFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.file.ExternalSdCardOperation.isOnExtSdCard +import com.amaze.filemanager.file_operations.filesystem.filetypes.file.UriForSafPersistance.get +import java.io.* + +/** + * This is in in essence calls to UnixFilesystem, but that class is not public so all calls must go + * through java.io.File + */ +class FileAmazeFilesystem private constructor() : AmazeFilesystem() { + companion object { + val INSTANCE = FileAmazeFilesystem() + val TAG = FileAmazeFilesystem::class.java.simpleName + + init { + AmazeFile.addFilesystem(INSTANCE) + } + } + + override fun isPathOfThisFilesystem(path: String): Boolean { + return path[0] == getSeparator() + } + + override val prefix: String + get() = throw NotImplementedError() + + override fun getSeparator(): Char { + return File.separatorChar + } + + override fun normalize(path: String): String { + return File(path).path + } + + override fun prefixLength(path: String): Int { + if (path.length == 0) return 0 + return if (path[0] == '/') 1 else 0 + } + + override fun resolve(parent: String?, child: String?): String { + return File(parent, child).path + } + + override val defaultParent: String + get() = File(File(""), "").path + + override fun isAbsolute(f: AmazeFile): Boolean { + return File(f.path).isAbsolute + } + + override fun resolve(f: AmazeFile): String { + return File(f.path).absolutePath + } + + @Throws(IOException::class) + override fun canonicalize(path: String?): String { + return File(path).canonicalPath + } + + override fun exists(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return f.exists(contextProvider) + } + + override fun isFile(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return File(f.path).isFile + } + + override fun isDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return File(f.path).isDirectory + } + + override fun isHidden(f: AmazeFile): Boolean { + return File(f.path).isHidden + } + + override fun canExecute(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return File(f.path).canExecute() + } + + override fun canWrite(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return File(f.path).canWrite() + } + + override fun canRead(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return File(f.path).canRead() + } + + override fun canAccess(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return File(f.path).exists() + } + + override fun setExecutable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + return File(f.path).setExecutable(enable, owneronly) + } + + override fun setWritable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + return File(f.path).setWritable(enable, owneronly) + } + + override fun setReadable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + return File(f.path).setReadable(enable, owneronly) + } + + override fun getLastModifiedTime(f: AmazeFile): Long { + return File(f.path).lastModified() + } + + @Throws(IOException::class) + override fun getLength(f: AmazeFile, contextProvider: ContextProvider): Long { + return File(f.path).length() + } + + @Throws(IOException::class) + override fun createFileExclusively(pathname: String?): Boolean { + return File(pathname).createNewFile() + } + + override fun delete(f: AmazeFile, contextProvider: ContextProvider): Boolean { + if (f.isDirectory(contextProvider)) { + val children = f.listFiles(contextProvider) + if (children != null) { + for (child in children) { + delete(child, contextProvider) + } + } + + // Try the normal way + if (File(f.path).delete()) { + return true + } + val context = contextProvider.getContext() + + // Try with Storage Access Framework. + if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + val document = getDocumentFile( + f, true, context, get(context)) + if (document != null && document.delete()) { + return true + } + } + + // Try the Kitkat workaround. + if (context != null && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + val resolver = context.contentResolver + val values = ContentValues() + values.put(MediaStore.MediaColumns.DATA, f.absolutePath) + resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) + + // Delete the created entry, such that content provider will delete the file. + resolver.delete( + MediaStore.Files.getContentUri("external"), + MediaStore.MediaColumns.DATA + "=?", arrayOf(f.absolutePath)) + } + return !f.exists(contextProvider) + } + if (File(f.path).delete()) { + return true + } + val context = contextProvider.getContext() + + // Try with Storage Access Framework. + if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isOnExtSdCard(f, context)) { + val document = getDocumentFile( + f, false, context, get(context)) ?: return true + if (document.delete()) { + return true + } + } + + // Try the Kitkat workaround. + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + val resolver = context!!.contentResolver + try { + val uri = MediaStoreHack.getUriFromFile(f.absolutePath, context) + ?: return false + resolver.delete(uri, null, null) + return !f.exists(contextProvider) + } catch (e: SecurityException) { + Log.e(TAG, "Security exception when checking for file " + f.absolutePath, e) + } + } + return false + } + + override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { + return File(f.path).list() + } + + override fun getInputStream(f: AmazeFile, contextProvider: ContextProvider): InputStream? { + return try { + FileInputStream(f.path) + } catch (e: FileNotFoundException) { + Log.e(TAG, "Cannot find file", e) + null + } + } + + override fun getOutputStream(f: AmazeFile, contextProvider: ContextProvider): OutputStream? { + return try { + if (f.canWrite(contextProvider)) { + return FileOutputStream(f.path) + } else { + val context = contextProvider.getContext() ?: return null + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // Storage Access Framework + val targetDocument = getDocumentFile( + f, false, context, get(context)) ?: return null + return context.contentResolver.openOutputStream(targetDocument.uri) + } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + // Workaround for Kitkat ext SD card + return MediaStoreHack.getOutputStream(context, f.path) + } + } + null + } catch (e: FileNotFoundException) { + Log.e(TAG, "Cannot find file", e) + null + } + } + + override fun createDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { + if (File(f.path).mkdir()) { + return true + } + val context = contextProvider.getContext() + + // Try with Storage Access Framework. + if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isOnExtSdCard(f, context)) { + val preferenceUri = get(context) + val document = getDocumentFile(f, true, context, preferenceUri) + ?: return false + // getDocumentFile implicitly creates the directory. + return document.exists() + } + + // Try the Kitkat workaround. + return if (context != null && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + try { + MediaStoreHack.mkdir(context, f) + } catch (e: IOException) { + false + } + } else false + } + + override fun rename(f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean { + return File(f1.path).renameTo(File(f2.path)) + } + + override fun setLastModifiedTime(f: AmazeFile, time: Long): Boolean { + return File(f.path).setLastModified(time) + } + + override fun setReadOnly(f: AmazeFile): Boolean { + return File(f.path).setReadOnly() + } + + override fun listRoots(): Array { + val roots = File.listRoots() + return Array(roots.size) { i: Int -> + AmazeFile(roots[i].path) + } + } + + override fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long { + return File(f.path).totalSpace + } + + override fun getFreeSpace(f: AmazeFile): Long { + return File(f.path).freeSpace + } + + override fun getUsableSpace(f: AmazeFile): Long { + return File(f.path).usableSpace + } + + override fun compare(f1: AmazeFile, f2: AmazeFile): Int { + return File(f1.path).compareTo(File(f2.path)) + } + + override fun hashCode(f: AmazeFile): Int { + return File(f.path).hashCode() + } +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java deleted file mode 100644 index eab9847576..0000000000 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.java +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.smb; - -import static com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.SMB_URI_PREFIX; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.MalformedURLException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; -import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; - -import android.net.Uri; -import android.text.TextUtils; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import jcifs.CIFSContext; -import jcifs.SmbConstants; -import jcifs.smb.NtlmPasswordAuthenticator; -import jcifs.smb.SmbException; -import jcifs.smb.SmbFile; -import kotlin.NotImplementedError; - -/** - * Root is "smb://:@" or "smb://" or - * "smb://:@/?disableIpcSigningCheck=true" or - * "smb:///?disableIpcSigningCheck=true" Relative paths are not supported - */ -public class SmbAmazeFilesystem extends AmazeFilesystem { - public static final String TAG = SmbAmazeFilesystem.class.getSimpleName(); - - public static final String PREFIX = SMB_URI_PREFIX; - public static final String PARAM_DISABLE_IPC_SIGNING_CHECK = "disableIpcSigningCheck"; - - private static final Pattern IPv4_PATTERN = - Pattern.compile("[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+"); - private static final Pattern METADATA_PATTERN = - Pattern.compile("([?][a-zA-Z]+=(\")?[a-zA-Z]+(\")?)+"); - - public static final SmbAmazeFilesystem INSTANCE = new SmbAmazeFilesystem(); - - static { - AmazeFile.addFilesystem(INSTANCE); - } - - private SmbAmazeFilesystem() {} - - @Override - public String getPrefix() { - return PREFIX; - } - - @NonNull - @Override - public String normalize(@NonNull String pathname) { - String canonical; - try { - canonical = canonicalize(pathname); - } catch (MalformedURLException e) { - Log.e(TAG, "Error getting SMB file canonical path", e); - canonical = pathname.substring(0, prefixLength(pathname)) + "/"; - } - return canonical; - } - - @Override - public int prefixLength(@NonNull String path) { - if (path.length() == 0) { - throw new IllegalArgumentException( - "This should never happen, all paths must start with SMB prefix"); - } - - Matcher matcherMetadata = METADATA_PATTERN.matcher(path); - if (matcherMetadata.find()) { - return matcherMetadata.end(); - } - - Matcher matcher = IPv4_PATTERN.matcher(path); - matcher.find(); - return matcher.end(); - } - - @NonNull - @Override - public String resolve(String parent, String child) { - final String prefix = parent.substring(0, prefixLength(parent)); - final String simplePathParent = parent.substring(prefixLength(parent)); - final String simplePathChild = child.substring(prefixLength(child)); - - return prefix + basicUnixResolve(simplePathParent, simplePathChild); - } - - /** This makes no sense for SMB */ - @NonNull - @Override - public String getDefaultParent() { - throw new IllegalStateException("There is no default SMB path"); - } - - @Override - public boolean isAbsolute(AmazeFile f) { - return f.getPath().startsWith(PREFIX); - } - - @NonNull - @Override - public String resolve(AmazeFile f) { - if (isAbsolute(f)) { - return f.getPath(); - } - - throw new IllegalArgumentException("Relative paths are not supported"); - } - - @NonNull - @Override - public String canonicalize(String path) throws MalformedURLException { - return create(path).getCanonicalPath(); - } - - public boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - SmbFile smbFile = create(f.getPath()); - return smbFile.exists(); - } catch (MalformedURLException | SmbException e) { - Log.e(TAG, "Failed to get attributes for SMB file", e); - return false; - } - } - - public boolean isFile(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - SmbFile smbFile = create(f.getPath()); - return smbFile.getType() == SmbConstants.TYPE_FILESYSTEM; - } catch (MalformedURLException | SmbException e) { - Log.e(TAG, "Failed to get attributes for SMB file", e); - return false; - } - } - - public boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - SmbFile smbFile = create(f.getPath()); - return smbFile.isDirectory(); - } catch (MalformedURLException | SmbException e) { - Log.e(TAG, "Failed to get attributes for SMB file", e); - return false; - } - } - - public boolean isHidden(AmazeFile f) { - try { - SmbFile smbFile = create(f.getPath()); - return smbFile.isHidden(); - } catch (MalformedURLException | SmbException e) { - Log.e(TAG, "Failed to get attributes for SMB file", e); - return false; - } - } - - public boolean canExecute(AmazeFile f, @NonNull ContextProvider contextProvider) { - throw new NotImplementedError(); - } - - public boolean canWrite(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - return create(f.getPath()).canWrite(); - } catch (MalformedURLException | SmbException e) { - Log.e(TAG, "Error getting SMB file to check access", e); - return false; - } - } - - public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - return create(f.getPath()).canRead(); - } catch (MalformedURLException | SmbException e) { - Log.e(TAG, "Error getting SMB file to check access", e); - return false; - } - } - - public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - SmbFile file = create(f.getPath()); - file.setConnectTimeout(2000); - return file.exists(); - } catch (MalformedURLException | SmbException e) { - Log.e(TAG, "Error getting SMB file to check access", e); - return false; - } - } - - public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly) { - throw new NotImplementedError(); - } - - public boolean setWritable(AmazeFile f, boolean enable, boolean owneronly) { - throw new NotImplementedError(); - } - - public boolean setReadable(AmazeFile f, boolean enable, boolean owneronly) { - throw new NotImplementedError(); - } - - @Override - public long getLastModifiedTime(AmazeFile f) { - try { - return create(f.getPath()).getLastModified(); - } catch (MalformedURLException e) { - Log.e(TAG, "Error getting SMB file to get last modified time", e); - return 0; - } - } - - @Override - public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) - throws SmbException, MalformedURLException { - return create(f.getPath()).length(); - } - - public static SmbFile create(String path) throws MalformedURLException { - String processedPath; - if (!path.endsWith(STANDARD_SEPARATOR + "")) { - processedPath = path + STANDARD_SEPARATOR; - } else { - processedPath = path; - } - Uri uri = Uri.parse(processedPath); - boolean disableIpcSigningCheck = - Boolean.parseBoolean(uri.getQueryParameter(PARAM_DISABLE_IPC_SIGNING_CHECK)); - String userInfo = uri.getUserInfo(); - - final String noExtraInfoPath; - if (path.contains("?")) { - noExtraInfoPath = path.substring(0, path.indexOf('?')); - } else { - noExtraInfoPath = path; - } - - final CIFSContext context = - CifsContexts.createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) - .withCredentials(createFrom(userInfo)); - - return new SmbFile(noExtraInfoPath, context); - } - - /** - * Create {@link NtlmPasswordAuthenticator} from given userInfo parameter. - * - *

Logic borrowed directly from jcifs-ng's own code. They should make that protected - * constructor public... - * - * @param userInfo authentication string, must be already URL decoded. {@link Uri} shall do this - * for you already - * @return {@link NtlmPasswordAuthenticator} instance - */ - private static @NonNull NtlmPasswordAuthenticator createFrom(@Nullable String userInfo) { - if (!TextUtils.isEmpty(userInfo)) { - String dom = null; - String user = null; - String pass = null; - int i; - int u; - int end = userInfo.length(); - for (i = 0, u = 0; i < end; i++) { - char c = userInfo.charAt(i); - if (c == ';') { - dom = userInfo.substring(0, i); - u = i + 1; - } else if (c == ':') { - pass = userInfo.substring(i + 1); - break; - } - } - user = userInfo.substring(u, i); - return new NtlmPasswordAuthenticator(dom, user, pass); - } else { - return new NtlmPasswordAuthenticator(); - } - } - - @Override - public boolean createFileExclusively(String pathname) throws IOException { - create(pathname).mkdirs(); - return true; - } - - @Override - public boolean delete(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - create(f.getPath()).delete(); - return true; - } catch (SmbException | MalformedURLException e) { - Log.e(TAG, "Error deleting SMB file", e); - return false; - } - } - - @Override - public String[] list(AmazeFile f, @NonNull ContextProvider contextProvider) { - String[] list; - try { - list = create(f.getPath()).list(); - } catch (SmbException | MalformedURLException e) { - Log.e(TAG, "Error listing SMB files", e); - return null; - } - final String prefix = f.getPath().substring(0, prefixLength(f.getPath())); - - for (int i = 0; i < list.length; i++) { - list[i] = SmbAmazeFilesystem.INSTANCE.normalize(prefix + getSeparator() + list[i]); - } - - return list; - } - - @Nullable - @Override - public InputStream getInputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - return create(f.getPath()).getInputStream(); - } catch (IOException e) { - Log.e(TAG, "Error creating SMB output stream", e); - return null; - } - } - - @Nullable - @Override - public OutputStream getOutputStream(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - return create(f.getPath()).getOutputStream(); - } catch (IOException e) { - Log.e(TAG, "Error creating SMB output stream", e); - return null; - } - } - - @Override - public boolean createDirectory(AmazeFile f, @NonNull ContextProvider contextProvider) { - try { - create(f.getPath()).mkdir(); - return true; - } catch (SmbException | MalformedURLException e) { - Log.e(TAG, "Error creating SMB directory", e); - return false; - } - } - - @Override - public boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider) { - try { - create(f1.getPath()).renameTo(create(f2.getPath())); - return true; - } catch (SmbException | MalformedURLException e) { - Log.e(TAG, "Error getting SMB files for a rename", e); - return false; - } - } - - @Override - public boolean setLastModifiedTime(AmazeFile f, long time) { - try { - create(f.getPath()).setLastModified(time); - return true; - } catch (SmbException | MalformedURLException e) { - Log.e(TAG, "Error getting SMB file to set modified time", e); - return false; - } - } - - @Override - public boolean setReadOnly(AmazeFile f) { - try { - create(f.getPath()).setReadOnly(); - return true; - } catch (SmbException | MalformedURLException e) { - Log.e(TAG, "Error getting SMB file to set read only", e); - return false; - } - } - - @Override - public AmazeFile[] listRoots() { - throw new NotImplementedError(); - } - - public long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider) { - // TODO: Find total storage space of SMB when JCIFS adds support - throw new NotImplementedError(); - } - - public long getFreeSpace(AmazeFile f) { - try { - return create(f.getPath()).getDiskFreeSpace(); - } catch (SmbException | MalformedURLException e) { - Log.e(TAG, "Error getting SMB file to read free volume space", e); - return 0; - } - } - - public long getUsableSpace(AmazeFile f) { - // TODO: Find total storage space of SMB when JCIFS adds support - throw new NotImplementedError(); - } -} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt new file mode 100644 index 0000000000..6a3e8e2c10 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.smb + +import android.net.Uri +import android.text.TextUtils +import android.util.Log +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.createWithDisableIpcSigningCheck +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem +import jcifs.smb.SmbFile +import jcifs.smb.NtlmPasswordAuthenticator +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.SMB_URI_PREFIX +import jcifs.smb.SmbException +import jcifs.SmbConstants +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.lang.IllegalArgumentException +import java.lang.IllegalStateException +import java.net.MalformedURLException +import java.util.regex.Pattern + +/** + * Root is "smb://:@" or "smb://" or + * "smb://:@/?disableIpcSigningCheck=true" or + * "smb:///?disableIpcSigningCheck=true" Relative paths are not supported + */ +class SmbAmazeFilesystem private constructor() : AmazeFilesystem() { + companion object { + val TAG = SmbAmazeFilesystem::class.java.simpleName + + const val PARAM_DISABLE_IPC_SIGNING_CHECK = "disableIpcSigningCheck" + private val IPv4_PATTERN = Pattern.compile("[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+") + private val METADATA_PATTERN = Pattern.compile("([?][a-zA-Z]+=(\")?[a-zA-Z]+(\")?)+") + + val INSTANCE = SmbAmazeFilesystem() + + @Throws(MalformedURLException::class) + fun create(path: String?): SmbFile { + val processedPath: String + processedPath = if (!path!!.endsWith(STANDARD_SEPARATOR + "")) { + path + STANDARD_SEPARATOR + } else { + path + } + val uri = Uri.parse(processedPath) + val disableIpcSigningCheck = java.lang.Boolean.parseBoolean(uri.getQueryParameter(PARAM_DISABLE_IPC_SIGNING_CHECK)) + val userInfo = uri.userInfo + val noExtraInfoPath: String + noExtraInfoPath = if (path.contains("?")) { + path.substring(0, path.indexOf('?')) + } else { + path + } + val context = createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) + .withCredentials(createFrom(userInfo)) + return SmbFile(noExtraInfoPath, context) + } + + /** + * Create [NtlmPasswordAuthenticator] from given userInfo parameter. + * + * + * Logic borrowed directly from jcifs-ng's own code. They should make that protected + * constructor public... + * + * @param userInfo authentication string, must be already URL decoded. [Uri] shall do this + * for you already + * @return [NtlmPasswordAuthenticator] instance + */ + private fun createFrom(userInfo: String?): NtlmPasswordAuthenticator { + return if (!TextUtils.isEmpty(userInfo)) { + var dom: String? = null + var user: String? = null + var pass: String? = null + var i: Int + var u: Int + val end = userInfo!!.length + i = 0 + u = 0 + while (i < end) { + val c = userInfo[i] + if (c == ';') { + dom = userInfo.substring(0, i) + u = i + 1 + } else if (c == ':') { + pass = userInfo.substring(i + 1) + break + } + i++ + } + user = userInfo.substring(u, i) + NtlmPasswordAuthenticator(dom, user, pass) + } else { + NtlmPasswordAuthenticator() + } + } + + init { + AmazeFile.addFilesystem(INSTANCE) + } + } + + override val prefix: String = SMB_URI_PREFIX + + override fun normalize(pathname: String): String { + val canonical: String + canonical = try { + canonicalize(pathname) + } catch (e: MalformedURLException) { + Log.e(TAG, "Error getting SMB file canonical path", e) + pathname.substring(0, prefixLength(pathname)) + "/" + } + return canonical + } + + override fun prefixLength(path: String): Int { + require(path.length != 0) { "This should never happen, all paths must start with SMB prefix" } + val matcherMetadata = METADATA_PATTERN.matcher(path) + if (matcherMetadata.find()) { + return matcherMetadata.end() + } + val matcher = IPv4_PATTERN.matcher(path) + matcher.find() + return matcher.end() + } + + override fun resolve(parent: String?, child: String?): String { + val prefix = parent!!.substring(0, prefixLength(parent)) + val simplePathParent = parent.substring(prefixLength(parent)) + val simplePathChild = child!!.substring(prefixLength(child)) + return prefix + basicUnixResolve(simplePathParent, simplePathChild) + } + + /** This makes no sense for SMB */ + override val defaultParent: String + get() { + throw IllegalStateException("There is no default SMB path") + } + + override fun isAbsolute(f: AmazeFile): Boolean { + return f.path.startsWith(prefix) + } + + override fun resolve(f: AmazeFile): String { + if (isAbsolute(f)) { + return f.path + } + throw IllegalArgumentException("Relative paths are not supported") + } + + @Throws(MalformedURLException::class) + override fun canonicalize(path: String?): String { + return create(path).canonicalPath + } + + override fun exists(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return try { + val smbFile = create(f.path) + smbFile.exists() + } catch (e: MalformedURLException) { + Log.e(TAG, "Failed to get attributes for SMB file", e) + false + } catch (e: SmbException) { + Log.e(TAG, "Failed to get attributes for SMB file", e) + false + } + } + + override fun isFile(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return try { + val smbFile = create(f.path) + smbFile.type == SmbConstants.TYPE_FILESYSTEM + } catch (e: MalformedURLException) { + Log.e(TAG, "Failed to get attributes for SMB file", e) + false + } catch (e: SmbException) { + Log.e(TAG, "Failed to get attributes for SMB file", e) + false + } + } + + override fun isDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return try { + val smbFile = create(f.path) + smbFile.isDirectory + } catch (e: MalformedURLException) { + Log.e(TAG, "Failed to get attributes for SMB file", e) + false + } catch (e: SmbException) { + Log.e(TAG, "Failed to get attributes for SMB file", e) + false + } + } + + override fun isHidden(f: AmazeFile): Boolean { + return try { + val smbFile = create(f.path) + smbFile.isHidden + } catch (e: MalformedURLException) { + Log.e(TAG, "Failed to get attributes for SMB file", e) + false + } catch (e: SmbException) { + Log.e(TAG, "Failed to get attributes for SMB file", e) + false + } + } + + override fun canExecute(f: AmazeFile, contextProvider: ContextProvider): Boolean { + throw NotImplementedError() + } + + override fun canWrite(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return try { + create(f.path).canWrite() + } catch (e: MalformedURLException) { + Log.e(TAG, "Error getting SMB file to check access", e) + false + } catch (e: SmbException) { + Log.e(TAG, "Error getting SMB file to check access", e) + false + } + } + + override fun canRead(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return try { + create(f.path).canRead() + } catch (e: MalformedURLException) { + Log.e(TAG, "Error getting SMB file to check access", e) + false + } catch (e: SmbException) { + Log.e(TAG, "Error getting SMB file to check access", e) + false + } + } + + override fun canAccess(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return try { + val file = create(f.path) + file.connectTimeout = 2000 + file.exists() + } catch (e: MalformedURLException) { + Log.e(TAG, "Error getting SMB file to check access", e) + false + } catch (e: SmbException) { + Log.e(TAG, "Error getting SMB file to check access", e) + false + } + } + + override fun setExecutable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + throw NotImplementedError() + } + + override fun setWritable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + throw NotImplementedError() + } + + override fun setReadable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + throw NotImplementedError() + } + + override fun getLastModifiedTime(f: AmazeFile): Long { + return try { + create(f.path).lastModified + } catch (e: MalformedURLException) { + Log.e(TAG, "Error getting SMB file to get last modified time", e) + 0 + } + } + + @Throws(SmbException::class, MalformedURLException::class) + override fun getLength(f: AmazeFile, contextProvider: ContextProvider): Long { + return create(f.path).length() + } + + @Throws(IOException::class) + override fun createFileExclusively(pathname: String?): Boolean { + create(pathname).mkdirs() + return true + } + + override fun delete(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return try { + create(f.path).delete() + true + } catch (e: SmbException) { + Log.e(TAG, "Error deleting SMB file", e) + false + } catch (e: MalformedURLException) { + Log.e(TAG, "Error deleting SMB file", e) + false + } + } + + override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { + val list: Array + list = try { + create(f.path).list() + } catch (e: SmbException) { + Log.e(TAG, "Error listing SMB files", e) + return null + } catch (e: MalformedURLException) { + Log.e(TAG, "Error listing SMB files", e) + return null + } + val prefix = f.path.substring(0, prefixLength(f.path)) + for (i in list.indices) { + list[i] = INSTANCE.normalize(prefix + getSeparator() + list[i]) + } + return list + } + + override fun getInputStream(f: AmazeFile, contextProvider: ContextProvider): InputStream? { + return try { + create(f.path).inputStream + } catch (e: IOException) { + Log.e(TAG, "Error creating SMB output stream", e) + null + } + } + + override fun getOutputStream(f: AmazeFile, contextProvider: ContextProvider): OutputStream? { + return try { + create(f.path).outputStream + } catch (e: IOException) { + Log.e(TAG, "Error creating SMB output stream", e) + null + } + } + + override fun createDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return try { + create(f.path).mkdir() + true + } catch (e: SmbException) { + Log.e(TAG, "Error creating SMB directory", e) + false + } catch (e: MalformedURLException) { + Log.e(TAG, "Error creating SMB directory", e) + false + } + } + + override fun rename(f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean { + return try { + create(f1!!.path).renameTo(create(f2!!.path)) + true + } catch (e: SmbException) { + Log.e(TAG, "Error getting SMB files for a rename", e) + false + } catch (e: MalformedURLException) { + Log.e(TAG, "Error getting SMB files for a rename", e) + false + } + } + + override fun setLastModifiedTime(f: AmazeFile, time: Long): Boolean { + return try { + create(f.path).lastModified = time + true + } catch (e: SmbException) { + Log.e(TAG, "Error getting SMB file to set modified time", e) + false + } catch (e: MalformedURLException) { + Log.e(TAG, "Error getting SMB file to set modified time", e) + false + } + } + + override fun setReadOnly(f: AmazeFile): Boolean { + return try { + create(f.path).setReadOnly() + true + } catch (e: SmbException) { + Log.e(TAG, "Error getting SMB file to set read only", e) + false + } catch (e: MalformedURLException) { + Log.e(TAG, "Error getting SMB file to set read only", e) + false + } + } + + override fun listRoots(): Array { + throw NotImplementedError() + } + + override fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long { + // TODO: Find total storage space of SMB when JCIFS adds support + throw NotImplementedError() + } + + override fun getFreeSpace(f: AmazeFile): Long { + return try { + create(f.path).diskFreeSpace + } catch (e: SmbException) { + Log.e(TAG, "Error getting SMB file to read free volume space", e) + 0 + } catch (e: MalformedURLException) { + Log.e(TAG, "Error getting SMB file to read free volume space", e) + 0 + } + } + + override fun getUsableSpace(f: AmazeFile): Long { + // TODO: Find total storage space of SMB when JCIFS adds support + throw NotImplementedError() + } +} \ No newline at end of file From e9e2336e670aa70d93ccdd0ee64b19a74ea25f00 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Thu, 27 Jan 2022 20:39:57 -0300 Subject: [PATCH 30/46] Converted AmazeFile to kt --- .../filesystem/otg/OtgAmazeFilesystem.kt | 10 +- .../filesystem/ssh/SshAmazeFilesystem.java | 2 +- file_operations/build.gradle | 1 + .../filesystem/filetypes/AmazeFile.java | 1465 ----------------- .../filesystem/filetypes/AmazeFile.kt | 1371 +++++++++++++++ .../filesystem/filetypes/AmazeFilesystem.kt | 2 +- .../filetypes/cloud/CloudAmazeFilesystem.kt | 13 +- .../filetypes/file/FileAmazeFilesystem.kt | 2 +- .../filetypes/smb/SmbAmazeFilesystem.kt | 10 +- 9 files changed, 1389 insertions(+), 1487 deletions(-) delete mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java create mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt index 4a9d2aaa64..1e2cc9bb86 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt @@ -125,12 +125,12 @@ class OtgAmazeFilesystem private constructor() : AmazeFilesystem() { return getDocumentFile(f.path, context, false)!!.delete() } - override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { + override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { val context = contextProvider.getContext() ?: return null - val list = ArrayList() - val rootUri = getDocumentFile(f.path, context, false) - for (file in rootUri!!.listFiles()) { - list.add(file.uri.path) + val list = ArrayList() + val rootUri = getDocumentFile(f.path, context, false) ?: return null + for (file in rootUri.listFiles()) { + file.uri.path?.let { list.add(it) } } return list.toTypedArray() } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java index 07a37d9bab..c1b39ecba6 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -251,7 +251,7 @@ public Boolean execute(@NonNull SFTPClient client) throws IOException { return retval != null && retval; } - @Nullable + @org.jetbrains.annotations.Nullable @Override public String[] list(AmazeFile f, @NonNull ContextProvider contextProvider) { List fileNameList = SshClientUtils.execute( diff --git a/file_operations/build.gradle b/file_operations/build.gradle index 0c700a9b9a..7012ee789c 100644 --- a/file_operations/build.gradle +++ b/file_operations/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.library' +apply plugin: 'kotlin-parcelize' apply plugin: 'kotlin-android' android { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java deleted file mode 100644 index ba49efcb2b..0000000000 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.java +++ /dev/null @@ -1,1465 +0,0 @@ -/* - * Copyright (C) 2014-2014 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes; - -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Path; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import kotlin.NotImplementedError; - -// Android-added: Info about UTF-8 usage in filenames. -/** - * An abstract representation of file and directory pathnames. - * - *

User interfaces and operating systems use system-dependent pathname strings to name - * files and directories. This class presents an abstract, system-independent view of hierarchical - * pathnames. An abstract pathname has two components: - * - *

    - *
  1. An optional system-dependent prefix string, such as a disk-drive specifier, - * "/" for the UNIX root directory, or "\\\\" for a Microsoft - * Windows UNC pathname, and - *
  2. A sequence of zero or more string names. - *
- * - * The first name in an abstract pathname may be a directory name or, in the case of Microsoft - * Windows UNC pathnames, a hostname. Each subsequent name in an abstract pathname denotes a - * directory; the last name may denote either a directory or a file. The empty abstract - * pathname has no prefix and an empty name sequence. - * - *

The conversion of a pathname string to or from an abstract pathname is inherently - * system-dependent. When an abstract pathname is converted into a pathname string, each name is - * separated from the next by a single copy of the default separator character. The default - * name-separator character is defined by the system property file.separator, and is - * made available in the public static fields {@link - * #separator} and {@link #separatorChar} of this class. When a pathname string - * is converted into an abstract pathname, the names within it may be separated by the default - * name-separator character or by any other name-separator character that is supported by the - * underlying system. - * - *

A pathname, whether abstract or in string form, may be either absolute or - * relative. An absolute pathname is complete in that no other information is required in - * order to locate the file that it denotes. A relative pathname, in contrast, must be interpreted - * in terms of information taken from some other pathname. By default the classes in the - * java.io package always resolve relative pathnames against the current user directory. This - * directory is named by the system property user.dir, and is typically the directory - * in which the Java virtual machine was invoked. - * - *

The parent of an abstract pathname may be obtained by invoking the {@link #getParent} - * method of this class and consists of the pathname's prefix and each name in the pathname's name - * sequence except for the last. Each directory's absolute pathname is an ancestor of any - * AmazeFile object with an absolute abstract pathname which begins with the directory's - * absolute pathname. For example, the directory denoted by the abstract pathname "/usr" is - * an ancestor of the directory denoted by the pathname "/usr/local/bin". - * - *

The prefix concept is used to handle root directories on UNIX platforms, and drive specifiers, - * root directories and UNC pathnames on Microsoft Windows platforms, as follows: - * - *

    - *
  • For UNIX platforms, the prefix of an absolute pathname is always "/". Relative - * pathnames have no prefix. The abstract pathname denoting the root directory has the prefix - * "/" and an empty name sequence. - *
  • For Microsoft Windows platforms, the prefix of a pathname that contains a drive specifier - * consists of the drive letter followed by ":" and possibly followed by - * "\\" if the pathname is absolute. The prefix of a UNC pathname is "\\\\" - * ; the hostname and the share name are the first two names in the name sequence. A - * relative pathname that does not specify a drive has no prefix. - *
- * - *

Instances of this class may or may not denote an actual file-system object such as a file or a - * directory. If it does denote such an object then that object resides in a partition. A - * partition is an operating system-specific portion of storage for a file system. A single storage - * device (e.g. a physical disk-drive, flash memory, CD-ROM) may contain multiple partitions. The - * object, if any, will reside on the partition named by some ancestor of the - * absolute form of this pathname. - * - *

A file system may implement restrictions to certain operations on the actual file-system - * object, such as reading, writing, and executing. These restrictions are collectively known as - * access permissions. The file system may have multiple sets of access permissions on a - * single object. For example, one set may apply to the object's owner, and another may apply - * to all other users. The access permissions on an object may cause some methods in this class to - * fail. - * - *

On Android strings are converted to UTF-8 byte sequences when sending filenames to the - * operating system, and byte sequences returned by the operating system (from the various {@code - * list} methods) are converted to strings by decoding them as UTF-8 byte sequences. - * - * @author unascribed - */ -public class AmazeFile implements Parcelable, Comparable { - - public static final String TAG = AmazeFile.class.getSimpleName(); - - /** The FileSystem object representing the platform's local file system. */ - private AmazeFilesystem fs; - - private static final List filesystems = new ArrayList<>(); - - public static void addFilesystem(AmazeFilesystem amazeFilesystem) { - filesystems.add(amazeFilesystem); - } - - /** - * This abstract pathname's normalized pathname string. A normalized pathname string uses the - * default name-separator character and does not contain any duplicate or redundant separators. - */ - @NonNull private final String path; - - /** The flag indicating whether the file path is invalid. */ - private transient PathStatus status = null; - - /** The length of this abstract pathname's prefix, or zero if it has no prefix. */ - private final transient int prefixLength; - - /** - * The system-dependent default name-separator character. This field is initialized to contain the - * first character of the value of the system property file.separator. On UNIX - * systems the value of this field is '/'; on Microsoft Windows systems it is - * '\\'. - * - * @see java.lang.System#getProperty(java.lang.String) - */ - public final char separatorChar; - - /** - * The system-dependent default name-separator character, represented as a string for convenience. - * This string contains a single character, namely {@link #separatorChar}. - */ - public final String separator; - - /** Enum type that indicates the status of a file path. */ - private enum PathStatus { - INVALID, - CHECKED - }; - - /** - * Check if the file has an invalid path. Currently, the inspection of a file path is very - * limited, and it only covers Nul character check. Returning true means the path is definitely - * invalid/garbage. But returning false does not guarantee that the path is valid. - * - * @return true if the file path is invalid. - */ - final boolean isInvalid() { - if (status == null) { - status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED : PathStatus.INVALID; - } - return status == PathStatus.INVALID; - } - - /** Returns the length of this abstract pathname's prefix. For use by FileSystem classes. */ - int getPrefixLength() { - return prefixLength; - } - - /* -- Constructors -- */ - - /** Internal constructor for already-normalized pathname strings. */ - private AmazeFile(@NonNull String pathname, int prefixLength) { - loadFilesystem(pathname); - separatorChar = fs.getSeparator(); - separator = "" + separatorChar; - this.path = pathname; - this.prefixLength = prefixLength; - } - - /** - * Internal constructor for already-normalized pathname strings. The parameter order is used to - * disambiguate this method from the public(AmazeFile, String) constructor. - */ - private AmazeFile(@NonNull String child, @NonNull AmazeFile parent) { - assert (!parent.path.equals("")); - loadFilesystem(parent.path); - separatorChar = fs.getSeparator(); - separator = "" + separatorChar; - this.path = fs.resolve(parent.path, child); - this.prefixLength = parent.prefixLength; - } - - /** - * Creates a new AmazeFile instance by converting the given pathname string into an - * abstract pathname. If the given string is the empty string, then the result is the empty - * abstract pathname. - * - * @param pathname A pathname string - */ - public AmazeFile(@NonNull String pathname) { - loadFilesystem(pathname); - separatorChar = fs.getSeparator(); - separator = "" + separatorChar; - this.path = fs.normalize(pathname); - this.prefixLength = fs.prefixLength(this.path); - } - - /* Note: The two-argument File constructors do not interpret an empty - parent abstract pathname as the current user directory. An empty parent - instead causes the child to be resolved against the system-dependent - directory defined by the FileSystem.getDefaultParent method. On Unix - this default is "/", while on Microsoft Windows it is "\\". This is required for - compatibility with the original behavior of this class. */ - - /** - * Creates a new AmazeFile instance from a parent pathname string and a child - * pathname string. - * - *

If parent is null then the new AmazeFile instance is - * created as if by invoking the single-argument AmazeFile constructor on the given - * child pathname string. - * - *

Otherwise the parent pathname string is taken to denote a directory, and the - * child pathname string is taken to denote either a directory or a file. If the - * child pathname string is absolute then it is converted into a relative pathname in - * a system-dependent way. If parent is the empty string then the new AmazeFile - * instance is created by converting child into an abstract pathname and - * resolving the result against a system-dependent default directory. Otherwise each pathname - * string is converted into an abstract pathname and the child abstract pathname is resolved - * against the parent. - * - * @param parent The parent pathname string - * @param child The child pathname string - */ - public AmazeFile(@Nullable String parent, @NonNull String child) { - // BEGIN Android-changed: b/25859957, app-compat; don't substitute empty parent. - if (parent != null && !parent.isEmpty()) { - loadFilesystem(parent); - separatorChar = fs.getSeparator(); - separator = "" + separatorChar; - this.path = fs.resolve(fs.normalize(parent), fs.normalize(child)); - // END Android-changed: b/25859957, app-compat; don't substitute empty parent. - } else { - loadFilesystem(child); - separatorChar = fs.getSeparator(); - separator = "" + separatorChar; - this.path = fs.normalize(child); - } - this.prefixLength = fs.prefixLength(this.path); - } - - /** - * Creates a new AmazeFile instance from a parent abstract pathname and a child - * pathname string. - * - *

If parent is null then the new AmazeFile instance is - * created as if by invoking the single-argument AmazeFile constructor on the given - * child pathname string. - * - *

Otherwise the parent abstract pathname is taken to denote a directory, and the - * child pathname string is taken to denote either a directory or a file. If the - * child pathname string is absolute then it is converted into a relative pathname in - * a system-dependent way. If parent is the empty abstract pathname then the new - * AmazeFile instance is created by converting child into an abstract - * pathname and resolving the result against a system-dependent default directory. Otherwise each - * pathname string is converted into an abstract pathname and the child abstract pathname is - * resolved against the parent. - * - * @param parent The parent abstract pathname - * @param child The child pathname string - */ - public AmazeFile(@Nullable AmazeFile parent, @NonNull String child) { - if (parent != null) { - loadFilesystem(parent.getPath()); - separatorChar = fs.getSeparator(); - separator = "" + separatorChar; - - if (parent.path.equals("")) { - this.path = fs.resolve(fs.getDefaultParent(), fs.normalize(child)); - } else { - this.path = fs.resolve(parent.path, fs.normalize(child)); - } - } else { - loadFilesystem(child); - separatorChar = fs.getSeparator(); - separator = "" + separatorChar; - this.path = fs.normalize(child); - } - this.prefixLength = fs.prefixLength(this.path); - } - - private void loadFilesystem(String path) { - for (AmazeFilesystem filesystem : filesystems) { - if (filesystem.isPathOfThisFilesystem(path)) { - fs = filesystem; - } - } - } - - /* -- Path-component accessors -- */ - - /** - * Returns the name of the file or directory denoted by this abstract pathname. This is just the - * last name in the pathname's name sequence. If the pathname's name sequence is empty, then the - * empty string is returned. - * - * @return The name of the file or directory denoted by this abstract pathname, or the empty - * string if this pathname's name sequence is empty - */ - @NonNull - public String getName() { - int index = path.lastIndexOf(separatorChar); - if (index < prefixLength) { - return path.substring(prefixLength); - } - if (path.endsWith("/")) { - int newIndex = path.substring(0, path.length() - 2).lastIndexOf(separatorChar); - if (newIndex < prefixLength) { - return path.substring(prefixLength); - } - return path.substring(newIndex + 1); - } - return path.substring(index + 1); - } - - /** - * Returns the pathname string of this abstract pathname's parent, or null if this - * pathname does not name a parent directory. - * - *

The parent of an abstract pathname consists of the pathname's prefix, if any, and - * each name in the pathname's name sequence except for the last. If the name sequence is empty - * then the pathname does not name a parent directory. - * - * @return The pathname string of the parent directory named by this abstract pathname, or - * null if this pathname does not name a parent - */ - @Nullable - public String getParent() { - int index = path.lastIndexOf(separatorChar); - if (index < prefixLength) { - if ((prefixLength > 0) && (path.length() > prefixLength)) { - return path.substring(0, prefixLength); - } - return null; - } - if (path.endsWith("/")) { - int newIndex = path.substring(0, path.length() - 2).lastIndexOf(separatorChar); - if (newIndex < prefixLength) { - if ((prefixLength > 0) && (path.length() > prefixLength)) { - return path.substring(0, prefixLength); - } - return null; - } - return path.substring(0, newIndex); - } - return path.substring(0, index); - } - - /** - * Returns the abstract pathname of this abstract pathname's parent, or null if this - * pathname does not name a parent directory. - * - *

The parent of an abstract pathname consists of the pathname's prefix, if any, and - * each name in the pathname's name sequence except for the last. If the name sequence is empty - * then the pathname does not name a parent directory. - * - * @return The abstract pathname of the parent directory named by this abstract pathname, or - * null if this pathname does not name a parent - */ - @Nullable - public AmazeFile getParentFile() { - String p = this.getParent(); - if (p == null) { - return null; - } - return new AmazeFile(p, this.prefixLength); - } - - /** - * Converts this abstract pathname into a pathname string. The resulting string uses the {@link - * #separator default name-separator character} to separate the names in the name sequence. - * - * @return The string form of this abstract pathname - */ - @NonNull - public String getPath() { - return path; - } - - /* -- Path operations -- */ - - // Android-changed: Android-specific path information - /** - * Tests whether this abstract pathname is absolute. The definition of absolute pathname is system - * dependent. On Android, absolute paths start with the character '/'. - * - * @return true if this abstract pathname is absolute, false otherwise - */ - public boolean isAbsolute() { - return fs.isAbsolute(this); - } - - // Android-changed: Android-specific path information - /** - * Returns the absolute path of this file. An absolute path is a path that starts at a root of the - * file system. On Android, there is only one root: {@code /}. - * - *

A common use for absolute paths is when passing paths to a {@code Process} as command-line - * arguments, to remove the requirement implied by relative paths, that the child must have the - * same working directory as its parent. - * - * @return The absolute pathname string denoting the same file or directory as this abstract - * pathname - * @see java.io.File#isAbsolute() - */ - @NonNull - public String getAbsolutePath() { - return fs.resolve(this); - } - - /** - * Returns the absolute form of this abstract pathname. Equivalent to - * new File(this.{@link #getAbsolutePath}). - * - * @return The absolute abstract pathname denoting the same file or directory as this abstract - * pathname - */ - @NonNull - public AmazeFile getAbsoluteFile() { - String absPath = getAbsolutePath(); - return new AmazeFile(absPath, fs.prefixLength(absPath)); - } - - /** - * Returns the canonical pathname string of this abstract pathname. - * - *

A canonical pathname is both absolute and unique. The precise definition of canonical form - * is system-dependent. This method first converts this pathname to absolute form if necessary, as - * if by invoking the {@link #getAbsolutePath} method, and then maps it to its unique form in a - * system-dependent way. This typically involves removing redundant names such as "." and - * ".." from the pathname, resolving symbolic links (on UNIX platforms), and converting - * drive letters to a standard case (on Microsoft Windows platforms). - * - *

Every pathname that denotes an existing file or directory has a unique canonical form. Every - * pathname that denotes a nonexistent file or directory also has a unique canonical form. The - * canonical form of the pathname of a nonexistent file or directory may be different from the - * canonical form of the same pathname after the file or directory is created. Similarly, the - * canonical form of the pathname of an existing file or directory may be different from the - * canonical form of the same pathname after the file or directory is deleted. - * - * @return The canonical pathname string denoting the same file or directory as this abstract - * pathname - * @throws IOException If an I/O error occurs, which is possible because the construction of the - * canonical pathname may require filesystem queries - * @see Path#toRealPath - */ - @NonNull - public String getCanonicalPath() throws IOException { - if (isInvalid()) { - throw new IOException("Invalid file path"); - } - return fs.canonicalize(fs.resolve(this)); - } - - /** - * Returns the canonical form of this abstract pathname. Equivalent to - * new File(this.{@link #getCanonicalPath}). - * - * @return The canonical pathname string denoting the same file or directory as this abstract - * pathname - * @throws IOException If an I/O error occurs, which is possible because the construction of the - * canonical pathname may require filesystem queries - * @see Path#toRealPath - */ - @NonNull - public AmazeFile getCanonicalFile() throws IOException { - String canonPath = getCanonicalPath(); - return new AmazeFile(canonPath, fs.prefixLength(canonPath)); - } - - @NonNull - private static String slashify(String path, boolean isDirectory) { - String p = path; - if (File.separatorChar != '/') p = p.replace(File.separatorChar, '/'); - if (!p.startsWith("/")) p = "/" + p; - if (!p.endsWith("/") && isDirectory) p = p + "/"; - return p; - } - - /* -- Attribute accessors -- */ - - // Android-changed. Removed javadoc comment about special privileges - // that doesn't make sense on android - /** - * Tests whether the application can read the file denoted by this abstract pathname. - * - * @return true if and only if the file specified by this abstract pathname exists - * and can be read by the application; false otherwise - */ - public boolean canRead(@NonNull ContextProvider contextProvider) { - if (isInvalid()) { - return false; - } - return fs.canRead(this, contextProvider); - } - - // Android-changed. Removed javadoc comment about special privileges - // that doesn't make sense on android - /** - * Tests whether the application can modify the file denoted by this abstract pathname. - * - * @return true if and only if the file system actually contains a file denoted by - * this abstract pathname and the application is allowed to write to the file; - * false otherwise. - */ - public boolean canWrite(@NonNull ContextProvider contextProvider) { - if (isInvalid()) { - return false; - } - return fs.canWrite(this, contextProvider); - } - - /** - * Tests whether the file or directory denoted by this abstract pathname exists. - * - * @return true if and only if the file or directory denoted by this abstract - * pathname exists; false otherwise - */ - public boolean exists(@NonNull ContextProvider contextProvider) { - if (isInvalid()) { - return false; - } - - // Android-changed: b/25878034 work around SELinux stat64 denial. - return fs.canAccess(this, contextProvider); - } - - /** - * Tests whether the file denoted by this abstract pathname is a directory. - * - *

Where it is required to distinguish an I/O exception from the case that the file is not a - * directory, or where several attributes of the same file are required at the same time, then the - * {@link java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) Files.readAttributes} method - * may be used. - * - * @return true if and only if the file denoted by this abstract pathname exists - * and is a directory; false otherwise - */ - public boolean isDirectory(@NonNull ContextProvider contextProvider) { - if (isInvalid()) { - return false; - } - return ((fs.getBooleanAttributes(this, contextProvider) & AmazeFilesystem.BA_DIRECTORY) != 0); - } - - /** - * Tests whether the file denoted by this abstract pathname is a normal file. A file is - * normal if it is not a directory and, in addition, satisfies other system-dependent - * criteria. Any non-directory file created by a Java application is guaranteed to be a normal - * file. - * - *

Where it is required to distinguish an I/O exception from the case that the file is not a - * normal file, or where several attributes of the same file are required at the same time, then - * the {@link java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) Files.readAttributes} - * method may be used. - * - * @return true if and only if the file denoted by this abstract pathname exists - * and is a normal file; false otherwise - */ - public boolean isFile(@NonNull ContextProvider contextProvider) { - if (isInvalid()) { - return false; - } - return ((fs.getBooleanAttributes(this, contextProvider) & AmazeFilesystem.BA_REGULAR) != 0); - } - - /** - * Tests whether the file named by this abstract pathname is a hidden file. The exact definition - * of hidden is system-dependent. On UNIX systems, a file is considered to be hidden if - * its name begins with a period character ('.'). On Microsoft Windows systems, a - * file is considered to be hidden if it has been marked as such in the filesystem. - * - * @return true if and only if the file denoted by this abstract pathname is hidden - * according to the conventions of the underlying platform - */ - public boolean isHidden(@NonNull ContextProvider contextProvider) { - if (isInvalid()) { - return false; - } - return ((fs.getBooleanAttributes(this, contextProvider) & AmazeFilesystem.BA_HIDDEN) != 0); - } - - /** - * Returns the time that the file denoted by this abstract pathname was last modified. - * - *

Where it is required to distinguish an I/O exception from the case where {@code 0L} is - * returned, or where several attributes of the same file are required at the same time, or where - * the time of last access or the creation time are required, then the {@link - * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) Files.readAttributes} method may be - * used. - * - * @return A long value representing the time the file was last modified, measured in - * milliseconds since the epoch (00:00:00 GMT, January 1, 1970), or 0L if the - * file does not exist or if an I/O error occurs - */ - public long lastModified() { - if (isInvalid()) { - return 0L; - } - return fs.getLastModifiedTime(this); - } - - /** - * Returns the length of the file denoted by this abstract pathname. The return value is - * unspecified if this pathname denotes a directory. - * - *

Where it is required to distinguish an I/O exception from the case that {@code 0L} is - * returned, or where several attributes of the same file are required at the same time, then the - * {@link java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) Files.readAttributes} method - * may be used. - * - * @return The length, in bytes, of the file denoted by this abstract pathname, or 0L - * if the file does not exist. Some operating systems may return 0L for pathnames - * denoting system-dependent entities such as devices or pipes. - */ - public long length(@NonNull ContextProvider contextProvider) throws IOException { - if (isInvalid()) { - return 0L; - } - return fs.getLength(this, contextProvider); - } - - /* -- File operations -- */ - - /** - * Atomically creates a new, empty file named by this abstract pathname if and only if a file with - * this name does not yet exist. The check for the existence of the file and the creation of the - * file if it does not exist are a single operation that is atomic with respect to all other - * filesystem activities that might affect the file. - * - *

Note: this method should not be used for file-locking, as the resulting protocol - * cannot be made to work reliably. The {@link java.nio.channels.FileLock FileLock} facility - * should be used instead. - * - * @return true if the named file does not exist and was successfully created; - * false if the named file already exists - * @throws IOException If an I/O error occurred - */ - public boolean createNewFile() throws IOException { - if (isInvalid()) { - throw new IOException("Invalid file path"); - } - return fs.createFileExclusively(path); - } - - /** - * Deletes the file or directory denoted by this abstract pathname. - * - *

Note that the {@link java.nio.file.Files} class defines the {@link - * java.nio.file.Files#delete(Path) delete} method to throw an {@link IOException} when a file - * cannot be deleted. This is useful for error reporting and to diagnose why a file cannot be - * deleted. - * - * @return true if and only if the file or directory is successfully deleted; - * false otherwise - */ - public boolean delete(@NonNull ContextProvider contextProvider) { - if (isInvalid()) { - return false; - } - return fs.delete(this, contextProvider); - } - - // Android-added: Additional information about Android behaviour. - /** - * Requests that the file or directory denoted by this abstract pathname be deleted when the - * virtual machine terminates. Files (or directories) are deleted in the reverse order that they - * are registered. Invoking this method to delete a file or directory that is already registered - * for deletion has no effect. Deletion will be attempted only for normal termination of the - * virtual machine, as defined by the Java Language Specification. - * - *

Once deletion has been requested, it is not possible to cancel the request. This method - * should therefore be used with care. - * - *

Note: this method should not be used for file-locking, as the resulting protocol - * cannot be made to work reliably. The {@link java.nio.channels.FileLock FileLock} facility - * should be used instead. - * - *

Note that on Android, the application lifecycle does not include VM termination, so - * calling this method will not ensure that files are deleted. Instead, you should use the - * most appropriate out of: - * - *

    - *
  • Use a {@code finally} clause to manually invoke {@link #delete}. - *
  • Maintain your own set of files to delete, and process it at an appropriate point in your - * application's lifecycle. - *
  • Use the Unix trick of deleting the file as soon as all readers and writers have opened - * it. No new readers/writers will be able to access the file, but all existing ones will - * still have access until the last one closes the file. - *
- * - * @see #delete - */ - public void deleteOnExit() { - if (isInvalid()) { - return; - } - AmazeDeleteOnExitHook.add(path); - } - - /** - * Returns an array of strings naming the files and directories in the directory denoted by this - * abstract pathname. - * - *

If this abstract pathname does not denote a directory, then this method returns {@code - * null}. Otherwise an array of strings is returned, one for each file or directory in the - * directory. Names denoting the directory itself and the directory's parent directory are not - * included in the result. Each string is a file name rather than a complete path. - * - *

There is no guarantee that the name strings in the resulting array will appear in any - * specific order; they are not, in particular, guaranteed to appear in alphabetical order. - * - *

Note that the {@link java.nio.file.Files} class defines the {@link - * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream} method to open a directory and - * iterate over the names of the files in the directory. This may use less resources when working - * with very large directories, and may be more responsive when working with remote directories. - * - * @return An array of strings naming the files and directories in the directory denoted by this - * abstract pathname. The array will be empty if the directory is empty. Returns {@code null} - * if this abstract pathname does not denote a directory, or if an I/O error occurs. - */ - @Nullable - public String[] list(@NonNull ContextProvider contextProvider) { - if (isInvalid()) { - return null; - } - return fs.list(this, contextProvider); - } - - /** - * Returns an array of strings naming the files and directories in the directory denoted by this - * abstract pathname that satisfy the specified filter. The behavior of this method is the same as - * that of the {@link #list()} method, except that the strings in the returned array must satisfy - * the filter. If the given {@code filter} is {@code null} then all names are accepted. Otherwise, - * a name satisfies the filter if and only if the value {@code true} results when the {@link - * AmazeFilenameFilter#accept AmazeFilenameFilter.accept(File, String)} method of the filter - * is invoked on this abstract pathname and the name of a file or directory in the directory that - * it denotes. - * - * @param filter A filename filter - * @return An array of strings naming the files and directories in the directory denoted by this - * abstract pathname that were accepted by the given {@code filter}. The array will be empty - * if the directory is empty or if no names were accepted by the filter. Returns {@code null} - * if this abstract pathname does not denote a directory, or if an I/O error occurs. - * @see java.nio.file.Files#newDirectoryStream(Path,String) - */ - @Nullable - public String[] list(AmazeFilenameFilter filter, @NonNull ContextProvider contextProvider) { - String names[] = list(contextProvider); - if ((names == null) || (filter == null)) { - return names; - } - List v = new ArrayList<>(); - for (int i = 0; i < names.length; i++) { - if (filter.accept(this, names[i])) { - v.add(names[i]); - } - } - return v.toArray(new String[v.size()]); - } - - /** - * Returns an array of abstract pathnames denoting the files in the directory denoted by this - * abstract pathname. - * - *

If this abstract pathname does not denote a directory, then this method returns {@code - * null}. Otherwise an array of {@code File} objects is returned, one for each file or directory - * in the directory. Pathnames denoting the directory itself and the directory's parent directory - * are not included in the result. Each resulting abstract pathname is constructed from this - * abstract pathname using the {@link #File(File, String) File(File, String)} constructor. - * Therefore if this pathname is absolute then each resulting pathname is absolute; if this - * pathname is relative then each resulting pathname will be relative to the same directory. - * - *

There is no guarantee that the name strings in the resulting array will appear in any - * specific order; they are not, in particular, guaranteed to appear in alphabetical order. - * - *

Note that the {@link java.nio.file.Files} class defines the {@link - * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream} method to open a directory and - * iterate over the names of the files in the directory. This may use less resources when working - * with very large directories. - * - * @return An array of abstract pathnames denoting the files and directories in the directory - * denoted by this abstract pathname. The array will be empty if the directory is empty. - * Returns {@code null} if this abstract pathname does not denote a directory, or if an I/O - * error occurs. - */ - @Nullable - public AmazeFile[] listFiles(@NonNull ContextProvider contextProvider) { - String[] ss = list(contextProvider); - if (ss == null) return null; - int n = ss.length; - AmazeFile[] fs = new AmazeFile[n]; - for (int i = 0; i < n; i++) { - fs[i] = new AmazeFile(ss[i], this); - } - return fs; - } - - /** - * Returns an array of abstract pathnames denoting the files and directories in the directory - * denoted by this abstract pathname that satisfy the specified filter. The behavior of this - * method is the same as that of the {@link #listFiles()} method, except that the pathnames in the - * returned array must satisfy the filter. If the given {@code filter} is {@code null} then all - * pathnames are accepted. Otherwise, a pathname satisfies the filter if and only if the value - * {@code true} results when the {@link AmazeFilenameFilter#accept - * AmazeFilenameFilter.accept(File, String)} method of the filter is invoked on this abstract - * pathname and the name of a file or directory in the directory that it denotes. - * - * @param filter A filename filter - * @return An array of abstract pathnames denoting the files and directories in the directory - * denoted by this abstract pathname. The array will be empty if the directory is empty. - * Returns {@code null} if this abstract pathname does not denote a directory, or if an I/O - * error occurs. - * @see java.nio.file.Files#newDirectoryStream(Path,String) - */ - @Nullable - public AmazeFile[] listFiles( - AmazeFilenameFilter filter, @NonNull ContextProvider contextProvider) { - String ss[] = list(contextProvider); - if (ss == null) return null; - ArrayList files = new ArrayList<>(); - for (String s : ss) - if ((filter == null) || filter.accept(this, s)) files.add(new AmazeFile(s, this)); - return files.toArray(new AmazeFile[files.size()]); - } - - /** - * Returns an array of abstract pathnames denoting the files and directories in the directory - * denoted by this abstract pathname that satisfy the specified filter. The behavior of this - * method is the same as that of the {@link #listFiles()} method, except that the pathnames in the - * returned array must satisfy the filter. If the given {@code filter} is {@code null} then all - * pathnames are accepted. Otherwise, a pathname satisfies the filter if and only if the value - * {@code true} results when the {@link FileFilter#accept FileFilter.accept(File)} method of the - * filter is invoked on the pathname. - * - * @param filter A file filter - * @return An array of abstract pathnames denoting the files and directories in the directory - * denoted by this abstract pathname. The array will be empty if the directory is empty. - * Returns {@code null} if this abstract pathname does not denote a directory, or if an I/O - * error occurs. - * @see java.nio.file.Files#newDirectoryStream(Path,java.nio.file.DirectoryStream.Filter) - */ - @Nullable - public AmazeFile[] listFiles(AmazeFileFilter filter, @NonNull ContextProvider contextProvider) { - String ss[] = list(contextProvider); - if (ss == null) return null; - ArrayList files = new ArrayList<>(); - for (String s : ss) { - AmazeFile f = new AmazeFile(s, this); - if ((filter == null) || filter.accept(f)) files.add(f); - } - return files.toArray(new AmazeFile[files.size()]); - } - - @NonNull - public InputStream getInputStream(@NonNull ContextProvider contextProvider) { - return fs.getInputStream(this, contextProvider); - } - - @NonNull - public OutputStream getOutputStream(@NonNull ContextProvider contextProvider) { - return fs.getOutputStream(this, contextProvider); - } - - /** - * Creates the directory named by this abstract pathname. - * - * @return true if and only if the directory was created; false - * otherwise - */ - public boolean mkdir(@NonNull ContextProvider contextProvider) { - if (isInvalid()) { - return false; - } - return fs.createDirectory(this, contextProvider); - } - - /** - * Creates the directory named by this abstract pathname, including any necessary but nonexistent - * parent directories. Note that if this operation fails it may have succeeded in creating some of - * the necessary parent directories. - * - * @return true if and only if the directory was created, along with all necessary - * parent directories; false otherwise - */ - public boolean mkdirs(@NonNull ContextProvider contextProvider) { - if (exists(contextProvider)) { - return false; - } - if (mkdir(contextProvider)) { - return true; - } - AmazeFile canonFile = null; - try { - canonFile = getCanonicalFile(); - } catch (IOException e) { - return false; - } - - AmazeFile parent = canonFile.getParentFile(); - return (parent != null - && (parent.mkdirs(contextProvider) || parent.exists(contextProvider)) - && canonFile.mkdir(contextProvider)); - } - - // Android-changed: Replaced generic platform info with Android specific one. - /** - * Renames the file denoted by this abstract pathname. - * - *

Many failures are possible. Some of the more likely failures include: - * - *

    - *
  • Write permission is required on the directories containing both the source and - * destination paths. - *
  • Search permission is required for all parents of both paths. - *
  • Both paths be on the same mount point. On Android, applications are most likely to hit - * this restriction when attempting to copy between internal storage and an SD card. - *
- * - *

The return value should always be checked to make sure that the rename operation was - * successful. - * - *

Note that the {@link java.nio.file.Files} class defines the {@link java.nio.file.Files#move - * move} method to move or rename a file in a platform independent manner. - * - * @param dest The new abstract pathname for the named file - * @return true if and only if the renaming succeeded; false otherwise - */ - public boolean renameTo(@NonNull AmazeFile dest, @NonNull ContextProvider contextProvider) { - if (this.isInvalid() || dest.isInvalid()) { - return false; - } - return fs.rename(this, dest, contextProvider); - } - - /** - * Sets the last-modified time of the file or directory named by this abstract pathname. - * - *

All platforms support file-modification times to the nearest second, but some provide more - * precision. The argument will be truncated to fit the supported precision. If the operation - * succeeds and no intervening operations on the file take place, then the next invocation of the - * {@link #lastModified} method will return the (possibly truncated) time - * argument that was passed to this method. - * - * @param time The new last-modified time, measured in milliseconds since the epoch (00:00:00 GMT, - * January 1, 1970) - * @return true if and only if the operation succeeded; false otherwise - * @throws IllegalArgumentException If the argument is negative - */ - public boolean setLastModified(long time) { - if (time < 0) throw new IllegalArgumentException("Negative time"); - if (isInvalid()) { - return false; - } - return fs.setLastModifiedTime(this, time); - } - - // Android-changed. Removed javadoc comment about special privileges - // that doesn't make sense on Android. - /** - * Marks the file or directory named by this abstract pathname so that only read operations are - * allowed. After invoking this method the file or directory will not change until it is either - * deleted or marked to allow write access. Whether or not a read-only file or directory may be - * deleted depends upon the underlying system. - * - * @return true if and only if the operation succeeded; false otherwise - */ - public boolean setReadOnly() { - if (isInvalid()) { - return false; - } - return fs.setReadOnly(this); - } - - // Android-changed. Removed javadoc comment about special privileges - // that doesn't make sense on Android. - /** - * Sets the owner's or everybody's write permission for this abstract pathname. - * - *

The {@link java.nio.file.Files} class defines methods that operate on file attributes - * including file permissions. This may be used when finer manipulation of file permissions is - * required. - * - * @param writable If true, sets the access permission to allow write operations; if - * false to disallow write operations - * @param ownerOnly If true, the write permission applies only to the owner's write - * permission; otherwise, it applies to everybody. If the underlying file system can not - * distinguish the owner's write permission from that of others, then the permission will - * apply to everybody, regardless of this value. - * @return true if and only if the operation succeeded. The operation will fail if - * the user does not have permission to change the access permissions of this abstract - * pathname. - */ - public boolean setWritable(boolean writable, boolean ownerOnly) { - if (isInvalid()) { - return false; - } - return fs.setWritable(this, writable, ownerOnly); - } - - // Android-changed. Removed javadoc comment about special privileges - // that doesn't make sense on Android. - /** - * A convenience method to set the owner's write permission for this abstract pathname. - * - *

An invocation of this method of the form file.setWritable(arg) behaves in exactly - * the same way as the invocation - * - *

-   *     file.setWritable(arg, true) 
- * - * @param writable If true, sets the access permission to allow write operations; if - * false to disallow write operations - * @return true if and only if the operation succeeded. The operation will fail if - * the user does not have permission to change the access permissions of this abstract - * pathname. - */ - public boolean setWritable(boolean writable) { - return setWritable(writable, true); - } - - // Android-changed. Removed javadoc comment about special privileges - // that doesn't make sense on Android. - /** - * Sets the owner's or everybody's read permission for this abstract pathname. - * - *

The {@link java.nio.file.Files} class defines methods that operate on file attributes - * including file permissions. This may be used when finer manipulation of file permissions is - * required. - * - * @param readable If true, sets the access permission to allow read operations; if - * false to disallow read operations - * @param ownerOnly If true, the read permission applies only to the owner's read - * permission; otherwise, it applies to everybody. If the underlying file system can not - * distinguish the owner's read permission from that of others, then the permission will apply - * to everybody, regardless of this value. - * @return true if and only if the operation succeeded. The operation will fail if - * the user does not have permission to change the access permissions of this abstract - * pathname. If readable is false and the underlying file system - * does not implement a read permission, then the operation will fail. - */ - public boolean setReadable(boolean readable, boolean ownerOnly) { - if (isInvalid()) { - return false; - } - return fs.setReadable(this, readable, ownerOnly); - } - - // Android-changed. Removed javadoc comment about special privileges - // that doesn't make sense on Android. - /** - * A convenience method to set the owner's read permission for this abstract pathname. - * - *

An invocation of this method of the form file.setReadable(arg) behaves in exactly - * the same way as the invocation - * - *

-   *     file.setReadable(arg, true) 
- * - * @param readable If true, sets the access permission to allow read operations; if - * false to disallow read operations - * @return true if and only if the operation succeeded. The operation will fail if - * the user does not have permission to change the access permissions of this abstract - * pathname. If readable is false and the underlying file system - * does not implement a read permission, then the operation will fail. - */ - public boolean setReadable(boolean readable) { - return setReadable(readable, true); - } - - // Android-changed. Removed javadoc comment about special privileges - // that doesn't make sense on Android. - /** - * Sets the owner's or everybody's execute permission for this abstract pathname. - * - *

The {@link java.nio.file.Files} class defines methods that operate on file attributes - * including file permissions. This may be used when finer manipulation of file permissions is - * required. - * - * @param executable If true, sets the access permission to allow execute operations; - * if false to disallow execute operations - * @param ownerOnly If true, the execute permission applies only to the owner's - * execute permission; otherwise, it applies to everybody. If the underlying file system can - * not distinguish the owner's execute permission from that of others, then the permission - * will apply to everybody, regardless of this value. - * @return true if and only if the operation succeeded. The operation will fail if - * the user does not have permission to change the access permissions of this abstract - * pathname. If executable is false and the underlying file system - * does not implement an execute permission, then the operation will fail. - */ - public boolean setExecutable(boolean executable, boolean ownerOnly) { - if (isInvalid()) { - return false; - } - return fs.setExecutable(this, executable, ownerOnly); - } - - // Android-changed. Removed javadoc comment about special privileges - // that doesn't make sense on Android. - /** - * A convenience method to set the owner's execute permission for this abstract pathname. - * - *

An invocation of this method of the form file.setExcutable(arg) behaves in exactly - * the same way as the invocation - * - *

-   *     file.setExecutable(arg, true) 
- * - * @param executable If true, sets the access permission to allow execute operations; - * if false to disallow execute operations - * @return true if and only if the operation succeeded. The operation will fail if - * the user does not have permission to change the access permissions of this abstract - * pathname. If executable is false and the underlying file system - * does not implement an execute permission, then the operation will fail. - */ - public boolean setExecutable(boolean executable) { - return setExecutable(executable, true); - } - - // Android-changed. Removed javadoc comment about special privileges - // that doesn't make sense on Android. - /** - * Tests whether the application can execute the file denoted by this abstract pathname. - * - * @return true if and only if the abstract pathname exists and the - * application is allowed to execute the file - */ - public boolean canExecute(@NonNull ContextProvider contextProvider) { - if (isInvalid()) { - return false; - } - return fs.canExecute(this, contextProvider); - } - - /* -- Filesystem interface -- */ - - // Android-changed: Replaced generic platform info with Android specific one. - /** - * Returns the file system roots. On Android and other Unix systems, there is a single root, - * {@code /}. - */ - public AmazeFile[] listRoots() { - return fs.listRoots(); - } - - /* -- Disk usage -- */ - - /** - * Returns the size of the partition named by this abstract pathname. - * - * @return The size, in bytes, of the partition or 0L if this abstract pathname does not - * name a partition If there is no way to determine, total space is -1 - */ - public long getTotalSpace(@NonNull ContextProvider contextProvider) { - if (isInvalid()) { - return 0L; - } - try { - return fs.getTotalSpace(this, contextProvider); - } catch (NotImplementedError e) { - Log.w(TAG, "Call to unimplemented fuction", e); - return -1; - } - } - - /** - * Returns the number of unallocated bytes in the partition named by this - * abstract path name. - * - *

The returned number of unallocated bytes is a hint, but not a guarantee, that it is possible - * to use most or any of these bytes. The number of unallocated bytes is most likely to be - * accurate immediately after this call. It is likely to be made inaccurate by any external I/O - * operations including those made on the system outside of this virtual machine. This method - * makes no guarantee that write operations to this file system will succeed. - * - * @return The number of unallocated bytes on the partition or 0L if the abstract - * pathname does not name a partition. This value will be less than or equal to the total file - * system size returned by {@link #getTotalSpace}. - */ - public long getFreeSpace() { - if (isInvalid()) { - return 0L; - } - return fs.getFreeSpace(this); - } - - // Android-added: Replaced generic platform info with Android specific one. - /** - * Returns the number of bytes available to this virtual machine on the partition named by this abstract pathname. When possible, this method checks for - * write permissions and other operating system restrictions and will therefore usually provide a - * more accurate estimate of how much new data can actually be written than {@link #getFreeSpace}. - * - *

The returned number of available bytes is a hint, but not a guarantee, that it is possible - * to use most or any of these bytes. The number of unallocated bytes is most likely to be - * accurate immediately after this call. It is likely to be made inaccurate by any external I/O - * operations including those made on the system outside of this virtual machine. This method - * makes no guarantee that write operations to this file system will succeed. - * - *

On Android (and other Unix-based systems), this method returns the number of free bytes - * available to non-root users, regardless of whether you're actually running as root, and - * regardless of any quota or other restrictions that might apply to the user. (The {@code - * getFreeSpace} method returns the number of bytes potentially available to root.) - * - * @return The number of available bytes on the partition or 0L if the abstract pathname - * does not name a partition. On systems where this information is not available, this method - * will be equivalent to a call to {@link #getFreeSpace}. If there is no way to determine the - * current space left -1 is returned. - */ - public long getUsableSpace() { - if (isInvalid()) { - return 0L; - } - try { - return fs.getUsableSpace(this); - } catch (NotImplementedError e) { - Log.w(TAG, "Call to unimplemented fuction", e); - return -1; - } - } - - /* -- Temporary files -- */ - - private static class TempDirectory { - // file name generation - private static final SecureRandom random = new SecureRandom(); - - @NonNull - static AmazeFile generateFile(String prefix, String suffix, AmazeFile dir) throws IOException { - // Android-changed: Use Math.randomIntInternal. This (pseudo) random number - // is initialized post-fork - - long n = random.nextLong(); - if (n == Long.MIN_VALUE) { - n = 0; // corner case - } else { - n = Math.abs(n); - } - - // Android-changed: Reject invalid file prefixes - // Use only the file name from the supplied prefix - // prefix = (new AmazeFile(prefix)).getName(); - - String name = prefix + Long.toString(n) + suffix; - AmazeFile f = new AmazeFile(dir, name); - if (!name.equals(f.getName()) || f.isInvalid()) { - if (System.getSecurityManager() != null) - throw new IOException("Unable to create temporary file"); - else throw new IOException("Unable to create temporary file, " + f); - } - return f; - } - } - - /** - * Creates a new empty file in the specified directory, using the given prefix and suffix strings - * to generate its name. If this method returns successfully then it is guaranteed that: - * - *

    - *
  1. The file denoted by the returned abstract pathname did not exist before this method was - * invoked, and - *
  2. Neither this method nor any of its variants will return the same abstract pathname again - * in the current invocation of the virtual machine. - *
- * - * This method provides only part of a temporary-file facility. To arrange for a file created by - * this method to be deleted automatically, use the {@link #deleteOnExit} method. - * - *

The prefix argument must be at least three characters long. It is recommended - * that the prefix be a short, meaningful string such as "hjb" or "mail" - * . The suffix argument may be null, in which case the suffix - * ".tmp" will be used. - * - *

To create the new file, the prefix and the suffix may first be adjusted to fit the - * limitations of the underlying platform. If the prefix is too long then it will be truncated, - * but its first three characters will always be preserved. If the suffix is too long then it too - * will be truncated, but if it begins with a period character ('.') then the period - * and the first three characters following it will always be preserved. Once these adjustments - * have been made the name of the new file will be generated by concatenating the prefix, five or - * more internally-generated characters, and the suffix. - * - *

If the directory argument is null then the system-dependent - * default temporary-file directory will be used. The default temporary-file directory is - * specified by the system property java.io.tmpdir. On UNIX systems the default value - * of this property is typically "/tmp" or "/var/tmp"; on Microsoft - * Windows systems it is typically "C:\\WINNT\\TEMP". A different value may be given - * to this system property when the Java virtual machine is invoked, but programmatic changes to - * this property are not guaranteed to have any effect upon the temporary directory used by this - * method. - * - * @param prefix The prefix string to be used in generating the file's name; must be at least - * three characters long - * @param suffix The suffix string to be used in generating the file's name; may be null - * , in which case the suffix ".tmp" will be used - * @param directory The directory in which the file is to be created, or null if the - * default temporary-file directory is to be used - * @return An abstract pathname denoting a newly-created empty file - * @throws IllegalArgumentException If the prefix argument contains fewer than three - * characters - * @throws IOException If a file could not be created - */ - @NonNull - public AmazeFile createTempFile( - @NonNull ContextProvider contextProvider, - @NonNull String prefix, - @Nullable String suffix, - @Nullable AmazeFile directory) - throws IOException { - if (prefix.length() < 3) { - throw new IllegalArgumentException("Prefix string too short"); - } - - // Android-changed: Handle java.io.tmpdir changes. - AmazeFile tmpdir = - (directory != null) ? directory : new AmazeFile(System.getProperty("java.io.tmpdir", ".")); - AmazeFile f; - do { - f = TempDirectory.generateFile(prefix, suffix != null ? suffix : ".tmp", tmpdir); - } while ((fs.getBooleanAttributes(f, contextProvider) & AmazeFilesystem.BA_EXISTS) != 0); - - if (!fs.createFileExclusively(f.getPath())) - throw new IOException("Unable to create temporary file"); - - return f; - } - - /** - * Creates an empty file in the default temporary-file directory, using the given prefix and - * suffix to generate its name. Invoking this method is equivalent to invoking - * {@link #createTempFile(java.lang.String, - * java.lang.String, java.io.File) - * createTempFile(prefix, suffix, null)}. - * - *

The {@link - * java.nio.file.Files#createTempFile(String,String,java.nio.file.attribute.FileAttribute[]) - * Files.createTempFile} method provides an alternative method to create an empty file in the - * temporary-file directory. Files created by that method may have more restrictive access - * permissions to files created by this method and so may be more suited to security-sensitive - * applications. - * - * @param prefix The prefix string to be used in generating the file's name; must be at least - * three characters long - * @param suffix The suffix string to be used in generating the file's name; may be null - * , in which case the suffix ".tmp" will be used - * @return An abstract pathname denoting a newly-created empty file - * @throws IllegalArgumentException If the prefix argument contains fewer than three - * characters - * @throws IOException If a file could not be created - * @see java.nio.file.Files#createTempDirectory(String,FileAttribute[]) - */ - @NonNull - public AmazeFile createTempFile( - @NonNull ContextProvider contextProvider, String prefix, String suffix) throws IOException { - return createTempFile(contextProvider, prefix, suffix, null); - } - - /* -- Basic infrastructure -- */ - - /** - * Compares two abstract pathnames lexicographically. The ordering defined by this method depends - * upon the underlying system. On UNIX systems, alphabetic case is significant in comparing - * pathnames; on Microsoft Windows systems it is not. - * - * @param pathname The abstract pathname to be compared to this abstract pathname - * @return Zero if the argument is equal to this abstract pathname, a value less than zero if this - * abstract pathname is lexicographically less than the argument, or a value greater than zero - * if this abstract pathname is lexicographically greater than the argument - */ - public int compareTo(AmazeFile pathname) { - return fs.compare(this, pathname); - } - - /** - * Tests this abstract pathname for equality with the given object. Returns true if - * and only if the argument is not null and is an abstract pathname that denotes the - * same file or directory as this abstract pathname. Whether or not two abstract pathnames are - * equal depends upon the underlying system. On UNIX systems, alphabetic case is significant in - * comparing pathnames; on Microsoft Windows systems it is not. - * - * @param obj The object to be compared with this abstract pathname - * @return true if and only if the objects are the same; false otherwise - */ - public boolean equals(Object obj) { - if (obj instanceof AmazeFile) { - return compareTo((AmazeFile) obj) == 0; - } - return false; - } - - /** - * Computes a hash code for this abstract pathname. Because equality of abstract pathnames is - * inherently system-dependent, so is the computation of their hash codes. On UNIX systems, the - * hash code of an abstract pathname is equal to the exclusive or of the hash code of its - * pathname string and the decimal value 1234321. On Microsoft Windows systems, the - * hash code is equal to the exclusive or of the hash code of its pathname string - * converted to lower case and the decimal value 1234321. Locale is not taken into - * account on lowercasing the pathname string. - * - * @return A hash code for this abstract pathname - */ - public int hashCode() { - return fs.hashCode(this); - } - - /** - * Returns the pathname string of this abstract pathname. This is just the string returned by the - * {@link #getPath} method. - * - * @return The string form of this abstract pathname - */ - @NonNull - public String toString() { - return getPath(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(getPath()); - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public AmazeFile createFromParcel(Parcel in) { - return new AmazeFile(Objects.requireNonNull(in.readString())); - } - - public AmazeFile[] newArray(int size) { - return new AmazeFile[size]; - } - }; -} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt new file mode 100644 index 0000000000..ac954fb4e9 --- /dev/null +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -0,0 +1,1371 @@ +/* + * Copyright (C) 2014-2014 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes + +import android.os.Parcel +import android.os.Parcelable +import android.util.Log +import kotlinx.parcelize.Parceler +import java.io.File +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.SecureRandom +import java.util.* + +// Android-added: Info about UTF-8 usage in filenames. +/** + * An abstract representation of file and directory pathnames. + * + * + * User interfaces and operating systems use system-dependent *pathname strings* to name + * files and directories. This class presents an abstract, system-independent view of hierarchical + * pathnames. An *abstract pathname* has two components: + * + * + * 1. An optional system-dependent *prefix* string, such as a disk-drive specifier, ` + * "/"` for the UNIX root directory, or `"\\\\"` for a Microsoft + * Windows UNC pathname, and + * 1. A sequence of zero or more string *names*. + * + * + * The first name in an abstract pathname may be a directory name or, in the case of Microsoft + * Windows UNC pathnames, a hostname. Each subsequent name in an abstract pathname denotes a + * directory; the last name may denote either a directory or a file. The *empty* abstract + * pathname has no prefix and an empty name sequence. + * + * + * The conversion of a pathname string to or from an abstract pathname is inherently + * system-dependent. When an abstract pathname is converted into a pathname string, each name is + * separated from the next by a single copy of the default *separator character*. The default + * name-separator character is defined by the system property `file.separator`, and is + * made available in the public static fields `[ ][.separator]` and `[.separatorChar]` of this class. When a pathname string + * is converted into an abstract pathname, the names within it may be separated by the default + * name-separator character or by any other name-separator character that is supported by the + * underlying system. + * + * + * A pathname, whether abstract or in string form, may be either *absolute* or + * *relative*. An absolute pathname is complete in that no other information is required in + * order to locate the file that it denotes. A relative pathname, in contrast, must be interpreted + * in terms of information taken from some other pathname. By default the classes in the ` + * java.io` package always resolve relative pathnames against the current user directory. This + * directory is named by the system property `user.dir`, and is typically the directory + * in which the Java virtual machine was invoked. + * + * + * The *parent* of an abstract pathname may be obtained by invoking the [.getParent] + * method of this class and consists of the pathname's prefix and each name in the pathname's name + * sequence except for the last. Each directory's absolute pathname is an ancestor of any + * AmazeFile object with an absolute abstract pathname which begins with the directory's + * absolute pathname. For example, the directory denoted by the abstract pathname "/usr" is + * an ancestor of the directory denoted by the pathname "/usr/local/bin". + * + * + * The prefix concept is used to handle root directories on UNIX platforms, and drive specifiers, + * root directories and UNC pathnames on Microsoft Windows platforms, as follows: + * + * + * * For UNIX platforms, the prefix of an absolute pathname is always `"/"`. Relative + * pathnames have no prefix. The abstract pathname denoting the root directory has the prefix + * `"/"` and an empty name sequence. + * * For Microsoft Windows platforms, the prefix of a pathname that contains a drive specifier + * consists of the drive letter followed by `":"` and possibly followed by ` + * "\\"` if the pathname is absolute. The prefix of a UNC pathname is `"\\\\" +` * ; the hostname and the share name are the first two names in the name sequence. A + * relative pathname that does not specify a drive has no prefix. + * + * + * + * Instances of this class may or may not denote an actual file-system object such as a file or a + * directory. If it does denote such an object then that object resides in a *partition*. A + * partition is an operating system-specific portion of storage for a file system. A single storage + * device (e.g. a physical disk-drive, flash memory, CD-ROM) may contain multiple partitions. The + * object, if any, will reside on the partition named by some ancestor of the + * absolute form of this pathname. + * + * + * A file system may implement restrictions to certain operations on the actual file-system + * object, such as reading, writing, and executing. These restrictions are collectively known as + * *access permissions*. The file system may have multiple sets of access permissions on a + * single object. For example, one set may apply to the object's *owner*, and another may apply + * to all other users. The access permissions on an object may cause some methods in this class to + * fail. + * + * + * On Android strings are converted to UTF-8 byte sequences when sending filenames to the + * operating system, and byte sequences returned by the operating system (from the various `list` methods) are converted to strings by decoding them as UTF-8 byte sequences. + * + * @author unascribed + */ +class AmazeFile : Comparable { + companion object { + val TAG = AmazeFile::class.java.simpleName + private val filesystems: MutableList = ArrayList() + + @JvmStatic + fun addFilesystem(amazeFilesystem: AmazeFilesystem) { + filesystems.add(amazeFilesystem) + } + + private fun slashify(path: String, isDirectory: Boolean): String { + var p = path + if (File.separatorChar != '/') p = p.replace(File.separatorChar, '/') + if (!p.startsWith("/")) p = "/$p" + if (!p.endsWith("/") && isDirectory) p = "$p/" + return p + } + + object AmazeFileParceler : Parceler { + override fun create(parcel: Parcel): AmazeFile = + AmazeFile(parcel.readString() ?: "") + + override fun AmazeFile.write(parcel: Parcel, flags: Int) { + parcel.writeString(absolutePath) + } + } + } + + /** The FileSystem object representing the platform's local file system. */ + private lateinit var fs: AmazeFilesystem + + /** + * Converts this abstract pathname into a pathname string. The resulting string uses the [separator] + * to separate the names in the name sequence. + * + * This abstract pathname's normalized pathname string. A normalized pathname string uses the + * default name-separator character and does not contain any duplicate or redundant separators. + */ + val path: String + + /** The flag indicating whether the file path is invalid. */ + @Transient + private var status: PathStatus? = null + + /** Returns the length of this abstract pathname's prefix. For use by FileSystem classes. */ + /** The length of this abstract pathname's prefix, or zero if it has no prefix. */ + @Transient + val prefixLength: Int + + /** + * The system-dependent default name-separator character. This field is initialized to contain the + * first character of the value of the system property `file.separator`. On UNIX + * systems the value of this field is `'/'`; on Microsoft Windows systems it is ` + * '\\'`. + * + * @see java.lang.System.getProperty + */ + val separatorChar: Char + + /** + * The system-dependent default name-separator character, represented as a string for convenience. + * This string contains a single character, namely `[.separatorChar]`. + */ + val separator: String + + /** Enum type that indicates the status of a file path. */ + private enum class PathStatus { + INVALID, CHECKED + } + + /** + * Check if the file has an invalid path. Currently, the inspection of a file path is very + * limited, and it only covers Nul character check. Returning true means the path is definitely + * invalid/garbage. But returning false does not guarantee that the path is valid. + * + * @return true if the file path is invalid. + */ + val isInvalid: Boolean + get() { + if (status == null) { + status = if (path.indexOf('\u0000') < 0) PathStatus.CHECKED else PathStatus.INVALID + } + return status == PathStatus.INVALID + } + /* -- Constructors -- */ + /** Internal constructor for already-normalized pathname strings. */ + private constructor(pathname: String, prefixLength: Int) { + loadFilesystem(pathname) + separatorChar = fs.getSeparator() + separator = "" + separatorChar + path = pathname + this.prefixLength = prefixLength + } + + /** + * Internal constructor for already-normalized pathname strings. The parameter order is used to + * disambiguate this method from the public(AmazeFile, String) constructor. + */ + private constructor(child: String, parent: AmazeFile) { + assert(parent.path != "") + loadFilesystem(parent.path) + separatorChar = fs.getSeparator() + separator = "" + separatorChar + path = fs.resolve(parent.path, child) + prefixLength = parent.prefixLength + } + + /** + * Creates a new `AmazeFile` instance by converting the given pathname string into an + * abstract pathname. If the given string is the empty string, then the result is the empty + * abstract pathname. + * + * @param pathname A pathname string + */ + constructor(pathname: String) { + loadFilesystem(pathname) + separatorChar = fs.getSeparator() + separator = "" + separatorChar + path = fs.normalize(pathname) + prefixLength = fs.prefixLength(path) + } + + /** + * Note: The two-argument File constructors do not interpret an empty + * parent abstract pathname as the current user directory. An empty parent + * instead causes the child to be resolved against the system-dependent + * directory defined by the FileSystem.getDefaultParent method. On Unix + * this default is "/", while on Microsoft Windows it is "\\". This is required for + * compatibility with the original behavior of this class. + * + * Creates a new `AmazeFile` instance from a parent pathname string and a child + * pathname string. + * + * + * If `parent` is `null` then the new `AmazeFile` instance is + * created as if by invoking the single-argument `AmazeFile` constructor on the given + * `child` pathname string. + * + * + * Otherwise the `parent` pathname string is taken to denote a directory, and the + * `child` pathname string is taken to denote either a directory or a file. If the + * `child` pathname string is absolute then it is converted into a relative pathname in + * a system-dependent way. If `parent` is the empty string then the new `AmazeFile + ` * instance is created by converting `child` into an abstract pathname and + * resolving the result against a system-dependent default directory. Otherwise each pathname + * string is converted into an abstract pathname and the child abstract pathname is resolved + * against the parent. + * + * @param parent The parent pathname string + * @param child The child pathname string + */ + constructor(parent: String?, child: String) { + // BEGIN Android-changed: b/25859957, app-compat; don't substitute empty parent. + if (parent != null && !parent.isEmpty()) { + loadFilesystem(parent) + separatorChar = fs.getSeparator() + separator = "" + separatorChar + path = fs.resolve(fs.normalize(parent), fs.normalize(child)) + // END Android-changed: b/25859957, app-compat; don't substitute empty parent. + } else { + loadFilesystem(child) + separatorChar = fs.getSeparator() + separator = "" + separatorChar + path = fs.normalize(child) + } + prefixLength = fs.prefixLength(path) + } + + /** + * Creates a new `AmazeFile` instance from a parent abstract pathname and a child + * pathname string. + * + * + * If `parent` is `null` then the new `AmazeFile` instance is + * created as if by invoking the single-argument `AmazeFile` constructor on the given + * `child` pathname string. + * + * + * Otherwise the `parent` abstract pathname is taken to denote a directory, and the + * `child` pathname string is taken to denote either a directory or a file. If the + * `child` pathname string is absolute then it is converted into a relative pathname in + * a system-dependent way. If `parent` is the empty abstract pathname then the new + * `AmazeFile` instance is created by converting `child` into an abstract + * pathname and resolving the result against a system-dependent default directory. Otherwise each + * pathname string is converted into an abstract pathname and the child abstract pathname is + * resolved against the parent. + * + * @param parent The parent abstract pathname + * @param child The child pathname string + */ + constructor(parent: AmazeFile?, child: String) { + if (parent != null) { + loadFilesystem(parent.path) + separatorChar = fs.getSeparator() + separator = "" + separatorChar + if (parent.path == "") { + path = fs.resolve(fs.defaultParent, fs.normalize(child)) + } else { + path = fs.resolve(parent.path, fs.normalize(child)) + } + } else { + loadFilesystem(child) + separatorChar = fs.getSeparator() + separator = "" + separatorChar + path = fs.normalize(child) + } + prefixLength = fs.prefixLength(path) + } + + private fun loadFilesystem(path: String) { + for (filesystem in filesystems) { + if (filesystem.isPathOfThisFilesystem(path)) { + fs = filesystem + } + } + } + /* -- Path-component accessors -- */ + /** + * Returns the name of the file or directory denoted by this abstract pathname. This is just the + * last name in the pathname's name sequence. If the pathname's name sequence is empty, then the + * empty string is returned. + * + * @return The name of the file or directory denoted by this abstract pathname, or the empty + * string if this pathname's name sequence is empty + */ + val name: String + get() { + val index = path.lastIndexOf(separatorChar) + if (index < prefixLength) { + return path.substring(prefixLength) + } + if (path.endsWith("/")) { + val newIndex = path.substring(0, path.length - 2).lastIndexOf(separatorChar) + return if (newIndex < prefixLength) { + path.substring(prefixLength) + } else path.substring(newIndex + 1) + } + return path.substring(index + 1) + } + + /** + * Returns the pathname string of this abstract pathname's parent, or `null` if this + * pathname does not name a parent directory. + * + * + * The *parent* of an abstract pathname consists of the pathname's prefix, if any, and + * each name in the pathname's name sequence except for the last. If the name sequence is empty + * then the pathname does not name a parent directory. + * + * @return The pathname string of the parent directory named by this abstract pathname, or ` + * null` if this pathname does not name a parent + */ + val parent: String? + get() { + val index = path.lastIndexOf(separatorChar) + if (index < prefixLength) { + return if (prefixLength > 0 && path.length > prefixLength) { + path.substring(0, prefixLength) + } else null + } + if (path.endsWith("/")) { + val newIndex = path.substring(0, path.length - 2).lastIndexOf(separatorChar) + return if (newIndex < prefixLength) { + if (prefixLength > 0 && path.length > prefixLength) { + path.substring(0, prefixLength) + } else null + } else path.substring(0, newIndex) + } + return path.substring(0, index) + } + + /** + * Returns the abstract pathname of this abstract pathname's parent, or `null` if this + * pathname does not name a parent directory. + * + * + * The *parent* of an abstract pathname consists of the pathname's prefix, if any, and + * each name in the pathname's name sequence except for the last. If the name sequence is empty + * then the pathname does not name a parent directory. + * + * @return The abstract pathname of the parent directory named by this abstract pathname, or + * `null` if this pathname does not name a parent + */ + val parentFile: AmazeFile? + get() { + val p = parent ?: return null + return AmazeFile(p, prefixLength) + } + /* -- Path operations -- */ // Android-changed: Android-specific path information + /** + * Tests whether this abstract pathname is absolute. The definition of absolute pathname is system + * dependent. On Android, absolute paths start with the character '/'. + * + * @return `true` if this abstract pathname is absolute, `false` otherwise + */ + val isAbsolute: Boolean + get() = fs.isAbsolute(this) + // Android-changed: Android-specific path information + /** + * Returns the absolute path of this file. An absolute path is a path that starts at a root of the + * file system. On Android, there is only one root: `/`. + * + * + * A common use for absolute paths is when passing paths to a `Process` as command-line + * arguments, to remove the requirement implied by relative paths, that the child must have the + * same working directory as its parent. + * + * @return The absolute pathname string denoting the same file or directory as this abstract + * pathname + * @see java.io.File.isAbsolute + */ + val absolutePath: String + get() = fs.resolve(this) + + /** + * Returns the absolute form of this abstract pathname. Equivalent to ` + * new File(this.[.getAbsolutePath])`. + * + * @return The absolute abstract pathname denoting the same file or directory as this abstract + * pathname + */ + val absoluteFile: AmazeFile + get() { + val absPath = absolutePath + return AmazeFile(absPath, fs.prefixLength(absPath)) + } + + /** + * Returns the canonical pathname string of this abstract pathname. + * + * + * A canonical pathname is both absolute and unique. The precise definition of canonical form + * is system-dependent. This method first converts this pathname to absolute form if necessary, as + * if by invoking the [.getAbsolutePath] method, and then maps it to its unique form in a + * system-dependent way. This typically involves removing redundant names such as "." and + * ".." from the pathname, resolving symbolic links (on UNIX platforms), and converting + * drive letters to a standard case (on Microsoft Windows platforms). + * + * + * Every pathname that denotes an existing file or directory has a unique canonical form. Every + * pathname that denotes a nonexistent file or directory also has a unique canonical form. The + * canonical form of the pathname of a nonexistent file or directory may be different from the + * canonical form of the same pathname after the file or directory is created. Similarly, the + * canonical form of the pathname of an existing file or directory may be different from the + * canonical form of the same pathname after the file or directory is deleted. + * + * @return The canonical pathname string denoting the same file or directory as this abstract + * pathname + * @throws IOException If an I/O error occurs, which is possible because the construction of the + * canonical pathname may require filesystem queries + * @see Path.toRealPath + */ + @get:Throws(IOException::class) + val canonicalPath: String + get() { + if (isInvalid) { + throw IOException("Invalid file path") + } + return fs.canonicalize(fs.resolve(this)) + } + + /** + * Returns the canonical form of this abstract pathname. Equivalent to ` + * new File(this.[.getCanonicalPath])`. + * + * @return The canonical pathname string denoting the same file or directory as this abstract + * pathname + * @throws IOException If an I/O error occurs, which is possible because the construction of the + * canonical pathname may require filesystem queries + * @see Path.toRealPath + */ + @get:Throws(IOException::class) + val canonicalFile: AmazeFile + get() { + val canonPath = canonicalPath + return AmazeFile(canonPath, fs.prefixLength(canonPath)) + } + /* -- Attribute accessors -- */ // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on android + /** + * Tests whether the application can read the file denoted by this abstract pathname. + * + * @return `true` if and only if the file specified by this abstract pathname exists + * *and* can be read by the application; `false` otherwise + */ + fun canRead(contextProvider: ContextProvider): Boolean { + return if (isInvalid) { + false + } else fs.canRead(this, contextProvider) + } + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on android + /** + * Tests whether the application can modify the file denoted by this abstract pathname. + * + * @return `true` if and only if the file system actually contains a file denoted by + * this abstract pathname *and* the application is allowed to write to the file; ` + * false` otherwise. + */ + fun canWrite(contextProvider: ContextProvider): Boolean { + return if (isInvalid) { + false + } else fs.canWrite(this, contextProvider) + } + + /** + * Tests whether the file or directory denoted by this abstract pathname exists. + * + * @return `true` if and only if the file or directory denoted by this abstract + * pathname exists; `false` otherwise + */ + fun exists(contextProvider: ContextProvider): Boolean { + return if (isInvalid) { + false + } else fs.canAccess(this, contextProvider) + + // Android-changed: b/25878034 work around SELinux stat64 denial. + } + + /** + * Tests whether the file denoted by this abstract pathname is a directory. + * + * + * Where it is required to distinguish an I/O exception from the case that the file is not a + * directory, or where several attributes of the same file are required at the same time, then the + * [Files.readAttributes][java.nio.file.Files.readAttributes] method + * may be used. + * + * @return `true` if and only if the file denoted by this abstract pathname exists + * *and* is a directory; `false` otherwise + */ + fun isDirectory(contextProvider: ContextProvider): Boolean { + return if (isInvalid) { + false + } else fs.getBooleanAttributes(this, contextProvider) and AmazeFilesystem.BA_DIRECTORY != 0 + } + + /** + * Tests whether the file denoted by this abstract pathname is a normal file. A file is + * *normal* if it is not a directory and, in addition, satisfies other system-dependent + * criteria. Any non-directory file created by a Java application is guaranteed to be a normal + * file. + * + * + * Where it is required to distinguish an I/O exception from the case that the file is not a + * normal file, or where several attributes of the same file are required at the same time, then + * the [Files.readAttributes][java.nio.file.Files.readAttributes] + * method may be used. + * + * @return `true` if and only if the file denoted by this abstract pathname exists + * *and* is a normal file; `false` otherwise + */ + fun isFile(contextProvider: ContextProvider): Boolean { + return if (isInvalid) { + false + } else fs.getBooleanAttributes(this, contextProvider) and AmazeFilesystem.BA_REGULAR != 0 + } + + /** + * Tests whether the file named by this abstract pathname is a hidden file. The exact definition + * of *hidden* is system-dependent. On UNIX systems, a file is considered to be hidden if + * its name begins with a period character (`'.'`). On Microsoft Windows systems, a + * file is considered to be hidden if it has been marked as such in the filesystem. + * + * @return `true` if and only if the file denoted by this abstract pathname is hidden + * according to the conventions of the underlying platform + */ + fun isHidden(contextProvider: ContextProvider): Boolean { + return if (isInvalid) { + false + } else fs.getBooleanAttributes(this, contextProvider) and AmazeFilesystem.BA_HIDDEN != 0 + } + + /** + * Returns the time that the file denoted by this abstract pathname was last modified. + * + * + * Where it is required to distinguish an I/O exception from the case where `0L` is + * returned, or where several attributes of the same file are required at the same time, or where + * the time of last access or the creation time are required, then the [ ][java.nio.file.Files.readAttributes] method may be + * used. + * + * @return A `long` value representing the time the file was last modified, measured in + * milliseconds since the epoch (00:00:00 GMT, January 1, 1970), or `0L` if the + * file does not exist or if an I/O error occurs + */ + fun lastModified(): Long { + return if (isInvalid) { + 0L + } else fs.getLastModifiedTime(this) + } + + /** + * Returns the length of the file denoted by this abstract pathname. The return value is + * unspecified if this pathname denotes a directory. + * + * + * Where it is required to distinguish an I/O exception from the case that `0L` is + * returned, or where several attributes of the same file are required at the same time, then the + * [Files.readAttributes][java.nio.file.Files.readAttributes] method + * may be used. + * + * @return The length, in bytes, of the file denoted by this abstract pathname, or `0L` + * if the file does not exist. Some operating systems may return `0L` for pathnames + * denoting system-dependent entities such as devices or pipes. + */ + @Throws(IOException::class) + fun length(contextProvider: ContextProvider): Long { + return if (isInvalid) { + 0L + } else fs.getLength(this, contextProvider) + } + /* -- File operations -- */ + /** + * Atomically creates a new, empty file named by this abstract pathname if and only if a file with + * this name does not yet exist. The check for the existence of the file and the creation of the + * file if it does not exist are a single operation that is atomic with respect to all other + * filesystem activities that might affect the file. + * + * + * Note: this method should *not* be used for file-locking, as the resulting protocol + * cannot be made to work reliably. The [FileLock][java.nio.channels.FileLock] facility + * should be used instead. + * + * @return `true` if the named file does not exist and was successfully created; ` + * false` if the named file already exists + * @throws IOException If an I/O error occurred + */ + @Throws(IOException::class) + fun createNewFile(): Boolean { + if (isInvalid) { + throw IOException("Invalid file path") + } + return fs.createFileExclusively(path) + } + + /** + * Deletes the file or directory denoted by this abstract pathname. + * + * + * Note that the [java.nio.file.Files] class defines the [ ][java.nio.file.Files.delete] method to throw an [IOException] when a file + * cannot be deleted. This is useful for error reporting and to diagnose why a file cannot be + * deleted. + * + * @return `true` if and only if the file or directory is successfully deleted; ` + * false` otherwise + */ + fun delete(contextProvider: ContextProvider): Boolean { + return if (isInvalid) { + false + } else fs.delete(this, contextProvider) + } + // Android-added: Additional information about Android behaviour. + /** + * Requests that the file or directory denoted by this abstract pathname be deleted when the + * virtual machine terminates. Files (or directories) are deleted in the reverse order that they + * are registered. Invoking this method to delete a file or directory that is already registered + * for deletion has no effect. Deletion will be attempted only for normal termination of the + * virtual machine, as defined by the Java Language Specification. + * + * + * Once deletion has been requested, it is not possible to cancel the request. This method + * should therefore be used with care. + * + * + * Note: this method should *not* be used for file-locking, as the resulting protocol + * cannot be made to work reliably. The [FileLock][java.nio.channels.FileLock] facility + * should be used instead. + * + * + * *Note that on Android, the application lifecycle does not include VM termination, so + * calling this method will not ensure that files are deleted*. Instead, you should use the + * most appropriate out of: + * + * + * * Use a `finally` clause to manually invoke [.delete]. + * * Maintain your own set of files to delete, and process it at an appropriate point in your + * application's lifecycle. + * * Use the Unix trick of deleting the file as soon as all readers and writers have opened + * it. No new readers/writers will be able to access the file, but all existing ones will + * still have access until the last one closes the file. + * + * + * @see .delete + */ + fun deleteOnExit() { + if (isInvalid) { + return + } + AmazeDeleteOnExitHook.add(path) + } + + /** + * Returns an array of strings naming the files and directories in the directory denoted by this + * abstract pathname. + * + * + * If this abstract pathname does not denote a directory, then this method returns `null`. Otherwise an array of strings is returned, one for each file or directory in the + * directory. Names denoting the directory itself and the directory's parent directory are not + * included in the result. Each string is a file name rather than a complete path. + * + * + * There is no guarantee that the name strings in the resulting array will appear in any + * specific order; they are not, in particular, guaranteed to appear in alphabetical order. + * + * + * Note that the [java.nio.file.Files] class defines the [ ][java.nio.file.Files.newDirectoryStream] method to open a directory and + * iterate over the names of the files in the directory. This may use less resources when working + * with very large directories, and may be more responsive when working with remote directories. + * + * @return An array of strings naming the files and directories in the directory denoted by this + * abstract pathname. The array will be empty if the directory is empty. Returns `null` + * if this abstract pathname does not denote a directory, or if an I/O error occurs. + */ + fun list(contextProvider: ContextProvider): Array? { + return if (isInvalid) { + null + } else fs.list(this, contextProvider) + } + + /** + * Returns an array of strings naming the files and directories in the directory denoted by this + * abstract pathname that satisfy the specified filter. The behavior of this method is the same as + * that of the [.list] method, except that the strings in the returned array must satisfy + * the filter. If the given `filter` is `null` then all names are accepted. Otherwise, + * a name satisfies the filter if and only if the value `true` results when the [ ][AmazeFilenameFilter.accept] method of the filter + * is invoked on this abstract pathname and the name of a file or directory in the directory that + * it denotes. + * + * @param filter A filename filter + * @return An array of strings naming the files and directories in the directory denoted by this + * abstract pathname that were accepted by the given `filter`. The array will be empty + * if the directory is empty or if no names were accepted by the filter. Returns `null` + * if this abstract pathname does not denote a directory, or if an I/O error occurs. + * @see java.nio.file.Files.newDirectoryStream + */ + fun list(filter: AmazeFilenameFilter?, contextProvider: ContextProvider): Array? { + val names = list(contextProvider) + if (names == null || filter == null) { + return names + } + val v: MutableList = ArrayList() + for (i in names.indices) { + if (filter.accept(this, names[i])) { + v.add(names[i]) + } + } + return v.toTypedArray() + } + + /** + * Returns an array of abstract pathnames denoting the files in the directory denoted by this + * abstract pathname. + * + * + * If this abstract pathname does not denote a directory, then this method returns `null`. Otherwise an array of `File` objects is returned, one for each file or directory + * in the directory. Pathnames denoting the directory itself and the directory's parent directory + * are not included in the result. Each resulting abstract pathname is constructed from this + * abstract pathname using the [File(File,&nbsp;String)][.File] constructor. + * Therefore if this pathname is absolute then each resulting pathname is absolute; if this + * pathname is relative then each resulting pathname will be relative to the same directory. + * + * + * There is no guarantee that the name strings in the resulting array will appear in any + * specific order; they are not, in particular, guaranteed to appear in alphabetical order. + * + * + * Note that the [java.nio.file.Files] class defines the [ ][java.nio.file.Files.newDirectoryStream] method to open a directory and + * iterate over the names of the files in the directory. This may use less resources when working + * with very large directories. + * + * @return An array of abstract pathnames denoting the files and directories in the directory + * denoted by this abstract pathname. The array will be empty if the directory is empty. + * Returns `null` if this abstract pathname does not denote a directory, or if an I/O + * error occurs. + */ + fun listFiles(contextProvider: ContextProvider): Array? { + val ss = list(contextProvider) ?: return null + return Array(ss.size) { i: Int -> + AmazeFile(ss[i], this) + } + } + + /** + * Returns an array of abstract pathnames denoting the files and directories in the directory + * denoted by this abstract pathname that satisfy the specified filter. The behavior of this + * method is the same as that of the [.listFiles] method, except that the pathnames in the + * returned array must satisfy the filter. If the given `filter` is `null` then all + * pathnames are accepted. Otherwise, a pathname satisfies the filter if and only if the value + * `true` results when the [ AmazeFilenameFilter.accept(File,&nbsp;String)][AmazeFilenameFilter.accept] method of the filter is invoked on this abstract + * pathname and the name of a file or directory in the directory that it denotes. + * + * @param filter A filename filter + * @return An array of abstract pathnames denoting the files and directories in the directory + * denoted by this abstract pathname. The array will be empty if the directory is empty. + * Returns `null` if this abstract pathname does not denote a directory, or if an I/O + * error occurs. + * @see java.nio.file.Files.newDirectoryStream + */ + fun listFiles( + filter: AmazeFilenameFilter?, contextProvider: ContextProvider): Array? { + val ss = list(contextProvider) ?: return null + val files = ArrayList() + for (s in ss) if (filter == null || filter.accept(this, s)) files.add(AmazeFile(s!!, this)) + return files.toTypedArray() + } + + /** + * Returns an array of abstract pathnames denoting the files and directories in the directory + * denoted by this abstract pathname that satisfy the specified filter. The behavior of this + * method is the same as that of the [.listFiles] method, except that the pathnames in the + * returned array must satisfy the filter. If the given `filter` is `null` then all + * pathnames are accepted. Otherwise, a pathname satisfies the filter if and only if the value + * `true` results when the [FileFilter.accept(File)][FileFilter.accept] method of the + * filter is invoked on the pathname. + * + * @param filter A file filter + * @return An array of abstract pathnames denoting the files and directories in the directory + * denoted by this abstract pathname. The array will be empty if the directory is empty. + * Returns `null` if this abstract pathname does not denote a directory, or if an I/O + * error occurs. + * @see java.nio.file.Files.newDirectoryStream + */ + fun listFiles(filter: AmazeFileFilter?, contextProvider: ContextProvider): Array? { + val ss = list(contextProvider) ?: return null + val files = ArrayList() + for (s in ss) { + val f = AmazeFile(s, this) + if (filter == null || filter.accept(f)) files.add(f) + } + return files.toTypedArray() + } + + fun getInputStream(contextProvider: ContextProvider): InputStream { + return fs.getInputStream(this, contextProvider)!! + } + + fun getOutputStream(contextProvider: ContextProvider): OutputStream { + return fs.getOutputStream(this, contextProvider)!! + } + + /** + * Creates the directory named by this abstract pathname. + * + * @return `true` if and only if the directory was created; `false` + * otherwise + */ + fun mkdir(contextProvider: ContextProvider): Boolean { + return if (isInvalid) { + false + } else fs.createDirectory(this, contextProvider) + } + + /** + * Creates the directory named by this abstract pathname, including any necessary but nonexistent + * parent directories. Note that if this operation fails it may have succeeded in creating some of + * the necessary parent directories. + * + * @return `true` if and only if the directory was created, along with all necessary + * parent directories; `false` otherwise + */ + fun mkdirs(contextProvider: ContextProvider): Boolean { + if (exists(contextProvider)) { + return false + } + if (mkdir(contextProvider)) { + return true + } + val canonFile = try { + canonicalFile + } catch (e: IOException) { + return false + } + val parent = canonFile.parentFile + return (parent != null && (parent.mkdirs(contextProvider) || parent.exists(contextProvider)) + && canonFile.mkdir(contextProvider)) + } + // Android-changed: Replaced generic platform info with Android specific one. + /** + * Renames the file denoted by this abstract pathname. + * + * + * Many failures are possible. Some of the more likely failures include: + * + * + * * Write permission is required on the directories containing both the source and + * destination paths. + * * Search permission is required for all parents of both paths. + * * Both paths be on the same mount point. On Android, applications are most likely to hit + * this restriction when attempting to copy between internal storage and an SD card. + * + * + * + * The return value should always be checked to make sure that the rename operation was + * successful. + * + * + * Note that the [java.nio.file.Files] class defines the [ move][java.nio.file.Files.move] method to move or rename a file in a platform independent manner. + * + * @param dest The new abstract pathname for the named file + * @return `true` if and only if the renaming succeeded; `false` otherwise + */ + fun renameTo(dest: AmazeFile, contextProvider: ContextProvider): Boolean { + return if (isInvalid || dest.isInvalid) { + false + } else fs.rename(this, dest, contextProvider) + } + + /** + * Sets the last-modified time of the file or directory named by this abstract pathname. + * + * + * All platforms support file-modification times to the nearest second, but some provide more + * precision. The argument will be truncated to fit the supported precision. If the operation + * succeeds and no intervening operations on the file take place, then the next invocation of the + * `[.lastModified]` method will return the (possibly truncated) `time + ` * argument that was passed to this method. + * + * @param time The new last-modified time, measured in milliseconds since the epoch (00:00:00 GMT, + * January 1, 1970) + * @return `true` if and only if the operation succeeded; `false` otherwise + * @throws IllegalArgumentException If the argument is negative + */ + fun setLastModified(time: Long): Boolean { + require(time >= 0) { "Negative time" } + return if (isInvalid) { + false + } else fs.setLastModifiedTime(this, time) + } + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * Marks the file or directory named by this abstract pathname so that only read operations are + * allowed. After invoking this method the file or directory will not change until it is either + * deleted or marked to allow write access. Whether or not a read-only file or directory may be + * deleted depends upon the underlying system. + * + * @return `true` if and only if the operation succeeded; `false` otherwise + */ + fun setReadOnly(): Boolean { + return if (isInvalid) { + false + } else fs.setReadOnly(this) + } + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * Sets the owner's or everybody's write permission for this abstract pathname. + * + * + * The [java.nio.file.Files] class defines methods that operate on file attributes + * including file permissions. This may be used when finer manipulation of file permissions is + * required. + * + * @param writable If `true`, sets the access permission to allow write operations; if + * `false` to disallow write operations + * @param ownerOnly If `true`, the write permission applies only to the owner's write + * permission; otherwise, it applies to everybody. If the underlying file system can not + * distinguish the owner's write permission from that of others, then the permission will + * apply to everybody, regardless of this value. + * @return `true` if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. + */ + fun setWritable(writable: Boolean, ownerOnly: Boolean): Boolean { + return if (isInvalid) { + false + } else fs.setWritable(this, writable, ownerOnly) + } + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * A convenience method to set the owner's write permission for this abstract pathname. + * + * + * An invocation of this method of the form file.setWritable(arg) behaves in exactly + * the same way as the invocation + * + *

+     * file.setWritable(arg, true) 
+ * + * @param writable If `true`, sets the access permission to allow write operations; if + * `false` to disallow write operations + * @return `true` if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. + */ + fun setWritable(writable: Boolean): Boolean { + return setWritable(writable, true) + } + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * Sets the owner's or everybody's read permission for this abstract pathname. + * + * + * The [java.nio.file.Files] class defines methods that operate on file attributes + * including file permissions. This may be used when finer manipulation of file permissions is + * required. + * + * @param readable If `true`, sets the access permission to allow read operations; if + * `false` to disallow read operations + * @param ownerOnly If `true`, the read permission applies only to the owner's read + * permission; otherwise, it applies to everybody. If the underlying file system can not + * distinguish the owner's read permission from that of others, then the permission will apply + * to everybody, regardless of this value. + * @return `true` if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. If `readable` is `false` and the underlying file system + * does not implement a read permission, then the operation will fail. + */ + fun setReadable(readable: Boolean, ownerOnly: Boolean): Boolean { + return if (isInvalid) { + false + } else fs.setReadable(this, readable, ownerOnly) + } + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * A convenience method to set the owner's read permission for this abstract pathname. + * + * + * An invocation of this method of the form file.setReadable(arg) behaves in exactly + * the same way as the invocation + * + *
+     * file.setReadable(arg, true) 
+ * + * @param readable If `true`, sets the access permission to allow read operations; if + * `false` to disallow read operations + * @return `true` if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. If `readable` is `false` and the underlying file system + * does not implement a read permission, then the operation will fail. + */ + fun setReadable(readable: Boolean): Boolean { + return setReadable(readable, true) + } + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * Sets the owner's or everybody's execute permission for this abstract pathname. + * + * + * The [java.nio.file.Files] class defines methods that operate on file attributes + * including file permissions. This may be used when finer manipulation of file permissions is + * required. + * + * @param executable If `true`, sets the access permission to allow execute operations; + * if `false` to disallow execute operations + * @param ownerOnly If `true`, the execute permission applies only to the owner's + * execute permission; otherwise, it applies to everybody. If the underlying file system can + * not distinguish the owner's execute permission from that of others, then the permission + * will apply to everybody, regardless of this value. + * @return `true` if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. If `executable` is `false` and the underlying file system + * does not implement an execute permission, then the operation will fail. + */ + fun setExecutable(executable: Boolean, ownerOnly: Boolean): Boolean { + return if (isInvalid) { + false + } else fs.setExecutable(this, executable, ownerOnly) + } + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * A convenience method to set the owner's execute permission for this abstract pathname. + * + * + * An invocation of this method of the form file.setExcutable(arg) behaves in exactly + * the same way as the invocation + * + *
+     * file.setExecutable(arg, true) 
+ * + * @param executable If `true`, sets the access permission to allow execute operations; + * if `false` to disallow execute operations + * @return `true` if and only if the operation succeeded. The operation will fail if + * the user does not have permission to change the access permissions of this abstract + * pathname. If `executable` is `false` and the underlying file system + * does not implement an execute permission, then the operation will fail. + */ + fun setExecutable(executable: Boolean): Boolean { + return setExecutable(executable, true) + } + // Android-changed. Removed javadoc comment about special privileges + // that doesn't make sense on Android. + /** + * Tests whether the application can execute the file denoted by this abstract pathname. + * + * @return `true` if and only if the abstract pathname exists *and* the + * application is allowed to execute the file + */ + fun canExecute(contextProvider: ContextProvider): Boolean { + return if (isInvalid) { + false + } else fs.canExecute(this, contextProvider) + } + /* -- Filesystem interface -- */ // Android-changed: Replaced generic platform info with Android specific one. + /** + * Returns the file system roots. On Android and other Unix systems, there is a single root, + * `/`. + */ + fun listRoots(): Array { + return fs.listRoots() + } + /* -- Disk usage -- */ + /** + * Returns the size of the partition [named](#partName) by this abstract pathname. + * + * @return The size, in bytes, of the partition or 0L if this abstract pathname does not + * name a partition If there is no way to determine, total space is -1 + */ + fun getTotalSpace(contextProvider: ContextProvider): Long { + return if (isInvalid) { + 0L + } else try { + fs.getTotalSpace(this, contextProvider) + } catch (e: NotImplementedError) { + Log.w(TAG, "Call to unimplemented fuction", e) + -1 + } + } + + /** + * Returns the number of unallocated bytes in the partition [named](#partName) by this + * abstract path name. + * + * + * The returned number of unallocated bytes is a hint, but not a guarantee, that it is possible + * to use most or any of these bytes. The number of unallocated bytes is most likely to be + * accurate immediately after this call. It is likely to be made inaccurate by any external I/O + * operations including those made on the system outside of this virtual machine. This method + * makes no guarantee that write operations to this file system will succeed. + * + * @return The number of unallocated bytes on the partition or 0L if the abstract + * pathname does not name a partition. This value will be less than or equal to the total file + * system size returned by [.getTotalSpace]. + */ + val freeSpace: Long + get() = if (isInvalid) { + 0L + } else fs.getFreeSpace(this) + // Android-added: Replaced generic platform info with Android specific one. + /** + * Returns the number of bytes available to this virtual machine on the partition [named](#partName) by this abstract pathname. When possible, this method checks for + * write permissions and other operating system restrictions and will therefore usually provide a + * more accurate estimate of how much new data can actually be written than [.getFreeSpace]. + * + * + * The returned number of available bytes is a hint, but not a guarantee, that it is possible + * to use most or any of these bytes. The number of unallocated bytes is most likely to be + * accurate immediately after this call. It is likely to be made inaccurate by any external I/O + * operations including those made on the system outside of this virtual machine. This method + * makes no guarantee that write operations to this file system will succeed. + * + * + * On Android (and other Unix-based systems), this method returns the number of free bytes + * available to non-root users, regardless of whether you're actually running as root, and + * regardless of any quota or other restrictions that might apply to the user. (The `getFreeSpace` method returns the number of bytes potentially available to root.) + * + * @return The number of available bytes on the partition or 0L if the abstract pathname + * does not name a partition. On systems where this information is not available, this method + * will be equivalent to a call to [.getFreeSpace]. If there is no way to determine the + * current space left -1 is returned. + */ + val usableSpace: Long + get() = if (isInvalid) { + 0L + } else try { + fs.getUsableSpace(this) + } catch (e: NotImplementedError) { + Log.w(TAG, "Call to unimplemented fuction", e) + -1 + } + + /* -- Temporary files -- */ + private object TempDirectory { + // file name generation + private val random = SecureRandom() + @Throws(IOException::class) + fun generateFile(prefix: String, suffix: String, dir: AmazeFile?): AmazeFile { + // Android-changed: Use Math.randomIntInternal. This (pseudo) random number + // is initialized post-fork + var n = random.nextLong() + n = if (n == Long.MIN_VALUE) { + 0 // corner case + } else { + Math.abs(n) + } + + // Android-changed: Reject invalid file prefixes + // Use only the file name from the supplied prefix + // prefix = (new AmazeFile(prefix)).getName(); + val name = prefix + java.lang.Long.toString(n) + suffix + val f = AmazeFile(dir, name) + if (name != f.name || f.isInvalid) { + if (System.getSecurityManager() != null) throw IOException("Unable to create temporary file") else throw IOException("Unable to create temporary file, $f") + } + return f + } + } + + /** + * Creates a new empty file in the specified directory, using the given prefix and suffix strings + * to generate its name. If this method returns successfully then it is guaranteed that: + * + * + * 1. The file denoted by the returned abstract pathname did not exist before this method was + * invoked, and + * 1. Neither this method nor any of its variants will return the same abstract pathname again + * in the current invocation of the virtual machine. + * + * + * This method provides only part of a temporary-file facility. To arrange for a file created by + * this method to be deleted automatically, use the `[.deleteOnExit]` method. + * + * + * The `prefix` argument must be at least three characters long. It is recommended + * that the prefix be a short, meaningful string such as `"hjb"` or `"mail"` + * . The `suffix` argument may be `null`, in which case the suffix ` + * ".tmp"` will be used. + * + * + * To create the new file, the prefix and the suffix may first be adjusted to fit the + * limitations of the underlying platform. If the prefix is too long then it will be truncated, + * but its first three characters will always be preserved. If the suffix is too long then it too + * will be truncated, but if it begins with a period character (`'.'`) then the period + * and the first three characters following it will always be preserved. Once these adjustments + * have been made the name of the new file will be generated by concatenating the prefix, five or + * more internally-generated characters, and the suffix. + * + * + * If the `directory` argument is `null` then the system-dependent + * default temporary-file directory will be used. The default temporary-file directory is + * specified by the system property `java.io.tmpdir`. On UNIX systems the default value + * of this property is typically `"/tmp"` or `"/var/tmp"`; on Microsoft + * Windows systems it is typically `"C:\\WINNT\\TEMP"`. A different value may be given + * to this system property when the Java virtual machine is invoked, but programmatic changes to + * this property are not guaranteed to have any effect upon the temporary directory used by this + * method. + * + * @param prefix The prefix string to be used in generating the file's name; must be at least + * three characters long + * @param suffix The suffix string to be used in generating the file's name; may be `null + ` * , in which case the suffix `".tmp"` will be used + * @param directory The directory in which the file is to be created, or `null` if the + * default temporary-file directory is to be used + * @return An abstract pathname denoting a newly-created empty file + * @throws IllegalArgumentException If the `prefix` argument contains fewer than three + * characters + * @throws IOException If a file could not be created + */ + @Throws(IOException::class) + fun createTempFile( + contextProvider: ContextProvider, + prefix: String, + suffix: String?, + directory: AmazeFile?): AmazeFile { + require(prefix.length >= 3) { "Prefix string too short" } + + // Android-changed: Handle java.io.tmpdir changes. + val tmpdir = directory ?: AmazeFile(System.getProperty("java.io.tmpdir", ".")) + var f: AmazeFile + do { + f = TempDirectory.generateFile(prefix, suffix ?: ".tmp", tmpdir) + } while (fs.getBooleanAttributes(f, contextProvider) and AmazeFilesystem.BA_EXISTS != 0) + if (!fs.createFileExclusively(f.path)) throw IOException("Unable to create temporary file") + return f + } + + /** + * Creates an empty file in the default temporary-file directory, using the given prefix and + * suffix to generate its name. Invoking this method is equivalent to invoking ` + * [ createTempFile(prefix,&nbsp;suffix,&nbsp;null)][.createTempFile]`. + * + * + * The [ ][java.nio.file.Files.createTempFile] method provides an alternative method to create an empty file in the + * temporary-file directory. Files created by that method may have more restrictive access + * permissions to files created by this method and so may be more suited to security-sensitive + * applications. + * + * @param prefix The prefix string to be used in generating the file's name; must be at least + * three characters long + * @param suffix The suffix string to be used in generating the file's name; may be `null + ` * , in which case the suffix `".tmp"` will be used + * @return An abstract pathname denoting a newly-created empty file + * @throws IllegalArgumentException If the `prefix` argument contains fewer than three + * characters + * @throws IOException If a file could not be created + * @see java.nio.file.Files.createTempDirectory + */ + @Throws(IOException::class) + fun createTempFile( + contextProvider: ContextProvider, prefix: String, suffix: String?): AmazeFile { + return createTempFile(contextProvider, prefix, suffix, null) + } + /* -- Basic infrastructure -- */ + /** + * Compares two abstract pathnames lexicographically. The ordering defined by this method depends + * upon the underlying system. On UNIX systems, alphabetic case is significant in comparing + * pathnames; on Microsoft Windows systems it is not. + * + * @param pathname The abstract pathname to be compared to this abstract pathname + * @return Zero if the argument is equal to this abstract pathname, a value less than zero if this + * abstract pathname is lexicographically less than the argument, or a value greater than zero + * if this abstract pathname is lexicographically greater than the argument + */ + override fun compareTo(pathname: AmazeFile?): Int { + return fs.compare(this, pathname!!) + } + + /** + * Tests this abstract pathname for equality with the given object. Returns `true` if + * and only if the argument is not `null` and is an abstract pathname that denotes the + * same file or directory as this abstract pathname. Whether or not two abstract pathnames are + * equal depends upon the underlying system. On UNIX systems, alphabetic case is significant in + * comparing pathnames; on Microsoft Windows systems it is not. + * + * @param obj The object to be compared with this abstract pathname + * @return `true` if and only if the objects are the same; `false` otherwise + */ + override fun equals(obj: Any?): Boolean { + return if (obj is AmazeFile) { + compareTo(obj as AmazeFile?) == 0 + } else false + } + + /** + * Computes a hash code for this abstract pathname. Because equality of abstract pathnames is + * inherently system-dependent, so is the computation of their hash codes. On UNIX systems, the + * hash code of an abstract pathname is equal to the exclusive *or* of the hash code of its + * pathname string and the decimal value `1234321`. On Microsoft Windows systems, the + * hash code is equal to the exclusive *or* of the hash code of its pathname string + * converted to lower case and the decimal value `1234321`. Locale is not taken into + * account on lowercasing the pathname string. + * + * @return A hash code for this abstract pathname + */ + override fun hashCode(): Int { + return fs.hashCode(this) + } + + /** + * Returns the pathname string of this abstract pathname. This is just the string returned by the + * `[.getPath]` method. + * + * @return The string form of this abstract pathname + */ + override fun toString(): String { + return path + } +} \ No newline at end of file diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt index 675937025a..5245e10dcb 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt @@ -181,7 +181,7 @@ abstract class AmazeFilesystem { * strings naming the elements of the directory if successful; otherwise, return `null` * . */ - abstract fun list(f: AmazeFile, contextProvider: ContextProvider): Array? + abstract fun list(f: AmazeFile, contextProvider: ContextProvider): Array? abstract fun getInputStream(f: AmazeFile, contextProvider: ContextProvider): InputStream? abstract fun getOutputStream( f: AmazeFile, contextProvider: ContextProvider): OutputStream? diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt index d4b24babba..f7dfe5a9cf 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt @@ -155,16 +155,13 @@ abstract class CloudAmazeFilesystem : AmazeFilesystem() { return true // This seems to never fail } - override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { - val account = account.account - Objects.requireNonNull(account) + override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { + val account = account.account ?: return null val noPrefixPath = removePrefix(f.path) - val metadatas = account!!.getChildren(noPrefixPath) - val list = arrayOfNulls(metadatas.size) - for (i in list.indices) { - list[i] = normalize(prefix + metadatas[i].path) + val metadatas = account.getChildren(noPrefixPath) + return Array(metadatas.size) { i: Int -> + normalize(prefix + metadatas[i].path) } - return list } override fun getInputStream(f: AmazeFile, contextProvider: ContextProvider): InputStream? { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt index d6b23d96c6..2a06e34ed6 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt @@ -210,7 +210,7 @@ class FileAmazeFilesystem private constructor() : AmazeFilesystem() { return false } - override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { + override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { return File(f.path).list() } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt index 6a3e8e2c10..deec9e392b 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt @@ -312,9 +312,8 @@ class SmbAmazeFilesystem private constructor() : AmazeFilesystem() { } } - override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { - val list: Array - list = try { + override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { + val list: Array = try { create(f.path).list() } catch (e: SmbException) { Log.e(TAG, "Error listing SMB files", e) @@ -324,10 +323,9 @@ class SmbAmazeFilesystem private constructor() : AmazeFilesystem() { return null } val prefix = f.path.substring(0, prefixLength(f.path)) - for (i in list.indices) { - list[i] = INSTANCE.normalize(prefix + getSeparator() + list[i]) + return Array(list.size) { i: Int -> + INSTANCE.normalize(prefix + getSeparator() + list[i]) } - return list } override fun getInputStream(f: AmazeFile, contextProvider: ContextProvider): InputStream? { From dac1cf3f81ab2bdfab9a9b6d8fe160b199d2631f Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Thu, 27 Jan 2022 21:01:51 -0300 Subject: [PATCH 31/46] Convert Filesystems to objects --- .../filesystem/otg/OtgAmazeFilesystem.kt | 15 +- .../filetypes/cloud/box/BoxAmazeFilesystem.kt | 15 +- .../cloud/dropbox/DropboxAmazeFilesystem.kt | 14 +- .../gdrive/GoogledriveAmazeFilesystem.kt | 14 +- .../cloud/onedrive/OnedriveAmazeFilesystem.kt | 14 +- .../filetypes/file/FileAmazeFilesystem.kt | 12 +- .../filetypes/smb/SmbAmazeFilesystem.kt | 147 +++++++++--------- 7 files changed, 110 insertions(+), 121 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt index 1e2cc9bb86..ef25f71629 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt @@ -8,17 +8,14 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvide import java.io.* import java.util.ArrayList -class OtgAmazeFilesystem private constructor() : AmazeFilesystem() { - companion object { - val TAG = OtgAmazeFilesystem::class.java.simpleName +object OtgAmazeFilesystem : AmazeFilesystem() { + @JvmStatic + val TAG = OtgAmazeFilesystem::class.java.simpleName - val INSTANCE = OtgAmazeFilesystem() + const val PREFIX = "otg:/" - const val PREFIX = "otg:/" - - init { - AmazeFile.addFilesystem(INSTANCE) - } + init { + AmazeFile.addFilesystem(this) } override val prefix = PREFIX diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.kt index a36a0f42c4..cbd54e30bc 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.kt @@ -25,17 +25,14 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAccount -class BoxAmazeFilesystem private constructor() : CloudAmazeFilesystem() { - companion object { - val TAG = BoxAmazeFilesystem::class.java.simpleName +object BoxAmazeFilesystem : CloudAmazeFilesystem() { + @JvmStatic + val TAG = BoxAmazeFilesystem::class.java.simpleName - const val PREFIX = "box:/" + const val PREFIX = "box:/" - val INSTANCE = BoxAmazeFilesystem() - - init { - AmazeFile.addFilesystem(INSTANCE) - } + init { + AmazeFile.addFilesystem(this) } override val prefix: String = PREFIX diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.kt index bbf547d0e8..02d8c0869c 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.kt @@ -25,15 +25,13 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount -class DropboxAmazeFilesystem private constructor() : CloudAmazeFilesystem() { - companion object { - val TAG = DropboxAmazeFilesystem::class.java.simpleName - const val PREFIX = "dropbox:/" - val INSTANCE = DropboxAmazeFilesystem() +object DropboxAmazeFilesystem : CloudAmazeFilesystem() { + @JvmStatic + val TAG = DropboxAmazeFilesystem::class.java.simpleName + const val PREFIX = "dropbox:/" - init { - AmazeFile.addFilesystem(INSTANCE) - } + init { + AmazeFile.addFilesystem(this) } override val prefix: String = PREFIX diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.kt index fdb2c0da32..9894f40dd3 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.kt @@ -25,15 +25,13 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount -class GoogledriveAmazeFilesystem private constructor() : CloudAmazeFilesystem() { - companion object { - val TAG = GoogledriveAmazeFilesystem::class.java.simpleName - const val PREFIX = "gdrive:/" - val INSTANCE = GoogledriveAmazeFilesystem() +object GoogledriveAmazeFilesystem : CloudAmazeFilesystem() { + @JvmStatic + val TAG = GoogledriveAmazeFilesystem::class.java.simpleName + const val PREFIX = "gdrive:/" - init { - AmazeFile.addFilesystem(INSTANCE) - } + init { + AmazeFile.addFilesystem(this) } override val prefix = PREFIX diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.kt index 5c8f0453dd..5ad411f557 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.kt @@ -23,15 +23,13 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAma import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -class OnedriveAmazeFilesystem private constructor() : CloudAmazeFilesystem() { - companion object { - val TAG = OnedriveAmazeFilesystem::class.java.simpleName - const val PREFIX = "onedrive:/" - val INSTANCE = OnedriveAmazeFilesystem() +object OnedriveAmazeFilesystem : CloudAmazeFilesystem() { + @JvmStatic + val TAG = OnedriveAmazeFilesystem::class.java.simpleName + const val PREFIX = "onedrive:/" - init { - AmazeFile.addFilesystem(INSTANCE) - } + init { + AmazeFile.addFilesystem(this) } override val prefix: String = PREFIX diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt index 2a06e34ed6..6f0a48279d 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt @@ -35,14 +35,12 @@ import java.io.* * This is in in essence calls to UnixFilesystem, but that class is not public so all calls must go * through java.io.File */ -class FileAmazeFilesystem private constructor() : AmazeFilesystem() { - companion object { - val INSTANCE = FileAmazeFilesystem() - val TAG = FileAmazeFilesystem::class.java.simpleName +object FileAmazeFilesystem: AmazeFilesystem() { + @JvmStatic + val TAG = FileAmazeFilesystem::class.java.simpleName - init { - AmazeFile.addFilesystem(INSTANCE) - } + init { + AmazeFile.addFilesystem(this) } override fun isPathOfThisFilesystem(path: String): Boolean { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt index deec9e392b..004c7a500b 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt @@ -42,82 +42,85 @@ import java.util.regex.Pattern /** * Root is "smb://:@" or "smb://" or * "smb://:@/?disableIpcSigningCheck=true" or - * "smb:///?disableIpcSigningCheck=true" Relative paths are not supported - */ -class SmbAmazeFilesystem private constructor() : AmazeFilesystem() { - companion object { - val TAG = SmbAmazeFilesystem::class.java.simpleName - - const val PARAM_DISABLE_IPC_SIGNING_CHECK = "disableIpcSigningCheck" - private val IPv4_PATTERN = Pattern.compile("[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+") - private val METADATA_PATTERN = Pattern.compile("([?][a-zA-Z]+=(\")?[a-zA-Z]+(\")?)+") - - val INSTANCE = SmbAmazeFilesystem() - - @Throws(MalformedURLException::class) - fun create(path: String?): SmbFile { - val processedPath: String - processedPath = if (!path!!.endsWith(STANDARD_SEPARATOR + "")) { - path + STANDARD_SEPARATOR - } else { - path - } - val uri = Uri.parse(processedPath) - val disableIpcSigningCheck = java.lang.Boolean.parseBoolean(uri.getQueryParameter(PARAM_DISABLE_IPC_SIGNING_CHECK)) - val userInfo = uri.userInfo - val noExtraInfoPath: String - noExtraInfoPath = if (path.contains("?")) { - path.substring(0, path.indexOf('?')) - } else { - path - } - val context = createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) - .withCredentials(createFrom(userInfo)) - return SmbFile(noExtraInfoPath, context) - } + * "smb:///?disableIpcSigningCheck=true" + * Relative paths are not supported + * + */ +object SmbAmazeFilesystem: AmazeFilesystem() { + @JvmStatic + val TAG = SmbAmazeFilesystem::class.java.simpleName + + const val PARAM_DISABLE_IPC_SIGNING_CHECK = "disableIpcSigningCheck" + @JvmStatic + private val IPv4_PATTERN = Pattern.compile("[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+") + @JvmStatic + private val METADATA_PATTERN = Pattern.compile("([?][a-zA-Z]+=(\")?[a-zA-Z]+(\")?)+") - /** - * Create [NtlmPasswordAuthenticator] from given userInfo parameter. - * - * - * Logic borrowed directly from jcifs-ng's own code. They should make that protected - * constructor public... - * - * @param userInfo authentication string, must be already URL decoded. [Uri] shall do this - * for you already - * @return [NtlmPasswordAuthenticator] instance - */ - private fun createFrom(userInfo: String?): NtlmPasswordAuthenticator { - return if (!TextUtils.isEmpty(userInfo)) { - var dom: String? = null - var user: String? = null - var pass: String? = null - var i: Int - var u: Int - val end = userInfo!!.length - i = 0 - u = 0 - while (i < end) { - val c = userInfo[i] - if (c == ';') { - dom = userInfo.substring(0, i) - u = i + 1 - } else if (c == ':') { - pass = userInfo.substring(i + 1) - break - } - i++ + @JvmStatic + @Throws(MalformedURLException::class) + fun create(path: String?): SmbFile { + val processedPath: String + processedPath = if (!path!!.endsWith(STANDARD_SEPARATOR + "")) { + path + STANDARD_SEPARATOR + } else { + path + } + val uri = Uri.parse(processedPath) + val disableIpcSigningCheck = java.lang.Boolean.parseBoolean(uri.getQueryParameter(PARAM_DISABLE_IPC_SIGNING_CHECK)) + val userInfo = uri.userInfo + val noExtraInfoPath: String + noExtraInfoPath = if (path.contains("?")) { + path.substring(0, path.indexOf('?')) + } else { + path + } + val context = createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) + .withCredentials(createFrom(userInfo)) + return SmbFile(noExtraInfoPath, context) + } + + /** + * Create [NtlmPasswordAuthenticator] from given userInfo parameter. + * + * + * Logic borrowed directly from jcifs-ng's own code. They should make that protected + * constructor public... + * + * @param userInfo authentication string, must be already URL decoded. [Uri] shall do this + * for you already + * @return [NtlmPasswordAuthenticator] instance + */ + @JvmStatic + private fun createFrom(userInfo: String?): NtlmPasswordAuthenticator { + return if (!TextUtils.isEmpty(userInfo)) { + var dom: String? = null + var user: String? = null + var pass: String? = null + var i: Int + var u: Int + val end = userInfo!!.length + i = 0 + u = 0 + while (i < end) { + val c = userInfo[i] + if (c == ';') { + dom = userInfo.substring(0, i) + u = i + 1 + } else if (c == ':') { + pass = userInfo.substring(i + 1) + break } - user = userInfo.substring(u, i) - NtlmPasswordAuthenticator(dom, user, pass) - } else { - NtlmPasswordAuthenticator() + i++ } + user = userInfo.substring(u, i) + NtlmPasswordAuthenticator(dom, user, pass) + } else { + NtlmPasswordAuthenticator() } + } - init { - AmazeFile.addFilesystem(INSTANCE) - } + init { + AmazeFile.addFilesystem(this) } override val prefix: String = SMB_URI_PREFIX @@ -324,7 +327,7 @@ class SmbAmazeFilesystem private constructor() : AmazeFilesystem() { } val prefix = f.path.substring(0, prefixLength(f.path)) return Array(list.size) { i: Int -> - INSTANCE.normalize(prefix + getSeparator() + list[i]) + normalize(prefix + getSeparator() + list[i]) } } From d2bffc2dd1906f243af64a2863ba84a453416df6 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Thu, 27 Jan 2022 21:40:17 -0300 Subject: [PATCH 32/46] Implementation for DocumentFileAmazeFilesystem --- .../filemanager/filesystem/HybridFile.java | 153 +++---------- .../files/DocumentFileAmazeFilesystem.kt | 212 ++++++++++++++++++ .../filesystem/otg/OtgAmazeFilesystem.kt | 17 +- .../filesystem/ssh/SshAmazeFilesystem.java | 14 +- .../filesystem/filetypes/AmazeFile.kt | 8 - .../filesystem/filetypes/AmazeFilesystem.kt | 6 +- .../filetypes/cloud/CloudAmazeFilesystem.kt | 8 +- .../filetypes/file/FileAmazeFilesystem.kt | 11 +- .../filetypes/smb/SmbAmazeFilesystem.kt | 8 +- 9 files changed, 264 insertions(+), 173 deletions(-) create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 8200248c9e..8f31f61b37 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -49,6 +49,7 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAccount; import com.amaze.filemanager.file_operations.filesystem.root.NativeOperations; import com.amaze.filemanager.filesystem.cloud.CloudUtil; +import com.amaze.filemanager.filesystem.files.DocumentFileAmazeFilesystem; import com.amaze.filemanager.filesystem.files.FileUtils; import com.amaze.filemanager.filesystem.root.DeleteFileCommand; import com.amaze.filemanager.filesystem.root.ListFilesCommand; @@ -96,9 +97,6 @@ public class HybridFile { protected static final String TAG = HybridFile.class.getSimpleName(); - public static final String DOCUMENT_FILE_PREFIX = - "content://com.android.externalstorage.documents"; - protected String path; protected OpenMode mode; protected String name; @@ -136,7 +134,7 @@ public void generateMode(Context context) { mode = OpenMode.SFTP; } else if (path.startsWith(OTGUtil.PREFIX_OTG)) { mode = OpenMode.OTG; - } else if (path.startsWith(DOCUMENT_FILE_PREFIX)) { + } else if (path.startsWith(DocumentFileAmazeFilesystem.DOCUMENT_FILE_PREFIX)) { mode = OpenMode.DOCUMENT_FILE; } else if (isCustomPath()) { mode = OpenMode.CUSTOM; @@ -260,9 +258,12 @@ public long lastModified() { case SFTP: case SMB: case FILE: - return new AmazeFile(path).lastModified(); case DOCUMENT_FILE: - return getDocumentFile(false).lastModified(); + case BOX: + case DROPBOX: + case GDRIVE: + case ONEDRIVE: + return new AmazeFile(path).lastModified(); case ROOT: HybridFileParcelable baseFile = generateBaseFileFromParent(); if (baseFile != null) return baseFile.getDate(); @@ -282,6 +283,7 @@ public long length(Context context) { case ONEDRIVE: case GDRIVE: case OTG: + case DOCUMENT_FILE: try { return new AmazeFile(path).length(() -> context); } catch (IOException e) { @@ -291,9 +293,6 @@ public long length(Context context) { HybridFileParcelable baseFile = generateBaseFileFromParent(); if (baseFile != null) return baseFile.getSize(); break; - case DOCUMENT_FILE: - s = getDocumentFile(false).length(); - break; default: break; } @@ -310,6 +309,12 @@ public String getSimpleName() { case SFTP: case SMB: case FILE: + case ONEDRIVE: + case GDRIVE: + case DROPBOX: + case BOX: + case OTG: + case DOCUMENT_FILE: return new AmazeFile(path).getName(); default: StringBuilder builder = new StringBuilder(path); @@ -327,16 +332,11 @@ public String getName(Context context) { case ONEDRIVE: case GDRIVE: case OTG: + case SFTP: + case DOCUMENT_FILE: return new AmazeFile(path).getName(); case ROOT: return getFile().getName(); - case DOCUMENT_FILE: - if (!Utils.isNullOrEmpty(name)) { - return name; - } - return OTGUtil.getDocumentFile( - path, SafRootHolder.getUriRoot(), context, OpenMode.DOCUMENT_FILE, false) - .getName(); default: if (path.isEmpty()) { return ""; @@ -375,6 +375,7 @@ public String getParent(Context context) { case GDRIVE: case SFTP: case OTG: + case DOCUMENT_FILE: return new AmazeFile(path).getParent(); default: if (path.length() == getName(context).length()) { @@ -397,7 +398,6 @@ public boolean isDirectory() { boolean isDirectory; switch (mode) { case SFTP: - return isDirectory(AppConfig.getInstance()); case SMB: case FILE: case DROPBOX: @@ -405,7 +405,8 @@ public boolean isDirectory() { case ONEDRIVE: case GDRIVE: case OTG: - return new AmazeFile(path).isDirectory(() -> null); + case DOCUMENT_FILE: + return new AmazeFile(path).isDirectory(AppConfig::getInstance); case ROOT: isDirectory = NativeOperations.isDirectory(path); break; @@ -420,32 +421,6 @@ public boolean isDirectory(Context context) { boolean isDirectory; switch (mode) { case SFTP: - final Boolean returnValue = - SshClientUtils.execute( - new SFtpClientTemplate(path) { - @Override - public Boolean execute(SFTPClient client) { - try { - return client - .stat(SshClientUtils.extractRemotePathFrom(path)) - .getType() - .equals(FileMode.Type.DIRECTORY); - } catch (IOException notFound) { - Log.e( - getClass().getSimpleName(), - "Fail to execute isDirectory for SFTP path :" + path, - notFound); - return false; - } - } - }); - - if (returnValue == null) { - Log.e(TAG, "Error obtaining if path is directory over SFTP"); - } - - //noinspection SimplifiableConditionalExpression - return returnValue == null ? false : returnValue; case SMB: case FILE: case DROPBOX: @@ -453,13 +428,11 @@ public Boolean execute(SFTPClient client) { case ONEDRIVE: case GDRIVE: case OTG: + case DOCUMENT_FILE: return new AmazeFile(path).isDirectory(() -> context); case ROOT: isDirectory = NativeOperations.isDirectory(path); break; - case DOCUMENT_FILE: - isDirectory = getDocumentFile(false).isDirectory(); - break; default: isDirectory = getFile().isDirectory(); break; @@ -533,12 +506,9 @@ public long getUsableSpace() { case ONEDRIVE: case GDRIVE: case SFTP: - case OTG: - return new AmazeFile(path).getUsableSpace(); + case OTG: case DOCUMENT_FILE: - size = - FileProperties.getDeviceStorageRemainingSpace(SafRootHolder.INSTANCE.getVolumeLabel()); - break; + return new AmazeFile(path).getUsableSpace(); } return size; } @@ -555,11 +525,9 @@ public long getTotal(Context context) { case ONEDRIVE: case GDRIVE: case SFTP: - case OTG: - return new AmazeFile(path).getTotalSpace(() -> context); + case OTG: case DOCUMENT_FILE: - size = getDocumentFile(false).length(); - break; + return new AmazeFile(path).getTotalSpace(() -> context); } return size; } @@ -747,18 +715,9 @@ public InputStream getInputStream(Context context) { case BOX: case ONEDRIVE: case GDRIVE: - case OTG: - return new AmazeFile(getPath()).getInputStream(() -> context); + case OTG: case DOCUMENT_FILE: - ContentResolver contentResolver = context.getContentResolver(); - DocumentFile documentSourceFile = getDocumentFile(false); - try { - inputStream = contentResolver.openInputStream(documentSourceFile.getUri()); - } catch (FileNotFoundException e) { - e.printStackTrace(); - inputStream = null; - } - break; + return new AmazeFile(getPath()).getInputStream(() -> context); default: try { inputStream = new FileInputStream(path); @@ -783,17 +742,8 @@ public OutputStream getOutputStream(Context context) { case ONEDRIVE: case GDRIVE: case OTG: - return new AmazeFile(path).getOutputStream(() -> context); case DOCUMENT_FILE: - ContentResolver contentResolver = context.getContentResolver(); - DocumentFile documentSourceFile = getDocumentFile(true); - try { - outputStream = contentResolver.openOutputStream(documentSourceFile.getUri()); - } catch (FileNotFoundException e) { - e.printStackTrace(); - outputStream = null; - } - break; + return new AmazeFile(path).getOutputStream(() -> context); default: try { outputStream = FileUtil.getOutputStream(getFile(), context); @@ -806,35 +756,14 @@ public OutputStream getOutputStream(Context context) { } public boolean exists() { - boolean exists = false; - if (isSftp()) { - // TODO use Amaze file - final Boolean executionReturn = - SshClientUtils.execute( - new SFtpClientTemplate(path) { - @Override - public Boolean execute(SFTPClient client) throws IOException { - try { - return client.stat(SshClientUtils.extractRemotePathFrom(path)) != null; - } catch (SFTPException notFound) { - return false; - } - } - }); - - if (executionReturn == null) { - Log.e(TAG, "Error obtaining existance of file over SFTP"); - } - - //noinspection SimplifiableConditionalExpression - exists = executionReturn == null ? false : executionReturn; - } else if (isSmb() || isLocal() || isDropBoxFile() || isBoxFile() || isGoogleDriveFile() || isOneDriveFile()) { + if (isSmb() || isLocal() || isDropBoxFile() || isBoxFile() || isGoogleDriveFile() + || isOneDriveFile() || isDocumentFile() || isSftp()) { return new AmazeFile(path).exists(() -> null); } else if (isRoot()) { return RootHelper.fileExists(path); } - return exists; + return false; } /** Helper method to check file existence in otg */ @@ -842,7 +771,7 @@ public boolean exists(Context context) { boolean exists = false; try { if (isSmb() || isLocal() || isDropBoxFile() || isBoxFile() || isGoogleDriveFile() - || isOneDriveFile() || isOtgFile() || isSftp()) { + || isOneDriveFile() || isOtgFile() || isSftp() || isDocumentFile()) { return new AmazeFile(path).exists(() -> context); } else if (isDocumentFile()) { exists = @@ -878,7 +807,8 @@ public boolean isSimpleFile() { } public boolean setLastModified(final long date) { - if (isSmb() || isLocal() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile() || isSftp() || isOtgFile()) { + if (isSmb() || isLocal() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() + || isDropBoxFile() || isSftp() || isOtgFile()) { return new AmazeFile(path).setLastModified(date); } File f = getFile(); @@ -886,21 +816,10 @@ public boolean setLastModified(final long date) { } public void mkdir(Context context) { - if (isSftp() || isSmb() || isLocal() || isRoot() || isCustomPath() || isUnknownFile() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile() || isOtgFile()) { + if (isSftp() || isSmb() || isLocal() || isRoot() || isCustomPath() || isUnknownFile() + || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile() + || isOtgFile() || isDocumentFile()) { new AmazeFile(path).mkdirs(() -> context); - } else if (isDocumentFile()) { - if (!exists(context)) { - DocumentFile parentDirectory = - OTGUtil.getDocumentFile( - getParent(context), - SafRootHolder.getUriRoot(), - context, - OpenMode.DOCUMENT_FILE, - true); - if (parentDirectory.isDirectory()) { - parentDirectory.createDirectory(getName(context)); - } - } } else { throw new IllegalStateException(); } diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt b/app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt new file mode 100644 index 0000000000..4b873b168c --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt @@ -0,0 +1,212 @@ +package com.amaze.filemanager.filesystem.files + +import android.content.ContentResolver +import android.util.Log +import androidx.documentfile.provider.DocumentFile +import com.amaze.filemanager.application.AppConfig +import com.amaze.filemanager.file_operations.filesystem.OpenMode +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider +import com.amaze.filemanager.filesystem.FileProperties.getDeviceStorageRemainingSpace +import com.amaze.filemanager.filesystem.SafRootHolder.uriRoot +import com.amaze.filemanager.filesystem.SafRootHolder.volumeLabel +import com.amaze.filemanager.utils.OTGUtil.getDocumentFile +import java.io.FileNotFoundException +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream + +object DocumentFileAmazeFilesystem: AmazeFilesystem() { + @JvmStatic + val TAG = DocumentFileAmazeFilesystem::class.java.simpleName + + const val DOCUMENT_FILE_PREFIX = "content://com.android.externalstorage.documents" + + init { + AmazeFile.addFilesystem(this) + } + + override val prefix: String = DOCUMENT_FILE_PREFIX + + override fun normalize(path: String): String { + val documentFile = getDocumentFile(path, false) ?: return defaultParent + return documentFile.uri.path ?: defaultParent + } + + override fun resolve(parent: String, child: String): String { + val documentFile = getDocumentFile(parent, false) ?: return defaultParent + val childDocumentFile = documentFile.findFile(child) ?: return defaultParent + return childDocumentFile.uri.path ?: return defaultParent + } + + override fun resolve(f: AmazeFile): String { + TODO("Not yet implemented") + } + + override val defaultParent: String = DOCUMENT_FILE_PREFIX + getSeparator() + + override fun isAbsolute(f: AmazeFile): Boolean = true // All paths are absolute + + override fun canonicalize(path: String?): String { + TODO("Not yet implemented") + } + + override fun exists(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val documentFile = getDocumentFile(f.path, false) ?: return false + return documentFile.exists() + } + + override fun isFile(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val documentFile = getDocumentFile(f.path, false) ?: return false + return documentFile.isFile + } + + override fun isDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val documentFile = getDocumentFile(f.path, false) ?: return false + return documentFile.isDirectory + } + + override fun isHidden(f: AmazeFile): Boolean { + throw NotImplementedError() + } + + override fun canExecute(f: AmazeFile, contextProvider: ContextProvider): Boolean { + throw NotImplementedError() + } + + override fun canWrite(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val documentFile = getDocumentFile(f.path, false) ?: return false + return documentFile.canWrite() + } + + override fun canRead(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val documentFile = getDocumentFile(f.path, false) ?: return false + return documentFile.canRead() + } + + override fun canAccess(f: AmazeFile, contextProvider: ContextProvider): Boolean { + return exists(f, contextProvider) + } + + override fun setExecutable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + return false //Can't set + } + + override fun setWritable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + return false //Can't set + } + + override fun setReadable(f: AmazeFile, enable: Boolean, owneronly: Boolean): Boolean { + return false //Can't set + } + + override fun getLastModifiedTime(f: AmazeFile): Long { + val documentFile = getDocumentFile(f.path, false) ?: return 0 + return documentFile.lastModified() + } + + override fun getLength(f: AmazeFile, contextProvider: ContextProvider): Long { + val documentFile = getDocumentFile(f.path, false) + ?: throw IOException("Could not create DocumentFile for length") + return documentFile.length() + } + + override fun createFileExclusively(pathname: String): Boolean { + TODO("Not yet implemented") + } + + override fun delete(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val documentFile = getDocumentFile(f.path, false) ?: return false + return documentFile.delete() + } + + override fun list(f: AmazeFile, contextProvider: ContextProvider): Array? { + val documentFile = getDocumentFile(f.path, false) ?: return null + return documentFile.listFiles().mapNotNull { it.uri.path }.toTypedArray() + } + + override fun getInputStream(f: AmazeFile, contextProvider: ContextProvider): InputStream? { + val context = contextProvider.getContext() ?: return null + val contentResolver: ContentResolver = context.contentResolver + val documentSourceFile = getDocumentFile(f.path, false) ?: return null + return try { + contentResolver.openInputStream(documentSourceFile.uri) + } catch (e: FileNotFoundException) { + Log.e(TAG, "Error getting input stream for DocumentFile", e) + null + } + } + + override fun getOutputStream(f: AmazeFile, contextProvider: ContextProvider): OutputStream? { + val context = contextProvider.getContext() ?: return null + val contentResolver: ContentResolver = context.contentResolver + val documentSourceFile = getDocumentFile(f.path, true) ?: return null + return try { + contentResolver.openOutputStream(documentSourceFile.uri) + } catch (e: FileNotFoundException) { + Log.e(TAG, "Error getting output stream for DocumentFile", e) + null + } + } + + override fun createDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { + val context = contextProvider.getContext() ?: return false + if (!exists(f, contextProvider)) { + val uriRoot = uriRoot ?: return false + val parent = f.parent ?: return false + val parentDirectory = getDocumentFile( + parent, + uriRoot, + context, + OpenMode.DOCUMENT_FILE, + true + ) ?: return false + + if (parentDirectory.isDirectory) { + parentDirectory.createDirectory(f.name) ?: return false + return true + } + } + + return false + } + + override fun rename(f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean { + TODO("Not yet implemented") + } + + override fun setLastModifiedTime(f: AmazeFile, time: Long): Boolean { + TODO("Not yet implemented") + } + + override fun setReadOnly(f: AmazeFile): Boolean { + return false //Can't set + } + + override fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long { + return getDocumentFile(defaultParent, false)?.length() ?: 0 + } + + override fun getFreeSpace(f: AmazeFile): Long { + return getUsableSpace(f) // Assume both are equal + } + + override fun getUsableSpace(f: AmazeFile): Long { + val volumeLabel = volumeLabel ?: return 0 + return getDeviceStorageRemainingSpace(volumeLabel) + } + + fun getDocumentFile(path: String, createRecursive: Boolean): DocumentFile? { + val uriRoot = uriRoot ?: return null + + return getDocumentFile( + path, + uriRoot, + AppConfig.getInstance(), + OpenMode.DOCUMENT_FILE, + createRecursive + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt index ef25f71629..ec58165444 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt @@ -29,10 +29,10 @@ object OtgAmazeFilesystem : AmazeFilesystem() { return simpleUnixNormalize(path) } - override fun resolve(parent: String?, child: String?): String { - val prefix = parent!!.substring(0, prefixLength(parent)) + override fun resolve(parent: String, child: String): String { + val prefix = parent.substring(0, prefixLength(parent)) val simplePathParent = parent.substring(prefixLength(parent)) - val simplePathChild = child!!.substring(prefixLength(child)) + val simplePathChild = child.substring(prefixLength(child)) return prefix + basicUnixResolve(simplePathParent, simplePathChild) } @@ -113,7 +113,7 @@ object OtgAmazeFilesystem : AmazeFilesystem() { } @Throws(IOException::class) - override fun createFileExclusively(pathname: String?): Boolean { + override fun createFileExclusively(pathname: String): Boolean { return false } @@ -185,15 +185,6 @@ object OtgAmazeFilesystem : AmazeFilesystem() { return false } - override fun listRoots(): Array { - val roots = File.listRoots() - val amazeRoots = arrayOfNulls(roots.size) - for (i in roots.indices) { - amazeRoots[i] = AmazeFile(roots[i].path) - } - return arrayOf(AmazeFile(defaultParent)) - } - override fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long { val context = contextProvider.getContext() // TODO: Find total storage space of OTG when {@link DocumentFile} API adds support diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java index c1b39ecba6..2fdb9ddf7c 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -58,7 +58,7 @@ public String normalize(@NonNull String path) { @NonNull @Override - public String resolve(String parent, String child) { + public String resolve(@NotNull String parent, @NotNull String child) { return PREFIX + new File(removePrefix(parent), child).getAbsolutePath(); } @@ -102,7 +102,7 @@ public boolean exists(AmazeFile f, @NonNull ContextProvider contextProvider) { final SFtpClientTemplate template = new SFtpClientTemplate(f.getPath()) { @Override public Boolean execute(@NonNull SFTPClient client) throws IOException { - return client.statExistence(f.getPath()) != null; + return client.stat(SshClientUtils.extractRemotePathFrom(f.getPath())) != null; } }; @@ -134,7 +134,7 @@ public boolean isDirectory(AmazeFile f, @NonNull ContextProvider contextProvider final SFtpClientTemplate template = new SFtpClientTemplate(f.getPath()) { @Override public Boolean execute(@NonNull SFTPClient client) throws IOException { - return client.lstat(f.getPath()).getType() == DIRECTORY; + return client.lstat(SshClientUtils.extractRemotePathFrom(f.getPath())).getType() == DIRECTORY; } }; @@ -228,7 +228,7 @@ public Long execute(@NonNull SFTPClient client) throws IOException { } @Override - public boolean createFileExclusively(String pathname) throws IOException { + public boolean createFileExclusively(@NotNull String pathname) { return false; } @@ -393,12 +393,6 @@ public boolean setReadOnly(AmazeFile f) { return false; } - @NotNull - @Override - public AmazeFile[] listRoots() { - return new AmazeFile[] { new AmazeFile(getDefaultParent()) }; - } - public long getTotalSpace(AmazeFile f, @NonNull ContextProvider contextProvider) { final Long returnValue = SshClientUtils.execute( diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index ac954fb4e9..789cd803d9 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -20,7 +20,6 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes import android.os.Parcel -import android.os.Parcelable import android.util.Log import kotlinx.parcelize.Parceler import java.io.File @@ -1115,13 +1114,6 @@ class AmazeFile : Comparable { } else fs.canExecute(this, contextProvider) } /* -- Filesystem interface -- */ // Android-changed: Replaced generic platform info with Android specific one. - /** - * Returns the file system roots. On Android and other Unix systems, there is a single root, - * `/`. - */ - fun listRoots(): Array { - return fs.listRoots() - } /* -- Disk usage -- */ /** * Returns the size of the partition [named](#partName) by this abstract pathname. diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt index 5245e10dcb..da7ace64a3 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt @@ -55,7 +55,7 @@ abstract class AmazeFilesystem { * Resolve the child pathname string against the parent. Both strings must be in normal form, and * the result will be in normal form. */ - abstract fun resolve(parent: String?, child: String?): String + abstract fun resolve(parent: String, child: String): String /** * Return the parent pathname string to be used when the parent-directory argument in one of the @@ -168,7 +168,7 @@ abstract class AmazeFilesystem { * Throw an IOException if an I/O error occurs. */ @Throws(IOException::class) - abstract fun createFileExclusively(pathname: String?): Boolean + abstract fun createFileExclusively(pathname: String): Boolean /** * Delete the file or directory denoted by the given abstract pathname, returning `true @@ -214,8 +214,6 @@ abstract class AmazeFilesystem { return path.substring(prefixLength(path)) } /* -- Filesystem interface -- */ - /** List the available filesystem roots. */ - abstract fun listRoots(): Array abstract fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long abstract fun getFreeSpace(f: AmazeFile): Long abstract fun getUsableSpace(f: AmazeFile): Long diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt index f7dfe5a9cf..1acf26a90c 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt @@ -49,7 +49,7 @@ abstract class CloudAmazeFilesystem : AmazeFilesystem() { return canonical.substring(0, canonical.length - 1) } - override fun resolve(parent: String?, child: String?): String { + override fun resolve(parent: String, child: String): String { return prefix + File(removePrefix(parent!!), child) } @@ -143,7 +143,7 @@ abstract class CloudAmazeFilesystem : AmazeFilesystem() { } @Throws(IOException::class) - override fun createFileExclusively(pathname: String?): Boolean { + override fun createFileExclusively(pathname: String): Boolean { return false } @@ -203,10 +203,6 @@ abstract class CloudAmazeFilesystem : AmazeFilesystem() { return false // This doesn't seem possible } - override fun listRoots(): Array { - return arrayOf(AmazeFile("$prefix/")) - } - override fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long { val account = account.account Objects.requireNonNull(account) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt index 6f0a48279d..33d859eabf 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt @@ -63,7 +63,7 @@ object FileAmazeFilesystem: AmazeFilesystem() { return if (path[0] == '/') 1 else 0 } - override fun resolve(parent: String?, child: String?): String { + override fun resolve(parent: String, child: String): String { return File(parent, child).path } @@ -137,7 +137,7 @@ object FileAmazeFilesystem: AmazeFilesystem() { } @Throws(IOException::class) - override fun createFileExclusively(pathname: String?): Boolean { + override fun createFileExclusively(pathname: String): Boolean { return File(pathname).createNewFile() } @@ -281,13 +281,6 @@ object FileAmazeFilesystem: AmazeFilesystem() { return File(f.path).setReadOnly() } - override fun listRoots(): Array { - val roots = File.listRoots() - return Array(roots.size) { i: Int -> - AmazeFile(roots[i].path) - } - } - override fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long { return File(f.path).totalSpace } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt index 004c7a500b..94497e5da6 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt @@ -147,7 +147,7 @@ object SmbAmazeFilesystem: AmazeFilesystem() { return matcher.end() } - override fun resolve(parent: String?, child: String?): String { + override fun resolve(parent: String, child: String): String { val prefix = parent!!.substring(0, prefixLength(parent)) val simplePathParent = parent.substring(prefixLength(parent)) val simplePathChild = child!!.substring(prefixLength(child)) @@ -297,7 +297,7 @@ object SmbAmazeFilesystem: AmazeFilesystem() { } @Throws(IOException::class) - override fun createFileExclusively(pathname: String?): Boolean { + override fun createFileExclusively(pathname: String): Boolean { create(pathname).mkdirs() return true } @@ -401,10 +401,6 @@ object SmbAmazeFilesystem: AmazeFilesystem() { } } - override fun listRoots(): Array { - throw NotImplementedError() - } - override fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long { // TODO: Find total storage space of SMB when JCIFS adds support throw NotImplementedError() From e0c65cf79e8153a6510f15eff2a1b69289a08021 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Fri, 28 Jan 2022 21:17:57 -0300 Subject: [PATCH 33/46] Move rename operation to Filesystems --- .../filemanager/filesystem/Operations.java | 130 ++++-------------- .../files/DocumentFileAmazeFilesystem.kt | 23 +++- .../filesystem/otg/OtgAmazeFilesystem.kt | 24 ++-- .../filesystem/ssh/SshAmazeFilesystem.java | 29 +++- app/src/main/res/values-ar/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-he/strings.xml | 1 - app/src/main/res/values-hu/strings.xml | 1 - app/src/main/res/values-id/strings.xml | 1 - app/src/main/res/values-ja/strings.xml | 1 - app/src/main/res/values-pt-rBR/strings.xml | 1 - app/src/main/res/values-uk/strings.xml | 1 - app/src/main/res/values-vi/strings.xml | 1 - app/src/main/res/values-zh-rCN/strings.xml | 1 - app/src/main/res/values-zh-rHK/strings.xml | 1 - app/src/main/res/values-zh-rTW/strings.xml | 1 - app/src/main/res/values/strings.xml | 1 - .../filesystem/filetypes/AmazeFilesystem.kt | 2 +- .../filetypes/cloud/CloudAmazeFilesystem.kt | 32 ++--- .../filetypes/file/FileAmazeFilesystem.kt | 4 +- .../filetypes/smb/SmbAmazeFilesystem.kt | 4 +- 23 files changed, 105 insertions(+), 158 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java index 6b943d4cf8..507ec2513e 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java @@ -68,7 +68,7 @@ public class Operations { - private static Executor executor = AsyncTask.THREAD_POOL_EXECUTOR; + private static final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR; private static final String TAG = Operations.class.getSimpleName(); @@ -120,7 +120,7 @@ public static void mkdir( @NonNull final ErrorCallBack errorCallBack) { new AsyncTask() { - private Function safCreateDirectory = + private final Function safCreateDirectory = input -> { if (input != null && input.isDirectory()) { boolean result = false; @@ -251,9 +251,9 @@ public static void mkfile( new AsyncTask() { - private DataUtils dataUtils = DataUtils.getInstance(); + private final DataUtils dataUtils = DataUtils.getInstance(); - private Function safCreateFile = + private final Function safCreateFile = input -> { if (input != null && input.isDirectory()) { boolean result = false; @@ -425,18 +425,6 @@ public static void rename( private final DataUtils dataUtils = DataUtils.getInstance(); - private Function safRenameFile = - input -> { - boolean result = false; - try { - result = input.renameTo(newFile.getName(context)); - } catch (Exception e) { - Log.w(getClass().getSimpleName(), "Failed to rename", e); - } - errorCallBack.done(newFile, result); - return null; - }; - @Override protected Void doInBackground(Void... params) { // check whether file names for new file are valid or recursion occurs. @@ -450,17 +438,17 @@ protected Void doInBackground(Void... params) { errorCallBack.exists(newFile); return null; } + AmazeFile amazeFile = new AmazeFile(oldFile.getPath()); + // FIXME: smbFile1 should be created from SmbUtil too so it can be mocked + AmazeFile amazeFile1 = new AmazeFile(newFile.getPath()); if (oldFile.isSmb()) { - AmazeFile smbFile = new AmazeFile(oldFile.getPath()); - // FIXME: smbFile1 should be created from SmbUtil too so it can be mocked - AmazeFile smbFile1 = new AmazeFile(newFile.getPath()); if (newFile.exists()) { errorCallBack.exists(newFile); return null; } - smbFile.renameTo(smbFile1, () -> context); - if (!smbFile.exists(() -> context) && smbFile1.exists(() -> context)) { + amazeFile.renameTo(amazeFile1, () -> context); + if (!amazeFile.exists(() -> context) && amazeFile1.exists(() -> context)) { errorCallBack.done(newFile, true); return null; } else { @@ -474,104 +462,46 @@ protected Void doInBackground(Void... params) { Log.e(TAG, "Error creating HybridFileParcelable", exceptionThrownDuringBuildParcelable); } } + return null; } else if (oldFile.isSftp()) { - SshClientUtils.execute( - new SFtpClientTemplate(oldFile.getPath()) { - @Override - public Void execute(@NonNull SFTPClient client) { - try { - client.rename( - SshClientUtils.extractRemotePathFrom(oldFile.getPath()), - SshClientUtils.extractRemotePathFrom(newFile.getPath())); - errorCallBack.done(newFile, true); - } catch (IOException e) { - String errmsg = - context.getString( - R.string.cannot_rename_file, - HybridFile.parseAndFormatUriForDisplay(oldFile.getPath()), - e.getMessage()); - Log.e(TAG, errmsg); - ArrayList failedOps = new ArrayList<>(); - // Nobody care the size or actual permission here. Put a simple "r" and zero - // here - failedOps.add( - new HybridFileParcelable( + boolean result = amazeFile.renameTo(amazeFile1, () -> context); + + if(!result) { + ArrayList failedOps = new ArrayList<>(); + // Nobody care the size or actual permission here. Put a simple "r" and zero + // here + failedOps.add( + new HybridFileParcelable( oldFile.getPath(), "r", oldFile.lastModified(), 0, oldFile.isDirectory(context))); - context.sendBroadcast( - new Intent(TAG_INTENT_FILTER_GENERAL) + context.sendBroadcast( + new Intent(TAG_INTENT_FILTER_GENERAL) .putParcelableArrayListExtra(TAG_INTENT_FILTER_FAILED_OPS, failedOps)); - errorCallBack.done(newFile, false); - } - return null; - } - }); - } else if (oldFile.isDropBoxFile()) { - CloudStorage cloudStorageDropbox = DropboxAccount.INSTANCE.getAccount(); - try { - cloudStorageDropbox.move( - CloudUtil.stripPath(OpenMode.DROPBOX, oldFile.getPath()), - CloudUtil.stripPath(OpenMode.DROPBOX, newFile.getPath())); - errorCallBack.done(newFile, true); - } catch (Exception e) { - e.printStackTrace(); - errorCallBack.done(newFile, false); - } - } else if (oldFile.isBoxFile()) { - CloudStorage cloudStorageBox = BoxAccount.INSTANCE.getAccount(); - try { - cloudStorageBox.move( - CloudUtil.stripPath(OpenMode.BOX, oldFile.getPath()), - CloudUtil.stripPath(OpenMode.BOX, newFile.getPath())); - errorCallBack.done(newFile, true); - } catch (Exception e) { - e.printStackTrace(); - errorCallBack.done(newFile, false); - } - } else if (oldFile.isOneDriveFile()) { - CloudStorage cloudStorageOneDrive = OnedriveAccount.INSTANCE.getAccount(); - try { - cloudStorageOneDrive.move( - CloudUtil.stripPath(OpenMode.ONEDRIVE, oldFile.getPath()), - CloudUtil.stripPath(OpenMode.ONEDRIVE, newFile.getPath())); - errorCallBack.done(newFile, true); - } catch (Exception e) { - e.printStackTrace(); - errorCallBack.done(newFile, false); - } - } else if (oldFile.isGoogleDriveFile()) { - CloudStorage cloudStorageGdrive = GoogledriveAccount.INSTANCE.getAccount(); - try { - cloudStorageGdrive.move( - CloudUtil.stripPath(OpenMode.GDRIVE, oldFile.getPath()), - CloudUtil.stripPath(OpenMode.GDRIVE, newFile.getPath())); - errorCallBack.done(newFile, true); - } catch (Exception e) { - e.printStackTrace(); - errorCallBack.done(newFile, false); } + + errorCallBack.done(newFile, result); + } else if (oldFile.isDropBoxFile() || oldFile.isBoxFile() || oldFile.isOneDriveFile() + || oldFile.isGoogleDriveFile()) { + boolean result = amazeFile.renameTo(amazeFile1, () -> context); + errorCallBack.done(newFile, result); } else if (oldFile.isOtgFile()) { if (checkOtgNewFileExists(newFile, context)) { errorCallBack.exists(newFile); return null; } - safRenameFile.apply(OTGUtil.getDocumentFile(oldFile.getPath(), context, false)); + boolean result = amazeFile.renameTo(amazeFile1, () -> context); + errorCallBack.done(newFile, result); return null; } else if (oldFile.isDocumentFile()) { if (checkDocumentFileNewFileExists(newFile, context)) { errorCallBack.exists(newFile); return null; } - safRenameFile.apply( - OTGUtil.getDocumentFile( - oldFile.getPath(), - SafRootHolder.getUriRoot(), - context, - OpenMode.DOCUMENT_FILE, - false)); + boolean result = amazeFile.renameTo(amazeFile1, () -> context); + errorCallBack.done(newFile, result); return null; } else { File file = new File(oldFile.getPath()); diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt b/app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt index 4b873b168c..e06dec1fe7 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt @@ -11,6 +11,7 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvide import com.amaze.filemanager.filesystem.FileProperties.getDeviceStorageRemainingSpace import com.amaze.filemanager.filesystem.SafRootHolder.uriRoot import com.amaze.filemanager.filesystem.SafRootHolder.volumeLabel +import com.amaze.filemanager.filesystem.otg.OtgAmazeFilesystem import com.amaze.filemanager.utils.OTGUtil.getDocumentFile import java.io.FileNotFoundException import java.io.IOException @@ -172,12 +173,28 @@ object DocumentFileAmazeFilesystem: AmazeFilesystem() { return false } - override fun rename(f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean { - TODO("Not yet implemented") + override fun rename(file1: AmazeFile, file2: AmazeFile, contextProvider: ContextProvider): Boolean { + val context = contextProvider.getContext() ?: return false + val uriRoot = uriRoot ?: return false + + val documentFile = getDocumentFile( + file1.path, + uriRoot, + context, + OpenMode.DOCUMENT_FILE, + false + ) ?: return false + + return try { + documentFile.renameTo(file2.name) + } catch (e: Exception) { + Log.e(TAG, "Error renaming DocumentFile file", e) + false + } } override fun setLastModifiedTime(f: AmazeFile, time: Long): Boolean { - TODO("Not yet implemented") + return false //Can't set } override fun setReadOnly(f: AmazeFile): Boolean { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt index ec58165444..8373e1d112 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt @@ -1,12 +1,12 @@ package com.amaze.filemanager.filesystem.otg import android.util.Log -import com.amaze.filemanager.utils.OTGUtil.getDocumentFile -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider +import com.amaze.filemanager.utils.OTGUtil +import com.amaze.filemanager.utils.OTGUtil.getDocumentFile import java.io.* -import java.util.ArrayList object OtgAmazeFilesystem : AmazeFilesystem() { @JvmStatic @@ -162,19 +162,23 @@ object OtgAmazeFilesystem : AmazeFilesystem() { } val context = contextProvider.getContext() ?: return false val parent = f.parent ?: return false - val parentDirectory = getDocumentFile(parent, context, true) - if (parentDirectory!!.isDirectory) { + val parentDirectory = getDocumentFile(parent, context, true) ?: return false + if (parentDirectory.isDirectory) { parentDirectory.createDirectory(f.name) return true } return false } - override fun rename(f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean { - val context = contextProvider.getContext() - val contentResolver = context!!.contentResolver - val documentSourceFile = getDocumentFile(f1.path, context, true) - return documentSourceFile!!.renameTo(f2.path) + override fun rename(file1: AmazeFile, file2: AmazeFile, contextProvider: ContextProvider): Boolean { + val context = contextProvider.getContext() ?: return false + val documentSourceFile = getDocumentFile(file1.path, context, false) ?: return false + return try { + documentSourceFile.renameTo(file2.name) + } catch (e: UnsupportedOperationException) { + Log.e(TAG, "Error renaming OTG file", e) + false + } } override fun setLastModifiedTime(f: AmazeFile, time: Long): Boolean { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java index 2fdb9ddf7c..73eeba6b65 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -1,16 +1,23 @@ package com.amaze.filemanager.filesystem.ssh; +import static com.amaze.filemanager.ui.activities.MainActivity.TAG_INTENT_FILTER_FAILED_OPS; +import static com.amaze.filemanager.ui.activities.MainActivity.TAG_INTENT_FILTER_GENERAL; import static net.schmizz.sshj.sftp.FileMode.Type.DIRECTORY; import static net.schmizz.sshj.sftp.FileMode.Type.REGULAR; +import android.content.Context; +import android.content.Intent; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.amaze.filemanager.R; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem; import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; +import com.amaze.filemanager.filesystem.HybridFile; +import com.amaze.filemanager.filesystem.HybridFileParcelable; import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.common.Buffer; @@ -358,17 +365,27 @@ public Void execute(@NonNull SFTPClient client) { } @Override - public boolean rename(AmazeFile f1, AmazeFile f2, @NonNull ContextProvider contextProvider) { + public boolean rename(AmazeFile file1, AmazeFile file2, @NonNull ContextProvider contextProvider) { + @Nullable final Context context = contextProvider.getContext(); + + if(context == null) { + Log.e(TAG, "Error getting context for renaming ssh file"); + return false; + } + Boolean retval = - SshClientUtils.execute( - new SFtpClientTemplate(f1.getPath()) { + SshClientUtils.execute( + new SFtpClientTemplate(file1.getPath()) { @Override - public Boolean execute(@NonNull SFTPClient client) throws IOException { + public Boolean execute(@NonNull SFTPClient client) { try { - client.rename(f1.getPath(), f2.getPath()); + client.rename( + SshClientUtils.extractRemotePathFrom(file1.getPath()), + SshClientUtils.extractRemotePathFrom(file2.getPath()) + ); return true; } catch (IOException e) { - Log.e(TAG, "Error renaming over SFTP", e); + Log.e(TAG, "Error renaming ssh file", e); return false; } } diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 685bd8845e..6969663f43 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -664,7 +664,6 @@ الغاء اتاحة التحقق من توقيع IPC (بالنسبة لوحدات خدمة SMBv1 القديمة-UNSAFE !) لا يمكن حذف الملف لا يمكن حذف الملف-%s - لا يمكن اعادة تسمية الملف %s-%s لا يمكن قراءة الدليل %s-%s مرة واحدة فقط دائما diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 8a7aff0cb6..c0827c321b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -629,7 +629,6 @@ andernfalls wird nach Treffern gesucht. IPC Signaturprüfung deaktivieren (Für veraltete SMBv1 Server - UNSICHER!) Datei kann nicht gelöscht werden Datei %s kann nicht gelöscht werden - Datei %s - %s kann nicht umbenannt werden Verzeichnis %s - %s kann nicht gelesen werden Nur einmal Immer diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3b718801d9..ff398532e7 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -669,7 +669,6 @@ Desactive la verificación de firma de IPC (para servidores SMBv1 heredados - ¡INSEGURO!) No se puede eliminar el archivo No se puede eliminar el archivo - %s - No se puede renombrar el archivo %s - %s No se puede leer el directorio %s - %s Sólo una vez Siempre diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 23bf26d192..317590247b 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -645,7 +645,6 @@ Désactiver la vérification de la signature IPC (Pour les anciens serveurs SMBv1 - RISQUÉ!) Impossible de supprimer le fichier Impossible de supprimer le fichier - %s - Impossible de renommer le fichier %s - %s Impossible de lire le répertoire %s - %s Juste une fois Toujours diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index d1d59d342b..9cfa2ae8e4 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -678,7 +678,6 @@ השבתת בדיקת חתימת IPC (לשרתי SMBv1 מיושנים - מסוכן!) לא ניתן למחוק קובץ לא ניתן למחוק קובץ - %s - לא ניתן לשנות את שם הקובץ %s - %s לא ניתן לקרוא את התיקייה %s - %s רק פעם אחת תמיד diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 5b644eab52..1902e40e08 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -682,7 +682,6 @@ IPC-aláírás ellenőrzés letiltása (Örökölt SMBv1-kiszolgálókhoz - UNSAFE!) Nem lehet törölni a fájlt Nem lehet törölni a fájlt - %s - Nem lehet átnevezni a fájlt %s - %s Nem lehet olvasni a könyvtárat %s - %s Csak egyszer Mindig diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index 7b1718568b..ff75d157ee 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -677,7 +677,6 @@ Nonaktifkan pemeriksaaan tanda tangan IPC (Untuk server SMBv1 lama - TIDAK AMAN!) Tidak bisa menghapus berkas Tidak bisa menghapus berkas - %s - Tidak bisa mengubah nama berkas %s - %s Tidak bisa membaca direktori %s - %s Hanya Sekali Selalu diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 6332ba9a3d..ba331b0327 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -659,7 +659,6 @@ Wi-Fiに接続する必要があります。\n\n クラッシュ報告がクリップボードにコピーしました ファイルを削除できません ファイルを削除できません - %s - ファイル %s 名前を変更できません - %s 一回だけ 常に 別にAppを選ぶ diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 36944a15d8..da95c842f9 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -656,7 +656,6 @@ Desative a verificação de assinatura IPC (Para servidores SMBv1 legados - INSEGURO!) Não foi possível deletar o arquivo Não foi possível deletar o arquivo%s - Não foi possível renomear o arquivo%s%s Não foi possível acessar o diretório%s%s Apenas uma vez Sempre diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 8021ad02d1..616dd730dd 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -644,7 +644,6 @@ Вимкнути перевірку підписання IPC (для застарілих серверів SMBv1 - НЕБЕЗПЕЧНО!) Не вдається видалити файл Не вдається видалити файл - %s - Не вдається перейменувати файл %s - %s Не вжається прочитати каталог %s - %s Лише цього разу Завжди diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 91b96a2967..edd36367a0 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -678,7 +678,6 @@ Tắt kiểm tra ký IPC (Đối với máy chủ SMBv1 cũ - KHÔNG AN TOÀN!) Không thể xóa tập tin Không thể xóa tập tin - %s - Không thể đổi tên tập tin %s - %s Không thể đọc thư mục %s - %s Chỉ một lần Luôn luôn diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 9a0f93abfc..4e1b3eaa7b 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -651,7 +651,6 @@ 详情: 无法删除文件 无法删除文件 - %s - 无法重命名文件%s-%s 无法读取文件夹%s-%s 仅本次 总是 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 0a019feca0..c4cef8fc6f 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -629,7 +629,6 @@ 關閉 IPC簽名檢查(對應舊式嘅 SMBv1伺服器 - 唔安全㗎!) 個檔案刪除唔到 個檔案刪除唔到 - %s - 檔案 %s 改唔到名 - %s 讀唔到檔案夾 %s - %s 只限呢次 以後都係 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 8ed701dbbc..925b17e510 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -673,7 +673,6 @@ 關閉 IPC數位簽名檢查(適用於舊式 SMBv1伺服器—不安全!) 無法刪除檔案 無法刪除檔案 - %s - 無法重新命名檔案 %s - %s 無法讀取檔案夾 %s - %s 只此一次 總是 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1b06cb4311..761474e539 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -699,7 +699,6 @@ Disable IPC signing check (For legacy SMBv1 servers - UNSAFE!) Cannot delete file Cannot delete file - %s - Cannot rename file %s - %s Cannot read directory %s - %s Just Once Always diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt index da7ace64a3..0507c2ea43 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt @@ -197,7 +197,7 @@ abstract class AmazeFilesystem { * pathname, returning `true` if and only if the operation succeeds. */ abstract fun rename( - f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean + file1: AmazeFile, file2: AmazeFile, contextProvider: ContextProvider): Boolean /** * Set the last-modified time of the file or directory denoted by the given abstract pathname, diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt index 1acf26a90c..272d2d0cfd 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt @@ -176,26 +176,23 @@ abstract class CloudAmazeFilesystem : AmazeFilesystem() { } override fun createDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { - val account = account.account - Objects.requireNonNull(account) + val account = account.account ?: return false val noPrefixPath = removePrefix(f.path) - account!!.createFolder(noPrefixPath) + account.createFolder(noPrefixPath) return true // This seems to never fail } - override fun rename(f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean { - val account = account.account - Objects.requireNonNull(account) - account!!.move(removePrefix(f1.path), removePrefix(f2.path)) + override fun rename(file1: AmazeFile, file2: AmazeFile, contextProvider: ContextProvider): Boolean { + val account = account.account ?: return false + account.move(removePrefix(file1.path), removePrefix(file2.path)) return true // This seems to never fail } override fun setLastModifiedTime(f: AmazeFile, time: Long): Boolean { - val account = account.account - Objects.requireNonNull(account) + val account = account.account ?: return false val noPrefixPath = removePrefix(f.path) // TODO check that this actually returns seconds since epoch - account!!.getMetadata(noPrefixPath).contentModifiedAt = time + account.getMetadata(noPrefixPath).contentModifiedAt = time return true // This seems to never fail } @@ -204,23 +201,20 @@ abstract class CloudAmazeFilesystem : AmazeFilesystem() { } override fun getTotalSpace(f: AmazeFile, contextProvider: ContextProvider): Long { - val account = account.account - Objects.requireNonNull(account) - val spaceAllocation = account!!.allocation + val account = account.account ?: return 0 + val spaceAllocation = account.allocation return spaceAllocation.total } override fun getFreeSpace(f: AmazeFile): Long { - val account = account.account - Objects.requireNonNull(account) - val spaceAllocation = account!!.allocation + val account = account.account ?: return 0 + val spaceAllocation = account.allocation return spaceAllocation.total - spaceAllocation.used } override fun getUsableSpace(f: AmazeFile): Long { - val account = account.account - Objects.requireNonNull(account) - val spaceAllocation = account!!.allocation + val account = account.account ?: return 0 + val spaceAllocation = account.allocation return spaceAllocation.total - spaceAllocation.used } diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt index 33d859eabf..5aa79bbc65 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt @@ -269,8 +269,8 @@ object FileAmazeFilesystem: AmazeFilesystem() { } else false } - override fun rename(f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean { - return File(f1.path).renameTo(File(f2.path)) + override fun rename(file1: AmazeFile, file2: AmazeFile, contextProvider: ContextProvider): Boolean { + return File(file1.path).renameTo(File(file2.path)) } override fun setLastModifiedTime(f: AmazeFile, time: Long): Boolean { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt index 94497e5da6..b306406675 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt @@ -362,9 +362,9 @@ object SmbAmazeFilesystem: AmazeFilesystem() { } } - override fun rename(f1: AmazeFile, f2: AmazeFile, contextProvider: ContextProvider): Boolean { + override fun rename(file1: AmazeFile, file2: AmazeFile, contextProvider: ContextProvider): Boolean { return try { - create(f1!!.path).renameTo(create(f2!!.path)) + create(file1.path).renameTo(create(file2.path)) true } catch (e: SmbException) { Log.e(TAG, "Error getting SMB files for a rename", e) From 14b260f7b224416bc26f6d3de0a80c73249b2152 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 29 Jan 2022 00:06:01 -0300 Subject: [PATCH 34/46] Apply spotless --- .../filesystem/filetypes/AmazeFile.kt | 43 ++++++++++++------- .../filesystem/filetypes/AmazeFilesystem.kt | 20 ++++++--- .../filetypes/cloud/CloudAmazeFilesystem.kt | 15 +++++-- .../filetypes/cloud/box/BoxAmazeFilesystem.kt | 9 ++-- .../cloud/dropbox/DropboxAmazeFilesystem.kt | 9 ++-- .../gdrive/GoogledriveAmazeFilesystem.kt | 7 ++- .../cloud/onedrive/OnedriveAmazeFilesystem.kt | 5 ++- .../filetypes/file/FileAmazeFilesystem.kt | 37 ++++++++++------ .../filetypes/smb/SmbAmazeFilesystem.kt | 31 ++++++++----- 9 files changed, 112 insertions(+), 64 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index 789cd803d9..0eade8f090 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.amaze.filemanager.file_operations.filesystem.filetypes import android.os.Parcel @@ -134,7 +135,7 @@ class AmazeFile : Comparable { object AmazeFileParceler : Parceler { override fun create(parcel: Parcel): AmazeFile = - AmazeFile(parcel.readString() ?: "") + AmazeFile(parcel.readString() ?: "") override fun AmazeFile.write(parcel: Parcel, flags: Int) { parcel.writeString(absolutePath) @@ -257,7 +258,7 @@ class AmazeFile : Comparable { * `child` pathname string is taken to denote either a directory or a file. If the * `child` pathname string is absolute then it is converted into a relative pathname in * a system-dependent way. If `parent` is the empty string then the new `AmazeFile - ` * instance is created by converting `child` into an abstract pathname and + ` * instance is created by converting `child` into an abstract pathname and * resolving the result against a system-dependent default directory. Otherwise each pathname * string is converted into an abstract pathname and the child abstract pathname is resolved * against the parent. @@ -814,7 +815,9 @@ class AmazeFile : Comparable { * @see java.nio.file.Files.newDirectoryStream */ fun listFiles( - filter: AmazeFilenameFilter?, contextProvider: ContextProvider): Array? { + filter: AmazeFilenameFilter?, + contextProvider: ContextProvider + ): Array? { val ss = list(contextProvider) ?: return null val files = ArrayList() for (s in ss) if (filter == null || filter.accept(this, s)) files.add(AmazeFile(s!!, this)) @@ -888,8 +891,10 @@ class AmazeFile : Comparable { return false } val parent = canonFile.parentFile - return (parent != null && (parent.mkdirs(contextProvider) || parent.exists(contextProvider)) - && canonFile.mkdir(contextProvider)) + return ( + parent != null && (parent.mkdirs(contextProvider) || parent.exists(contextProvider)) && + canonFile.mkdir(contextProvider) + ) } // Android-changed: Replaced generic platform info with Android specific one. /** @@ -930,7 +935,7 @@ class AmazeFile : Comparable { * precision. The argument will be truncated to fit the supported precision. If the operation * succeeds and no intervening operations on the file take place, then the next invocation of the * `[.lastModified]` method will return the (possibly truncated) `time - ` * argument that was passed to this method. + ` * argument that was passed to this method. * * @param time The new last-modified time, measured in milliseconds since the epoch (00:00:00 GMT, * January 1, 1970) @@ -1205,7 +1210,11 @@ class AmazeFile : Comparable { val name = prefix + java.lang.Long.toString(n) + suffix val f = AmazeFile(dir, name) if (name != f.name || f.isInvalid) { - if (System.getSecurityManager() != null) throw IOException("Unable to create temporary file") else throw IOException("Unable to create temporary file, $f") + if (System.getSecurityManager() != null) { + throw IOException("Unable to create temporary file") + } else { + throw IOException("Unable to create temporary file, $f") + } } return f } @@ -1253,7 +1262,7 @@ class AmazeFile : Comparable { * @param prefix The prefix string to be used in generating the file's name; must be at least * three characters long * @param suffix The suffix string to be used in generating the file's name; may be `null - ` * , in which case the suffix `".tmp"` will be used + ` * , in which case the suffix `".tmp"` will be used * @param directory The directory in which the file is to be created, or `null` if the * default temporary-file directory is to be used * @return An abstract pathname denoting a newly-created empty file @@ -1263,10 +1272,11 @@ class AmazeFile : Comparable { */ @Throws(IOException::class) fun createTempFile( - contextProvider: ContextProvider, - prefix: String, - suffix: String?, - directory: AmazeFile?): AmazeFile { + contextProvider: ContextProvider, + prefix: String, + suffix: String?, + directory: AmazeFile? + ): AmazeFile { require(prefix.length >= 3) { "Prefix string too short" } // Android-changed: Handle java.io.tmpdir changes. @@ -1293,7 +1303,7 @@ class AmazeFile : Comparable { * @param prefix The prefix string to be used in generating the file's name; must be at least * three characters long * @param suffix The suffix string to be used in generating the file's name; may be `null - ` * , in which case the suffix `".tmp"` will be used + ` * , in which case the suffix `".tmp"` will be used * @return An abstract pathname denoting a newly-created empty file * @throws IllegalArgumentException If the `prefix` argument contains fewer than three * characters @@ -1302,7 +1312,10 @@ class AmazeFile : Comparable { */ @Throws(IOException::class) fun createTempFile( - contextProvider: ContextProvider, prefix: String, suffix: String?): AmazeFile { + contextProvider: ContextProvider, + prefix: String, + suffix: String? + ): AmazeFile { return createTempFile(contextProvider, prefix, suffix, null) } /* -- Basic infrastructure -- */ @@ -1360,4 +1373,4 @@ class AmazeFile : Comparable { override fun toString(): String { return path } -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt index 0507c2ea43..67f596f91c 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.amaze.filemanager.file_operations.filesystem.filetypes import java.io.File @@ -172,7 +173,7 @@ abstract class AmazeFilesystem { /** * Delete the file or directory denoted by the given abstract pathname, returning `true - ` * if and only if the operation succeeds. + ` * if and only if the operation succeeds. */ abstract fun delete(f: AmazeFile, contextProvider: ContextProvider): Boolean @@ -184,7 +185,9 @@ abstract class AmazeFilesystem { abstract fun list(f: AmazeFile, contextProvider: ContextProvider): Array? abstract fun getInputStream(f: AmazeFile, contextProvider: ContextProvider): InputStream? abstract fun getOutputStream( - f: AmazeFile, contextProvider: ContextProvider): OutputStream? + f: AmazeFile, + contextProvider: ContextProvider + ): OutputStream? /** * Create a new directory denoted by the given abstract pathname, returning `true` if @@ -197,7 +200,10 @@ abstract class AmazeFilesystem { * pathname, returning `true` if and only if the operation succeeds. */ abstract fun rename( - file1: AmazeFile, file2: AmazeFile, contextProvider: ContextProvider): Boolean + file1: AmazeFile, + file2: AmazeFile, + contextProvider: ContextProvider + ): Boolean /** * Set the last-modified time of the file or directory denoted by the given abstract pathname, @@ -356,8 +362,10 @@ abstract class AmazeFilesystem { } init { - useCanonCaches = getBooleanProperty("sun.io.useCanonCaches", useCanonCaches) - useCanonPrefixCache = getBooleanProperty("sun.io.useCanonPrefixCache", useCanonPrefixCache) + useCanonCaches = + getBooleanProperty("sun.io.useCanonCaches", useCanonCaches) + useCanonPrefixCache = + getBooleanProperty("sun.io.useCanonPrefixCache", useCanonPrefixCache) } } -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt index 272d2d0cfd..711aaa39bd 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/CloudAmazeFilesystem.kt @@ -17,11 +17,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud import android.util.Log -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider import com.cloudrail.si.types.CloudMetaData import java.io.File @@ -34,7 +35,9 @@ import java.util.* abstract class CloudAmazeFilesystem : AmazeFilesystem() { abstract val account: Account override fun prefixLength(path: String): Int { - require(path.length != 0) { "This should never happen, all paths must start with OTG prefix" } + require(path.isNotEmpty()) { + "This should never happen, all paths must start with OTG prefix" + } return super.prefixLength(path) } @@ -182,7 +185,11 @@ abstract class CloudAmazeFilesystem : AmazeFilesystem() { return true // This seems to never fail } - override fun rename(file1: AmazeFile, file2: AmazeFile, contextProvider: ContextProvider): Boolean { + override fun rename( + file1: AmazeFile, + file2: AmazeFile, + contextProvider: ContextProvider + ): Boolean { val account = account.account ?: return false account.move(removePrefix(file1.path), removePrefix(file2.path)) return true // This seems to never fail @@ -221,4 +228,4 @@ abstract class CloudAmazeFilesystem : AmazeFilesystem() { companion object { val TAG = CloudAmazeFilesystem::class.java.simpleName } -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.kt index cbd54e30bc..3b0ac14310 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/box/BoxAmazeFilesystem.kt @@ -17,13 +17,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAccount +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem object BoxAmazeFilesystem : CloudAmazeFilesystem() { @JvmStatic @@ -35,8 +34,8 @@ object BoxAmazeFilesystem : CloudAmazeFilesystem() { AmazeFile.addFilesystem(this) } - override val prefix: String = PREFIX + override val prefix: String = PREFIX override val account: Account get() = BoxAccount -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.kt index 02d8c0869c..299acacc0a 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/dropbox/DropboxAmazeFilesystem.kt @@ -17,13 +17,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem object DropboxAmazeFilesystem : CloudAmazeFilesystem() { @JvmStatic @@ -34,8 +33,8 @@ object DropboxAmazeFilesystem : CloudAmazeFilesystem() { AmazeFile.addFilesystem(this) } - override val prefix: String = PREFIX + override val prefix: String = PREFIX override val account: Account get() = DropboxAccount -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.kt index 9894f40dd3..6a30761b77 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/gdrive/GoogledriveAmazeFilesystem.kt @@ -17,13 +17,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem object GoogledriveAmazeFilesystem : CloudAmazeFilesystem() { @JvmStatic @@ -38,4 +37,4 @@ object GoogledriveAmazeFilesystem : CloudAmazeFilesystem() { override val account: Account get() = GoogledriveAccount -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.kt index 5ad411f557..d221bb5d83 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/cloud/onedrive/OnedriveAmazeFilesystem.kt @@ -17,11 +17,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.Account +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.CloudAmazeFilesystem object OnedriveAmazeFilesystem : CloudAmazeFilesystem() { @JvmStatic @@ -36,4 +37,4 @@ object OnedriveAmazeFilesystem : CloudAmazeFilesystem() { override val account: Account get() = OnedriveAccount -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt index 5aa79bbc65..971929ba34 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.amaze.filemanager.file_operations.filesystem.filetypes.file import android.content.ContentValues @@ -35,7 +36,7 @@ import java.io.* * This is in in essence calls to UnixFilesystem, but that class is not public so all calls must go * through java.io.File */ -object FileAmazeFilesystem: AmazeFilesystem() { +object FileAmazeFilesystem : AmazeFilesystem() { @JvmStatic val TAG = FileAmazeFilesystem::class.java.simpleName @@ -159,7 +160,8 @@ object FileAmazeFilesystem: AmazeFilesystem() { // Try with Storage Access Framework. if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val document = getDocumentFile( - f, true, context, get(context)) + f, true, context, get(context) + ) if (document != null && document.delete()) { return true } @@ -174,8 +176,9 @@ object FileAmazeFilesystem: AmazeFilesystem() { // Delete the created entry, such that content provider will delete the file. resolver.delete( - MediaStore.Files.getContentUri("external"), - MediaStore.MediaColumns.DATA + "=?", arrayOf(f.absolutePath)) + MediaStore.Files.getContentUri("external"), + MediaStore.MediaColumns.DATA + "=?", arrayOf(f.absolutePath) + ) } return !f.exists(contextProvider) } @@ -185,9 +188,12 @@ object FileAmazeFilesystem: AmazeFilesystem() { val context = contextProvider.getContext() // Try with Storage Access Framework. - if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isOnExtSdCard(f, context)) { + if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && + isOnExtSdCard(f, context) + ) { val document = getDocumentFile( - f, false, context, get(context)) ?: return true + f, false, context, get(context) + ) ?: return true if (document.delete()) { return true } @@ -198,7 +204,7 @@ object FileAmazeFilesystem: AmazeFilesystem() { val resolver = context!!.contentResolver try { val uri = MediaStoreHack.getUriFromFile(f.absolutePath, context) - ?: return false + ?: return false resolver.delete(uri, null, null) return !f.exists(contextProvider) } catch (e: SecurityException) { @@ -230,7 +236,8 @@ object FileAmazeFilesystem: AmazeFilesystem() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Storage Access Framework val targetDocument = getDocumentFile( - f, false, context, get(context)) ?: return null + f, false, context, get(context) + ) ?: return null return context.contentResolver.openOutputStream(targetDocument.uri) } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { // Workaround for Kitkat ext SD card @@ -251,10 +258,12 @@ object FileAmazeFilesystem: AmazeFilesystem() { val context = contextProvider.getContext() // Try with Storage Access Framework. - if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isOnExtSdCard(f, context)) { + if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && + isOnExtSdCard(f, context) + ) { val preferenceUri = get(context) val document = getDocumentFile(f, true, context, preferenceUri) - ?: return false + ?: return false // getDocumentFile implicitly creates the directory. return document.exists() } @@ -269,7 +278,11 @@ object FileAmazeFilesystem: AmazeFilesystem() { } else false } - override fun rename(file1: AmazeFile, file2: AmazeFile, contextProvider: ContextProvider): Boolean { + override fun rename( + file1: AmazeFile, + file2: AmazeFile, + contextProvider: ContextProvider + ): Boolean { return File(file1.path).renameTo(File(file2.path)) } @@ -300,4 +313,4 @@ object FileAmazeFilesystem: AmazeFilesystem() { override fun hashCode(f: AmazeFile): Int { return File(f.path).hashCode() } -} \ No newline at end of file +} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt index b306406675..3107841a77 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt @@ -17,23 +17,25 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.amaze.filemanager.file_operations.filesystem.filetypes.smb import android.net.Uri import android.text.TextUtils import android.util.Log -import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.createWithDisableIpcSigningCheck -import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem -import jcifs.smb.SmbFile -import jcifs.smb.NtlmPasswordAuthenticator import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.SMB_URI_PREFIX -import jcifs.smb.SmbException +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.CifsContexts.createWithDisableIpcSigningCheck import jcifs.SmbConstants +import jcifs.smb.NtlmPasswordAuthenticator +import jcifs.smb.SmbException +import jcifs.smb.SmbFile import java.io.IOException import java.io.InputStream import java.io.OutputStream +import java.lang.Boolean.parseBoolean import java.lang.IllegalArgumentException import java.lang.IllegalStateException import java.net.MalformedURLException @@ -46,7 +48,7 @@ import java.util.regex.Pattern * Relative paths are not supported * */ -object SmbAmazeFilesystem: AmazeFilesystem() { +object SmbAmazeFilesystem : AmazeFilesystem() { @JvmStatic val TAG = SmbAmazeFilesystem::class.java.simpleName @@ -66,7 +68,8 @@ object SmbAmazeFilesystem: AmazeFilesystem() { path } val uri = Uri.parse(processedPath) - val disableIpcSigningCheck = java.lang.Boolean.parseBoolean(uri.getQueryParameter(PARAM_DISABLE_IPC_SIGNING_CHECK)) + val disableIpcSigningCheck = + parseBoolean(uri.getQueryParameter(PARAM_DISABLE_IPC_SIGNING_CHECK)) val userInfo = uri.userInfo val noExtraInfoPath: String noExtraInfoPath = if (path.contains("?")) { @@ -75,7 +78,7 @@ object SmbAmazeFilesystem: AmazeFilesystem() { path } val context = createWithDisableIpcSigningCheck(path, disableIpcSigningCheck) - .withCredentials(createFrom(userInfo)) + .withCredentials(createFrom(userInfo)) return SmbFile(noExtraInfoPath, context) } @@ -137,7 +140,9 @@ object SmbAmazeFilesystem: AmazeFilesystem() { } override fun prefixLength(path: String): Int { - require(path.length != 0) { "This should never happen, all paths must start with SMB prefix" } + require(path.isNotEmpty()) { + "This should never happen, all paths must start with SMB prefix" + } val matcherMetadata = METADATA_PATTERN.matcher(path) if (matcherMetadata.find()) { return matcherMetadata.end() @@ -362,7 +367,11 @@ object SmbAmazeFilesystem: AmazeFilesystem() { } } - override fun rename(file1: AmazeFile, file2: AmazeFile, contextProvider: ContextProvider): Boolean { + override fun rename( + file1: AmazeFile, + file2: AmazeFile, + contextProvider: ContextProvider + ): Boolean { return try { create(file1.path).renameTo(create(file2.path)) true @@ -422,4 +431,4 @@ object SmbAmazeFilesystem: AmazeFilesystem() { // TODO: Find total storage space of SMB when JCIFS adds support throw NotImplementedError() } -} \ No newline at end of file +} From c3067bab30c4eb8fd6c3b63d2c748c90d92d4b96 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 29 Jan 2022 13:33:37 -0300 Subject: [PATCH 35/46] Added onFileFound to AmazeFile --- .../filesystem/filetypes/AmazeFile.kt | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index 0eade8f090..e0cc608e17 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -792,10 +792,17 @@ class AmazeFile : Comparable { * error occurs. */ fun listFiles(contextProvider: ContextProvider): Array? { + val files = ArrayList() + listFiles(contextProvider, files::add) ?: return null + return files.toTypedArray() + } + + fun listFiles(contextProvider: ContextProvider, onFileFound: (AmazeFile) -> Unit): Unit? { val ss = list(contextProvider) ?: return null - return Array(ss.size) { i: Int -> - AmazeFile(ss[i], this) + for (i in ss.indices) { + onFileFound(AmazeFile(ss[i], this)) } + return Unit } /** @@ -818,12 +825,25 @@ class AmazeFile : Comparable { filter: AmazeFilenameFilter?, contextProvider: ContextProvider ): Array? { - val ss = list(contextProvider) ?: return null val files = ArrayList() - for (s in ss) if (filter == null || filter.accept(this, s)) files.add(AmazeFile(s!!, this)) + listFiles(filter, contextProvider, files::add) ?: return null return files.toTypedArray() } + fun listFiles( + filter: AmazeFilenameFilter?, + contextProvider: ContextProvider, + onFileFound: (AmazeFile) -> Unit + ): Unit? { + val ss = list(contextProvider) ?: return null + for (s in ss) { + if (filter == null || filter.accept(this, s)) { + onFileFound(AmazeFile(s, this)) + } + } + return Unit + } + /** * Returns an array of abstract pathnames denoting the files and directories in the directory * denoted by this abstract pathname that satisfy the specified filter. The behavior of this @@ -841,13 +861,36 @@ class AmazeFile : Comparable { * @see java.nio.file.Files.newDirectoryStream */ fun listFiles(filter: AmazeFileFilter?, contextProvider: ContextProvider): Array? { - val ss = list(contextProvider) ?: return null val files = ArrayList() + forFiles(filter, contextProvider, files::add) ?: return null + return files.toTypedArray() + } + + /** + * Calls a function on an array of abstract pathnames denoting the files and directories + * in the directory denoted by this abstract pathname that satisfy the specified filter. + * The behavior of this method is the same as that of the [listFiles] method, except that the + * pathnames in the returned array must satisfy the filter. If the given `filter` is `null` then + * all pathnames are accepted. Otherwise, a pathname satisfies the filter if and only if the value + * `true` results when the [FileFilter.accept] method of the + * filter is invoked on the pathname. + * + * @param filter A file filter + * @param onFileFound the function called on every file and directory in the directory + * denoted by this abstract pathname. Returns `null` if this abstract pathname does + * not denote a directory, or if an I/O error occurs. + * @see java.nio.file.Files.newDirectoryStream + */ + fun forFiles(filter: AmazeFileFilter?, contextProvider: ContextProvider, + onFileFound: (AmazeFile) -> Unit): Unit? { + val ss = list(contextProvider) ?: return null for (s in ss) { val f = AmazeFile(s, this) - if (filter == null || filter.accept(f)) files.add(f) + if (filter == null || filter.accept(f)) { + onFileFound(f) + } } - return files.toTypedArray() + return Unit } fun getInputStream(contextProvider: ContextProvider): InputStream { From f60cada2fb5c1f09d286c0ee770b8ce66d1dacbf Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sun, 6 Feb 2022 11:51:40 -0300 Subject: [PATCH 36/46] Fix some rootmode operations --- .../adapters/AppsRecyclerAdapter.kt | 1 + .../filesystem/ExternalSdCardOperation.kt | 3 - .../filemanager/filesystem/HybridFile.java | 59 +------- .../files/DocumentFileAmazeFilesystem.kt | 9 +- .../filesystem/files}/FileAmazeFilesystem.kt | 141 ++++++++++-------- .../PreferencesConstants.kt | 5 +- .../filesystem/filetypes/AmazeFilesystem.kt | 2 +- 7 files changed, 95 insertions(+), 125 deletions(-) rename {file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file => app/src/main/java/com/amaze/filemanager/filesystem/files}/FileAmazeFilesystem.kt (71%) diff --git a/app/src/main/java/com/amaze/filemanager/adapters/AppsRecyclerAdapter.kt b/app/src/main/java/com/amaze/filemanager/adapters/AppsRecyclerAdapter.kt index ed22fa5349..959c2f78ee 100644 --- a/app/src/main/java/com/amaze/filemanager/adapters/AppsRecyclerAdapter.kt +++ b/app/src/main/java/com/amaze/filemanager/adapters/AppsRecyclerAdapter.kt @@ -52,6 +52,7 @@ import com.amaze.filemanager.asynchronous.asynctasks.DeleteTask import com.amaze.filemanager.asynchronous.management.ServiceWatcherUtil import com.amaze.filemanager.asynchronous.services.CopyService import com.amaze.filemanager.file_operations.filesystem.OpenMode +import com.amaze.filemanager.filesystem.files.FileAmazeFilesystem import com.amaze.filemanager.filesystem.HybridFileParcelable import com.amaze.filemanager.filesystem.RootHelper import com.amaze.filemanager.filesystem.files.FileUtils diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ExternalSdCardOperation.kt b/app/src/main/java/com/amaze/filemanager/filesystem/ExternalSdCardOperation.kt index db4e344bcd..19996bca56 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ExternalSdCardOperation.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ExternalSdCardOperation.kt @@ -26,10 +26,7 @@ import android.net.Uri import android.os.Build import android.util.Log import androidx.documentfile.provider.DocumentFile -import androidx.preference.PreferenceManager -import com.amaze.filemanager.file_operations.filesystem.filetypes.file.FileAmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.file.UriForSafPersistance -import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants import java.io.File import java.io.IOException import java.util.* diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 8f31f61b37..1fbbe8f0f9 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -42,11 +42,6 @@ import com.amaze.filemanager.file_operations.exceptions.ShellNotRunningException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; -import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAccount; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAccount; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAccount; -import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAccount; import com.amaze.filemanager.file_operations.filesystem.root.NativeOperations; import com.amaze.filemanager.filesystem.cloud.CloudUtil; import com.amaze.filemanager.filesystem.files.DocumentFileAmazeFilesystem; @@ -54,19 +49,13 @@ import com.amaze.filemanager.filesystem.root.DeleteFileCommand; import com.amaze.filemanager.filesystem.root.ListFilesCommand; import com.amaze.filemanager.filesystem.ssh.SFtpClientTemplate; -import com.amaze.filemanager.filesystem.ssh.SshClientTemplate; import com.amaze.filemanager.filesystem.ssh.SshClientUtils; import com.amaze.filemanager.filesystem.ssh.SshConnectionPool; -import com.amaze.filemanager.filesystem.ssh.Statvfs; import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants; import com.amaze.filemanager.utils.DataUtils; import com.amaze.filemanager.utils.OTGUtil; import com.amaze.filemanager.utils.OnFileFound; -import com.amaze.filemanager.utils.Utils; -import com.cloudrail.si.interfaces.CloudStorage; -import com.cloudrail.si.types.SpaceAllocation; -import android.content.ContentResolver; import android.content.Context; import android.net.Uri; import android.os.Build; @@ -79,13 +68,9 @@ import jcifs.smb.SmbException; import jcifs.smb.SmbFile; -import net.schmizz.sshj.SSHClient; -import net.schmizz.sshj.common.Buffer; -import net.schmizz.sshj.sftp.FileMode; -import net.schmizz.sshj.sftp.RemoteFile; + import net.schmizz.sshj.sftp.RemoteResourceInfo; import net.schmizz.sshj.sftp.SFTPClient; -import net.schmizz.sshj.sftp.SFTPException; /** * Hybrid file for handeling all types of files @@ -756,35 +741,12 @@ public OutputStream getOutputStream(Context context) { } public boolean exists() { - if (isSmb() || isLocal() || isDropBoxFile() || isBoxFile() || isGoogleDriveFile() - || isOneDriveFile() || isDocumentFile() || isSftp()) { - return new AmazeFile(path).exists(() -> null); - } else if (isRoot()) { - return RootHelper.fileExists(path); - } - - return false; + return new AmazeFile(path).exists(() -> null); } /** Helper method to check file existence in otg */ public boolean exists(Context context) { - boolean exists = false; - try { - if (isSmb() || isLocal() || isDropBoxFile() || isBoxFile() || isGoogleDriveFile() - || isOneDriveFile() || isOtgFile() || isSftp() || isDocumentFile()) { - return new AmazeFile(path).exists(() -> context); - } else if (isDocumentFile()) { - exists = - OTGUtil.getDocumentFile( - path, SafRootHolder.getUriRoot(), context, OpenMode.DOCUMENT_FILE, false) - != null; - } else { - return (exists()); - } - } catch (Exception e) { - Log.i(getClass().getSimpleName(), "Failed to find file", e); - } - return exists; + return new AmazeFile(path).exists(() -> context); } /** @@ -807,22 +769,11 @@ public boolean isSimpleFile() { } public boolean setLastModified(final long date) { - if (isSmb() || isLocal() || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() - || isDropBoxFile() || isSftp() || isOtgFile()) { - return new AmazeFile(path).setLastModified(date); - } - File f = getFile(); - return f.setLastModified(date); + return new AmazeFile(path).setLastModified(date); } public void mkdir(Context context) { - if (isSftp() || isSmb() || isLocal() || isRoot() || isCustomPath() || isUnknownFile() - || isOneDriveFile() || isBoxFile() || isGoogleDriveFile() || isDropBoxFile() - || isOtgFile() || isDocumentFile()) { - new AmazeFile(path).mkdirs(() -> context); - } else { - throw new IllegalStateException(); - } + new AmazeFile(path).mkdirs(() -> context); } public boolean delete(Context context, boolean rootmode) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt b/app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt index e06dec1fe7..b0575effbe 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/DocumentFileAmazeFilesystem.kt @@ -11,7 +11,6 @@ import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvide import com.amaze.filemanager.filesystem.FileProperties.getDeviceStorageRemainingSpace import com.amaze.filemanager.filesystem.SafRootHolder.uriRoot import com.amaze.filemanager.filesystem.SafRootHolder.volumeLabel -import com.amaze.filemanager.filesystem.otg.OtgAmazeFilesystem import com.amaze.filemanager.utils.OTGUtil.getDocumentFile import java.io.FileNotFoundException import java.io.IOException @@ -54,8 +53,12 @@ object DocumentFileAmazeFilesystem: AmazeFilesystem() { } override fun exists(f: AmazeFile, contextProvider: ContextProvider): Boolean { - val documentFile = getDocumentFile(f.path, false) ?: return false - return documentFile.exists() + val uriRoot = uriRoot ?: return false + val context = contextProvider.getContext() ?: return false + val documentFile = getDocumentFile( + f.path, uriRoot, context, OpenMode.DOCUMENT_FILE, false + ) + return documentFile != null } override fun isFile(f: AmazeFile, contextProvider: ContextProvider): Boolean { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileAmazeFilesystem.kt similarity index 71% rename from file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt rename to app/src/main/java/com/amaze/filemanager/filesystem/files/FileAmazeFilesystem.kt index 971929ba34..7d1b321468 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/file/FileAmazeFilesystem.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileAmazeFilesystem.kt @@ -1,35 +1,18 @@ -/* - * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes.file +package com.amaze.filemanager.filesystem.files import android.content.ContentValues import android.os.Build import android.provider.MediaStore import android.util.Log +import androidx.preference.PreferenceManager import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider -import com.amaze.filemanager.file_operations.filesystem.filetypes.file.ExternalSdCardOperation.getDocumentFile -import com.amaze.filemanager.file_operations.filesystem.filetypes.file.ExternalSdCardOperation.isOnExtSdCard -import com.amaze.filemanager.file_operations.filesystem.filetypes.file.UriForSafPersistance.get +import com.amaze.filemanager.file_operations.filesystem.filetypes.file.ExternalSdCardOperation +import com.amaze.filemanager.file_operations.filesystem.filetypes.file.MediaStoreHack +import com.amaze.filemanager.file_operations.filesystem.filetypes.file.UriForSafPersistance +import com.amaze.filemanager.filesystem.RootHelper +import com.amaze.filemanager.filesystem.root.MakeDirectoryCommand import java.io.* /** @@ -37,6 +20,8 @@ import java.io.* * through java.io.File */ object FileAmazeFilesystem : AmazeFilesystem() { + const val PREFERENCE_ROOTMODE = "rootmode" + @JvmStatic val TAG = FileAmazeFilesystem::class.java.simpleName @@ -48,8 +33,7 @@ object FileAmazeFilesystem : AmazeFilesystem() { return path[0] == getSeparator() } - override val prefix: String - get() = throw NotImplementedError() + override val prefix: String = "" override fun getSeparator(): Char { return File.separatorChar @@ -85,7 +69,11 @@ object FileAmazeFilesystem : AmazeFilesystem() { } override fun exists(f: AmazeFile, contextProvider: ContextProvider): Boolean { - return f.exists(contextProvider) + return runAsNormalOrRoot(f, contextProvider, { + File(f.path).exists() + }) { + RootHelper.fileExists(f.path) + } } override fun isFile(f: AmazeFile, contextProvider: ContextProvider): Boolean { @@ -143,6 +131,7 @@ object FileAmazeFilesystem : AmazeFilesystem() { } override fun delete(f: AmazeFile, contextProvider: ContextProvider): Boolean { + if (f.isDirectory(contextProvider)) { val children = f.listFiles(contextProvider) if (children != null) { @@ -159,8 +148,8 @@ object FileAmazeFilesystem : AmazeFilesystem() { // Try with Storage Access Framework. if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - val document = getDocumentFile( - f, true, context, get(context) + val document = ExternalSdCardOperation.getDocumentFile( + f, true, context, UriForSafPersistance.get(context) ) if (document != null && document.delete()) { return true @@ -176,7 +165,7 @@ object FileAmazeFilesystem : AmazeFilesystem() { // Delete the created entry, such that content provider will delete the file. resolver.delete( - MediaStore.Files.getContentUri("external"), + MediaStore.Files.getContentUri("external"), MediaStore.MediaColumns.DATA + "=?", arrayOf(f.absolutePath) ) } @@ -189,10 +178,10 @@ object FileAmazeFilesystem : AmazeFilesystem() { // Try with Storage Access Framework. if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && - isOnExtSdCard(f, context) + ExternalSdCardOperation.isOnExtSdCard(f, context) ) { - val document = getDocumentFile( - f, false, context, get(context) + val document = ExternalSdCardOperation.getDocumentFile( + f, false, context, UriForSafPersistance.get(context) ) ?: return true if (document.delete()) { return true @@ -235,8 +224,8 @@ object FileAmazeFilesystem : AmazeFilesystem() { val context = contextProvider.getContext() ?: return null if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Storage Access Framework - val targetDocument = getDocumentFile( - f, false, context, get(context) + val targetDocument = ExternalSdCardOperation.getDocumentFile( + f, false, context, UriForSafPersistance.get(context) ) ?: return null return context.contentResolver.openOutputStream(targetDocument.uri) } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { @@ -252,36 +241,42 @@ object FileAmazeFilesystem : AmazeFilesystem() { } override fun createDirectory(f: AmazeFile, contextProvider: ContextProvider): Boolean { - if (File(f.path).mkdir()) { - return true - } - val context = contextProvider.getContext() - - // Try with Storage Access Framework. - if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && - isOnExtSdCard(f, context) - ) { - val preferenceUri = get(context) - val document = getDocumentFile(f, true, context, preferenceUri) - ?: return false - // getDocumentFile implicitly creates the directory. - return document.exists() - } + return runAsNormalOrRoot(f, contextProvider, { + if (File(f.path).mkdir()) { + return@runAsNormalOrRoot true + } + val context = contextProvider.getContext() - // Try the Kitkat workaround. - return if (context != null && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { - try { - MediaStoreHack.mkdir(context, f) - } catch (e: IOException) { - false + // Try with Storage Access Framework. + if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && + ExternalSdCardOperation.isOnExtSdCard(f, context) + ) { + val preferenceUri = UriForSafPersistance.get(context) + val document = ExternalSdCardOperation.getDocumentFile(f, true, context, preferenceUri) + ?: return@runAsNormalOrRoot false + // getDocumentFile implicitly creates the directory. + return@runAsNormalOrRoot document.exists() } - } else false + + // Try the Kitkat workaround. + return@runAsNormalOrRoot if (context != null && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + try { + MediaStoreHack.mkdir(context, f) + } catch (e: IOException) { + false + } + } else false + }) { + val parent = f.parent ?: return@runAsNormalOrRoot false + MakeDirectoryCommand.makeDirectory(parent, f.name) + return@runAsNormalOrRoot true + } } override fun rename( - file1: AmazeFile, - file2: AmazeFile, - contextProvider: ContextProvider + file1: AmazeFile, + file2: AmazeFile, + contextProvider: ContextProvider ): Boolean { return File(file1.path).renameTo(File(file2.path)) } @@ -313,4 +308,28 @@ object FileAmazeFilesystem : AmazeFilesystem() { override fun hashCode(f: AmazeFile): Int { return File(f.path).hashCode() } -} + + private fun runAsNormalOrRoot(file: AmazeFile, contextProvider: ContextProvider, + normalMode: () -> T, rootMode: () -> T): T { + val context = contextProvider.getContext() ?: return normalMode() + + val rootmode = PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean(PREFERENCE_ROOTMODE, false) + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + if (rootmode && !canRead(file, contextProvider)) { + return rootMode() + } + + return normalMode() + } + + if (ExternalSdCardOperation.isOnExtSdCard(file, context)) { + return normalMode() + } else if (rootmode && !canRead(file, contextProvider)) { + return rootMode() + } + + return normalMode() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt index 1ac49653ff..0bc0223dcd 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt @@ -20,8 +20,7 @@ package com.amaze.filemanager.ui.fragments.preference_fragments -import com.amaze.filemanager.file_operations.filesystem.filetypes.file.FileAmazeFilesystem -import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants.PREFERENCE_ROOTMODE +import com.amaze.filemanager.filesystem.files.FileAmazeFilesystem object PreferencesConstants { // appearance_prefs.xml @@ -71,7 +70,7 @@ object PreferencesConstants { // behavior_prefs.xml const val PREFERENCE_ROOT_LEGACY_LISTING = "legacyListing" - const val PREFERENCE_ROOTMODE = "rootmode" + const val PREFERENCE_ROOTMODE = FileAmazeFilesystem.PREFERENCE_ROOTMODE const val PREFERENCE_CHANGEPATHS = "typeablepaths" const val PREFERENCE_SAVED_PATHS = "savepaths" const val PREFERENCE_ZIP_EXTRACT_PATH = "extractpath" diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt index 67f596f91c..283a9fa59a 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt @@ -267,7 +267,7 @@ abstract class AmazeFilesystem { return `val`.equals("true", ignoreCase = true) } - /* + /* * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * From dc81f49eac908b78bd3dc4f7813525d1db938957 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 19 Feb 2022 15:23:31 -0300 Subject: [PATCH 37/46] Added hacks so that AmazeFilesystem subclasses actually load --- .../filesystem/FilesystemLoader.kt | 20 ++++++++++++++++ .../filemanager/filesystem/HybridFile.java | 4 ++++ .../filesystem/filetypes/AmazeFile.kt | 23 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/FilesystemLoader.kt diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/FilesystemLoader.kt b/app/src/main/java/com/amaze/filemanager/filesystem/FilesystemLoader.kt new file mode 100644 index 0000000000..c347ba1be5 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/filesystem/FilesystemLoader.kt @@ -0,0 +1,20 @@ +package com.amaze.filemanager.filesystem + +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFilesystem +import com.amaze.filemanager.filesystem.files.FileAmazeFilesystem +import com.amaze.filemanager.filesystem.otg.OtgAmazeFilesystem +import com.amaze.filemanager.filesystem.ssh.SshAmazeFilesystem + +/** + * TODO remove this by moving all Filesystem subclasses to file_operations + */ +object FilesystemLoader { + init { + AmazeFile //Loads all of the file_operations Filesystem subclasses + FileAmazeFilesystem + OtgAmazeFilesystem + SmbAmazeFilesystem + SshAmazeFilesystem.INSTANCE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java index 1fbbe8f0f9..ffb09190f7 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java @@ -88,6 +88,10 @@ public class HybridFile { private final DataUtils dataUtils = DataUtils.getInstance(); + static { + FilesystemLoader.INSTANCE.toString(); // HACK forces the class to load + } + public HybridFile(OpenMode mode, String path) { this.path = path; this.mode = mode; diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index e0cc608e17..8d8537c3bd 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -22,6 +22,11 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes import android.os.Parcel import android.util.Log +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.dropbox.DropboxAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.gdrive.GoogledriveAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.onedrive.OnedriveAmazeFilesystem +import com.amaze.filemanager.file_operations.filesystem.filetypes.smb.SmbAmazeFilesystem import kotlinx.parcelize.Parceler import java.io.File import java.io.IOException @@ -117,7 +122,9 @@ import java.util.* */ class AmazeFile : Comparable { companion object { + @JvmStatic val TAG = AmazeFile::class.java.simpleName + @JvmStatic private val filesystems: MutableList = ArrayList() @JvmStatic @@ -125,6 +132,15 @@ class AmazeFile : Comparable { filesystems.add(amazeFilesystem) } + init { + BoxAmazeFilesystem + DropboxAmazeFilesystem + GoogledriveAmazeFilesystem + OnedriveAmazeFilesystem + + SmbAmazeFilesystem + } + private fun slashify(path: String, isDirectory: Boolean): String { var p = path if (File.separatorChar != '/') p = p.replace(File.separatorChar, '/') @@ -325,11 +341,18 @@ class AmazeFile : Comparable { } private fun loadFilesystem(path: String) { + var loadedAFs = false + for (filesystem in filesystems) { if (filesystem.isPathOfThisFilesystem(path)) { fs = filesystem + loadedAFs = true } } + + if(!loadedAFs) { + Log.e(TAG, "Failed to load a filesystem, did you forget to add the class to the [AmazeFile]'s companion object initialization block?") + } } /* -- Path-component accessors -- */ /** From b1f9d54a26c688f9721dd4b9729a25514803d352 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 19 Feb 2022 15:43:26 -0300 Subject: [PATCH 38/46] Apply spotless --- .../filesystem/filetypes/AmazeFile.kt | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index 8d8537c3bd..c9a368fa56 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -350,8 +350,12 @@ class AmazeFile : Comparable { } } - if(!loadedAFs) { - Log.e(TAG, "Failed to load a filesystem, did you forget to add the class to the [AmazeFile]'s companion object initialization block?") + if (!loadedAFs) { + Log.e( + TAG, + "Failed to load a filesystem, did you forget to add the class to the " + + "[AmazeFile]'s companion object initialization block?" + ) } } /* -- Path-component accessors -- */ @@ -854,9 +858,9 @@ class AmazeFile : Comparable { } fun listFiles( - filter: AmazeFilenameFilter?, - contextProvider: ContextProvider, - onFileFound: (AmazeFile) -> Unit + filter: AmazeFilenameFilter?, + contextProvider: ContextProvider, + onFileFound: (AmazeFile) -> Unit ): Unit? { val ss = list(contextProvider) ?: return null for (s in ss) { @@ -904,8 +908,11 @@ class AmazeFile : Comparable { * not denote a directory, or if an I/O error occurs. * @see java.nio.file.Files.newDirectoryStream */ - fun forFiles(filter: AmazeFileFilter?, contextProvider: ContextProvider, - onFileFound: (AmazeFile) -> Unit): Unit? { + fun forFiles( + filter: AmazeFileFilter?, + contextProvider: ContextProvider, + onFileFound: (AmazeFile) -> Unit + ): Unit? { val ss = list(contextProvider) ?: return null for (s in ss) { val f = AmazeFile(s, this) From 78102ffcf7d2abf30f90936e1311eb558d3dac88 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Sat, 19 Feb 2022 20:59:19 -0300 Subject: [PATCH 39/46] Fix a small test of SshFilesystemAmaze --- .../amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java index 73eeba6b65..815fbab96a 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -167,7 +167,7 @@ public boolean canRead(AmazeFile f, @NonNull ContextProvider contextProvider) { throw new NotImplementedError(); } public boolean canAccess(AmazeFile f, @NonNull ContextProvider contextProvider) { - throw new NotImplementedError(); + return exists(f, contextProvider); } public boolean setExecutable(AmazeFile f, boolean enable, boolean owneronly) { From 6f13d977a909f27b7bd8de8347812b3536092081 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Tue, 19 Apr 2022 21:19:00 -0300 Subject: [PATCH 40/46] Remove isInvalid for AmazeFile --- .../filesystem/filetypes/AmazeFile.kt | 140 ++++++------------ 1 file changed, 44 insertions(+), 96 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index c9a368fa56..7247e15b2c 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -171,10 +171,6 @@ class AmazeFile : Comparable { */ val path: String - /** The flag indicating whether the file path is invalid. */ - @Transient - private var status: PathStatus? = null - /** Returns the length of this abstract pathname's prefix. For use by FileSystem classes. */ /** The length of this abstract pathname's prefix, or zero if it has no prefix. */ @Transient @@ -195,26 +191,7 @@ class AmazeFile : Comparable { * This string contains a single character, namely `[.separatorChar]`. */ val separator: String - - /** Enum type that indicates the status of a file path. */ - private enum class PathStatus { - INVALID, CHECKED - } - - /** - * Check if the file has an invalid path. Currently, the inspection of a file path is very - * limited, and it only covers Nul character check. Returning true means the path is definitely - * invalid/garbage. But returning false does not guarantee that the path is valid. - * - * @return true if the file path is invalid. - */ - val isInvalid: Boolean - get() { - if (status == null) { - status = if (path.indexOf('\u0000') < 0) PathStatus.CHECKED else PathStatus.INVALID - } - return status == PathStatus.INVALID - } + /* -- Constructors -- */ /** Internal constructor for already-normalized pathname strings. */ private constructor(pathname: String, prefixLength: Int) { @@ -497,9 +474,6 @@ class AmazeFile : Comparable { @get:Throws(IOException::class) val canonicalPath: String get() { - if (isInvalid) { - throw IOException("Invalid file path") - } return fs.canonicalize(fs.resolve(this)) } @@ -528,9 +502,7 @@ class AmazeFile : Comparable { * *and* can be read by the application; `false` otherwise */ fun canRead(contextProvider: ContextProvider): Boolean { - return if (isInvalid) { - false - } else fs.canRead(this, contextProvider) + return fs.canRead(this, contextProvider) } // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on android @@ -542,9 +514,7 @@ class AmazeFile : Comparable { * false` otherwise. */ fun canWrite(contextProvider: ContextProvider): Boolean { - return if (isInvalid) { - false - } else fs.canWrite(this, contextProvider) + return fs.canWrite(this, contextProvider) } /** @@ -554,9 +524,7 @@ class AmazeFile : Comparable { * pathname exists; `false` otherwise */ fun exists(contextProvider: ContextProvider): Boolean { - return if (isInvalid) { - false - } else fs.canAccess(this, contextProvider) + return fs.canAccess(this, contextProvider) // Android-changed: b/25878034 work around SELinux stat64 denial. } @@ -574,9 +542,7 @@ class AmazeFile : Comparable { * *and* is a directory; `false` otherwise */ fun isDirectory(contextProvider: ContextProvider): Boolean { - return if (isInvalid) { - false - } else fs.getBooleanAttributes(this, contextProvider) and AmazeFilesystem.BA_DIRECTORY != 0 + return fs.getBooleanAttributes(this, contextProvider) and AmazeFilesystem.BA_DIRECTORY != 0 } /** @@ -595,9 +561,7 @@ class AmazeFile : Comparable { * *and* is a normal file; `false` otherwise */ fun isFile(contextProvider: ContextProvider): Boolean { - return if (isInvalid) { - false - } else fs.getBooleanAttributes(this, contextProvider) and AmazeFilesystem.BA_REGULAR != 0 + return fs.getBooleanAttributes(this, contextProvider) and AmazeFilesystem.BA_REGULAR != 0 } /** @@ -610,9 +574,7 @@ class AmazeFile : Comparable { * according to the conventions of the underlying platform */ fun isHidden(contextProvider: ContextProvider): Boolean { - return if (isInvalid) { - false - } else fs.getBooleanAttributes(this, contextProvider) and AmazeFilesystem.BA_HIDDEN != 0 + return fs.getBooleanAttributes(this, contextProvider) and AmazeFilesystem.BA_HIDDEN != 0 } /** @@ -629,9 +591,7 @@ class AmazeFile : Comparable { * file does not exist or if an I/O error occurs */ fun lastModified(): Long { - return if (isInvalid) { - 0L - } else fs.getLastModifiedTime(this) + return fs.getLastModifiedTime(this) } /** @@ -650,10 +610,30 @@ class AmazeFile : Comparable { */ @Throws(IOException::class) fun length(contextProvider: ContextProvider): Long { - return if (isInvalid) { + return fs.getLength(this, contextProvider) + } + + /** + * Returns the length of the file or directory denoted by this abstract pathname. + * + * Where it is required to distinguish an I/O exception from the case that `0L` is + * returned, or where several attributes of the same file are required at the same time, then the + * [Files.readAttributes][java.nio.file.Files.readAttributes] method + * may be used. + * + * @return The length, in bytes, of the file denoted by this abstract pathname, or `0L` + * if the file does not exist. Some operating systems may return `0L` for pathnames + * denoting system-dependent entities such as devices or pipes. + */ + fun safeLength(contextProvider: ContextProvider): Long { + return try { + length(contextProvider) + } catch (e: IOException) { + Log.e(TAG, "Error getting file size", e) 0L - } else fs.getLength(this, contextProvider) + } } + /* -- File operations -- */ /** * Atomically creates a new, empty file named by this abstract pathname if and only if a file with @@ -672,9 +652,6 @@ class AmazeFile : Comparable { */ @Throws(IOException::class) fun createNewFile(): Boolean { - if (isInvalid) { - throw IOException("Invalid file path") - } return fs.createFileExclusively(path) } @@ -690,9 +667,7 @@ class AmazeFile : Comparable { * false` otherwise */ fun delete(contextProvider: ContextProvider): Boolean { - return if (isInvalid) { - false - } else fs.delete(this, contextProvider) + return fs.delete(this, contextProvider) } // Android-added: Additional information about Android behaviour. /** @@ -728,9 +703,6 @@ class AmazeFile : Comparable { * @see .delete */ fun deleteOnExit() { - if (isInvalid) { - return - } AmazeDeleteOnExitHook.add(path) } @@ -757,9 +729,7 @@ class AmazeFile : Comparable { * if this abstract pathname does not denote a directory, or if an I/O error occurs. */ fun list(contextProvider: ContextProvider): Array? { - return if (isInvalid) { - null - } else fs.list(this, contextProvider) + return fs.list(this, contextProvider) } /** @@ -938,9 +908,7 @@ class AmazeFile : Comparable { * otherwise */ fun mkdir(contextProvider: ContextProvider): Boolean { - return if (isInvalid) { - false - } else fs.createDirectory(this, contextProvider) + return fs.createDirectory(this, contextProvider) } /** @@ -995,9 +963,7 @@ class AmazeFile : Comparable { * @return `true` if and only if the renaming succeeded; `false` otherwise */ fun renameTo(dest: AmazeFile, contextProvider: ContextProvider): Boolean { - return if (isInvalid || dest.isInvalid) { - false - } else fs.rename(this, dest, contextProvider) + return fs.rename(this, dest, contextProvider) } /** @@ -1017,9 +983,7 @@ class AmazeFile : Comparable { */ fun setLastModified(time: Long): Boolean { require(time >= 0) { "Negative time" } - return if (isInvalid) { - false - } else fs.setLastModifiedTime(this, time) + return fs.setLastModifiedTime(this, time) } // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. @@ -1032,9 +996,7 @@ class AmazeFile : Comparable { * @return `true` if and only if the operation succeeded; `false` otherwise */ fun setReadOnly(): Boolean { - return if (isInvalid) { - false - } else fs.setReadOnly(this) + return fs.setReadOnly(this) } // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. @@ -1057,9 +1019,7 @@ class AmazeFile : Comparable { * pathname. */ fun setWritable(writable: Boolean, ownerOnly: Boolean): Boolean { - return if (isInvalid) { - false - } else fs.setWritable(this, writable, ownerOnly) + return fs.setWritable(this, writable, ownerOnly) } // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. @@ -1104,9 +1064,7 @@ class AmazeFile : Comparable { * does not implement a read permission, then the operation will fail. */ fun setReadable(readable: Boolean, ownerOnly: Boolean): Boolean { - return if (isInvalid) { - false - } else fs.setReadable(this, readable, ownerOnly) + return fs.setReadable(this, readable, ownerOnly) } // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. @@ -1152,9 +1110,7 @@ class AmazeFile : Comparable { * does not implement an execute permission, then the operation will fail. */ fun setExecutable(executable: Boolean, ownerOnly: Boolean): Boolean { - return if (isInvalid) { - false - } else fs.setExecutable(this, executable, ownerOnly) + return fs.setExecutable(this, executable, ownerOnly) } // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on Android. @@ -1187,9 +1143,7 @@ class AmazeFile : Comparable { * application is allowed to execute the file */ fun canExecute(contextProvider: ContextProvider): Boolean { - return if (isInvalid) { - false - } else fs.canExecute(this, contextProvider) + return fs.canExecute(this, contextProvider) } /* -- Filesystem interface -- */ // Android-changed: Replaced generic platform info with Android specific one. /* -- Disk usage -- */ @@ -1200,9 +1154,7 @@ class AmazeFile : Comparable { * name a partition If there is no way to determine, total space is -1 */ fun getTotalSpace(contextProvider: ContextProvider): Long { - return if (isInvalid) { - 0L - } else try { + return try { fs.getTotalSpace(this, contextProvider) } catch (e: NotImplementedError) { Log.w(TAG, "Call to unimplemented fuction", e) @@ -1226,9 +1178,7 @@ class AmazeFile : Comparable { * system size returned by [.getTotalSpace]. */ val freeSpace: Long - get() = if (isInvalid) { - 0L - } else fs.getFreeSpace(this) + get() = fs.getFreeSpace(this) // Android-added: Replaced generic platform info with Android specific one. /** * Returns the number of bytes available to this virtual machine on the partition [named](#partName) by this abstract pathname. When possible, this method checks for @@ -1253,9 +1203,7 @@ class AmazeFile : Comparable { * current space left -1 is returned. */ val usableSpace: Long - get() = if (isInvalid) { - 0L - } else try { + get() = try { fs.getUsableSpace(this) } catch (e: NotImplementedError) { Log.w(TAG, "Call to unimplemented fuction", e) @@ -1282,7 +1230,7 @@ class AmazeFile : Comparable { // prefix = (new AmazeFile(prefix)).getName(); val name = prefix + java.lang.Long.toString(n) + suffix val f = AmazeFile(dir, name) - if (name != f.name || f.isInvalid) { + if (name != f.name) { if (System.getSecurityManager() != null) { throw IOException("Unable to create temporary file") } else { From 73b49d51556b91225506409afa93f80a1c95faf9 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Tue, 19 Apr 2022 21:21:37 -0300 Subject: [PATCH 41/46] Remove AmazeFile.deleteOnExit --- .../filetypes/AmazeDeleteOnExitHook.java | 75 ------------------- .../filesystem/filetypes/AmazeFile.kt | 36 --------- 2 files changed, 111 deletions(-) delete mode 100644 file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java deleted file mode 100644 index 924f1e1d31..0000000000 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeDeleteOnExitHook.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2014-2010 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager 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 com.amaze.filemanager.file_operations.filesystem.filetypes; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; - -/** - * This class holds a set of filenames to be deleted on VM exit through a shutdown hook. A set is - * used both to prevent double-insertion of the same file as well as offer quick removal. - */ -public class AmazeDeleteOnExitHook { - private static LinkedHashSet files = new LinkedHashSet<>(); - - static { - // BEGIN Android-changed: Use Runtime.addShutdownHook() rather than SharedSecrets. - Runtime.getRuntime() - .addShutdownHook( - new Thread() { - public void run() { - runHooks(); - } - }); - // END Android-changed: Use Runtime.addShutdownHook() rather than SharedSecrets. - } - - private AmazeDeleteOnExitHook() {} - - static synchronized void add(String file) { - if (files == null) { - // DeleteOnExitHook is running. Too late to add a file - throw new IllegalStateException("Shutdown in progress"); - } - - files.add(file); - } - - static void runHooks() { - LinkedHashSet theFiles; - - synchronized (AmazeDeleteOnExitHook.class) { - theFiles = files; - files = null; - } - - ArrayList toBeDeleted = new ArrayList<>(theFiles); - - // reverse the list to maintain previous jdk deletion order. - // Last in first deleted. - Collections.reverse(toBeDeleted); - for (String filename : toBeDeleted) { - (new File(filename)).delete(); - } - } -} diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index 7247e15b2c..e7bb0e1054 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -669,42 +669,6 @@ class AmazeFile : Comparable { fun delete(contextProvider: ContextProvider): Boolean { return fs.delete(this, contextProvider) } - // Android-added: Additional information about Android behaviour. - /** - * Requests that the file or directory denoted by this abstract pathname be deleted when the - * virtual machine terminates. Files (or directories) are deleted in the reverse order that they - * are registered. Invoking this method to delete a file or directory that is already registered - * for deletion has no effect. Deletion will be attempted only for normal termination of the - * virtual machine, as defined by the Java Language Specification. - * - * - * Once deletion has been requested, it is not possible to cancel the request. This method - * should therefore be used with care. - * - * - * Note: this method should *not* be used for file-locking, as the resulting protocol - * cannot be made to work reliably. The [FileLock][java.nio.channels.FileLock] facility - * should be used instead. - * - * - * *Note that on Android, the application lifecycle does not include VM termination, so - * calling this method will not ensure that files are deleted*. Instead, you should use the - * most appropriate out of: - * - * - * * Use a `finally` clause to manually invoke [.delete]. - * * Maintain your own set of files to delete, and process it at an appropriate point in your - * application's lifecycle. - * * Use the Unix trick of deleting the file as soon as all readers and writers have opened - * it. No new readers/writers will be able to access the file, but all existing ones will - * still have access until the last one closes the file. - * - * - * @see .delete - */ - fun deleteOnExit() { - AmazeDeleteOnExitHook.add(path) - } /** * Returns an array of strings naming the files and directories in the directory denoted by this From bec6231494216c4212ad5f903229d55b0cae0495 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Tue, 19 Apr 2022 21:22:53 -0300 Subject: [PATCH 42/46] Remove AmazeFile.createTempFile --- .../filesystem/filetypes/AmazeFile.kt | 131 +----------------- 1 file changed, 1 insertion(+), 130 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index e7bb0e1054..376ef3227b 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -1173,136 +1173,7 @@ class AmazeFile : Comparable { Log.w(TAG, "Call to unimplemented fuction", e) -1 } - - /* -- Temporary files -- */ - private object TempDirectory { - // file name generation - private val random = SecureRandom() - @Throws(IOException::class) - fun generateFile(prefix: String, suffix: String, dir: AmazeFile?): AmazeFile { - // Android-changed: Use Math.randomIntInternal. This (pseudo) random number - // is initialized post-fork - var n = random.nextLong() - n = if (n == Long.MIN_VALUE) { - 0 // corner case - } else { - Math.abs(n) - } - - // Android-changed: Reject invalid file prefixes - // Use only the file name from the supplied prefix - // prefix = (new AmazeFile(prefix)).getName(); - val name = prefix + java.lang.Long.toString(n) + suffix - val f = AmazeFile(dir, name) - if (name != f.name) { - if (System.getSecurityManager() != null) { - throw IOException("Unable to create temporary file") - } else { - throw IOException("Unable to create temporary file, $f") - } - } - return f - } - } - - /** - * Creates a new empty file in the specified directory, using the given prefix and suffix strings - * to generate its name. If this method returns successfully then it is guaranteed that: - * - * - * 1. The file denoted by the returned abstract pathname did not exist before this method was - * invoked, and - * 1. Neither this method nor any of its variants will return the same abstract pathname again - * in the current invocation of the virtual machine. - * - * - * This method provides only part of a temporary-file facility. To arrange for a file created by - * this method to be deleted automatically, use the `[.deleteOnExit]` method. - * - * - * The `prefix` argument must be at least three characters long. It is recommended - * that the prefix be a short, meaningful string such as `"hjb"` or `"mail"` - * . The `suffix` argument may be `null`, in which case the suffix ` - * ".tmp"` will be used. - * - * - * To create the new file, the prefix and the suffix may first be adjusted to fit the - * limitations of the underlying platform. If the prefix is too long then it will be truncated, - * but its first three characters will always be preserved. If the suffix is too long then it too - * will be truncated, but if it begins with a period character (`'.'`) then the period - * and the first three characters following it will always be preserved. Once these adjustments - * have been made the name of the new file will be generated by concatenating the prefix, five or - * more internally-generated characters, and the suffix. - * - * - * If the `directory` argument is `null` then the system-dependent - * default temporary-file directory will be used. The default temporary-file directory is - * specified by the system property `java.io.tmpdir`. On UNIX systems the default value - * of this property is typically `"/tmp"` or `"/var/tmp"`; on Microsoft - * Windows systems it is typically `"C:\\WINNT\\TEMP"`. A different value may be given - * to this system property when the Java virtual machine is invoked, but programmatic changes to - * this property are not guaranteed to have any effect upon the temporary directory used by this - * method. - * - * @param prefix The prefix string to be used in generating the file's name; must be at least - * three characters long - * @param suffix The suffix string to be used in generating the file's name; may be `null - ` * , in which case the suffix `".tmp"` will be used - * @param directory The directory in which the file is to be created, or `null` if the - * default temporary-file directory is to be used - * @return An abstract pathname denoting a newly-created empty file - * @throws IllegalArgumentException If the `prefix` argument contains fewer than three - * characters - * @throws IOException If a file could not be created - */ - @Throws(IOException::class) - fun createTempFile( - contextProvider: ContextProvider, - prefix: String, - suffix: String?, - directory: AmazeFile? - ): AmazeFile { - require(prefix.length >= 3) { "Prefix string too short" } - - // Android-changed: Handle java.io.tmpdir changes. - val tmpdir = directory ?: AmazeFile(System.getProperty("java.io.tmpdir", ".")) - var f: AmazeFile - do { - f = TempDirectory.generateFile(prefix, suffix ?: ".tmp", tmpdir) - } while (fs.getBooleanAttributes(f, contextProvider) and AmazeFilesystem.BA_EXISTS != 0) - if (!fs.createFileExclusively(f.path)) throw IOException("Unable to create temporary file") - return f - } - - /** - * Creates an empty file in the default temporary-file directory, using the given prefix and - * suffix to generate its name. Invoking this method is equivalent to invoking ` - * [ createTempFile(prefix,&nbsp;suffix,&nbsp;null)][.createTempFile]`. - * - * - * The [ ][java.nio.file.Files.createTempFile] method provides an alternative method to create an empty file in the - * temporary-file directory. Files created by that method may have more restrictive access - * permissions to files created by this method and so may be more suited to security-sensitive - * applications. - * - * @param prefix The prefix string to be used in generating the file's name; must be at least - * three characters long - * @param suffix The suffix string to be used in generating the file's name; may be `null - ` * , in which case the suffix `".tmp"` will be used - * @return An abstract pathname denoting a newly-created empty file - * @throws IllegalArgumentException If the `prefix` argument contains fewer than three - * characters - * @throws IOException If a file could not be created - * @see java.nio.file.Files.createTempDirectory - */ - @Throws(IOException::class) - fun createTempFile( - contextProvider: ContextProvider, - prefix: String, - suffix: String? - ): AmazeFile { - return createTempFile(contextProvider, prefix, suffix, null) - } + /* -- Basic infrastructure -- */ /** * Compares two abstract pathnames lexicographically. The ordering defined by this method depends From d933bb35793da6ca4054d81d8891ba1597fa0bff Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Tue, 19 Apr 2022 22:01:11 -0300 Subject: [PATCH 43/46] Move hash calculation to AmazeFile --- .../hashcalculator/CalculateHashCallback.java | 77 +++---------------- .../hashcalculator/CalculateHashTask.kt | 16 ++-- .../filesystem/ssh/SshAmazeFilesystem.java | 47 +++++++++++ .../ui/dialogs/GeneralDialogCreation.java | 3 +- .../filesystem/filetypes/AmazeFile.kt | 31 ++++++-- .../filesystem/filetypes/AmazeFilesystem.kt | 52 +++++++++++++ 6 files changed, 146 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/hashcalculator/CalculateHashCallback.java b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/hashcalculator/CalculateHashCallback.java index 9b8e50c7d0..2ba58c2d06 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/hashcalculator/CalculateHashCallback.java +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/hashcalculator/CalculateHashCallback.java @@ -27,6 +27,8 @@ import java.util.Objects; import java.util.concurrent.Callable; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider; import com.amaze.filemanager.filesystem.HybridFileParcelable; import com.amaze.filemanager.filesystem.files.GenericCopyUtil; @@ -37,18 +39,12 @@ /** Generates hashes from files (MD5 and SHA256) */ public class CalculateHashCallback implements Callable { - private final boolean isNotADirectory; - private final InputStream inputStreamMd5; - private final InputStream inputStreamSha; + private final AmazeFile file; + private final ContextProvider contextProvider; - public CalculateHashCallback(HybridFileParcelable file, final Context context) { - if (file.isSftp()) { - throw new IllegalArgumentException("Use CalculateHashSftpCallback"); - } - - this.isNotADirectory = !file.isDirectory(context); - this.inputStreamMd5 = file.getInputStream(context); - this.inputStreamSha = file.getInputStream(context); + public CalculateHashCallback(AmazeFile file, final Context context) { + this.file = file; + this.contextProvider = () -> context; } @WorkerThread @@ -57,9 +53,9 @@ public Hash call() throws Exception { String md5 = null; String sha256 = null; - if (isNotADirectory) { - md5 = getMD5Checksum(); - sha256 = getSHA256Checksum(); + if (!file.isDirectory(contextProvider)) { + md5 = file.getHashMD5(contextProvider); + sha256 = file.getHashSHA256(contextProvider); } Objects.requireNonNull(md5); @@ -67,57 +63,4 @@ public Hash call() throws Exception { return new Hash(md5, sha256); } - - // see this How-to for a faster way to convert a byte array to a HEX string - - private String getMD5Checksum() throws Exception { - byte[] b = createChecksum(); - String result = ""; - - for (byte aB : b) { - result += Integer.toString((aB & 0xff) + 0x100, 16).substring(1); - } - return result; - } - - private String getSHA256Checksum() throws NoSuchAlgorithmException, IOException { - MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); - byte[] input = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; - int length; - InputStream inputStream = inputStreamMd5; - while ((length = inputStream.read(input)) != -1) { - if (length > 0) messageDigest.update(input, 0, length); - } - - byte[] hash = messageDigest.digest(); - - StringBuilder hexString = new StringBuilder(); - - for (byte aHash : hash) { - // convert hash to base 16 - String hex = Integer.toHexString(0xff & aHash); - if (hex.length() == 1) hexString.append('0'); - hexString.append(hex); - } - inputStream.close(); - return hexString.toString(); - } - - private byte[] createChecksum() throws Exception { - InputStream fis = inputStreamSha; - - byte[] buffer = new byte[8192]; - MessageDigest complete = MessageDigest.getInstance("MD5"); - int numRead; - - do { - numRead = fis.read(buffer); - if (numRead > 0) { - complete.update(buffer, 0, numRead); - } - } while (numRead != -1); - - fis.close(); - return complete.digest(); - } } diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/hashcalculator/CalculateHashTask.kt b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/hashcalculator/CalculateHashTask.kt index 98dfeb18c0..fcd7bb4269 100644 --- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/hashcalculator/CalculateHashTask.kt +++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/hashcalculator/CalculateHashTask.kt @@ -28,6 +28,8 @@ import android.widget.TextView import android.widget.Toast import com.amaze.filemanager.R import com.amaze.filemanager.asynchronous.asynctasks.Task +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile +import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider import com.amaze.filemanager.filesystem.HybridFileParcelable import com.amaze.filemanager.filesystem.files.FileUtils import java.lang.ref.WeakReference @@ -37,7 +39,7 @@ import java.util.concurrent.Callable data class Hash(val md5: String, val sha: String) class CalculateHashTask( - private val file: HybridFileParcelable, + private val file: AmazeFile, context: Context, view: View ) : Task> { @@ -46,11 +48,7 @@ class CalculateHashTask( private val TAG = CalculateHashTask::class.java.simpleName } - private val task: Callable = if (file.isSftp) { - CalculateHashSftpCallback(file) - } else { - CalculateHashCallback(file, context) - } + private val task: Callable = CalculateHashCallback(file, context) private val context = WeakReference(context) private val view = WeakReference(view) @@ -82,7 +80,11 @@ class CalculateHashTask( val mMD5LinearLayout = view.findViewById(R.id.properties_dialog_md5) val mSHA256LinearLayout = view.findViewById(R.id.properties_dialog_sha256) - if (!file.isDirectory(context) && file.getSize() != 0L) { + val contextProvider = object : ContextProvider { + override fun getContext(): Context? = context + } + + if (!file.isDirectory(contextProvider) && file.safeLength(contextProvider) != 0L) { md5HashText.text = md5Text sha256Text.text = shaText mMD5LinearLayout.setOnLongClickListener { diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java index 815fbab96a..7dccdd7796 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -21,6 +21,8 @@ import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.common.Buffer; +import net.schmizz.sshj.common.IOUtils; +import net.schmizz.sshj.connection.channel.direct.Session; import net.schmizz.sshj.sftp.RemoteFile; import net.schmizz.sshj.sftp.RemoteResourceInfo; import net.schmizz.sshj.sftp.SFTPClient; @@ -199,6 +201,51 @@ public Long execute(@NonNull SFTPClient client) throws IOException { return returnValue; } + @NonNull + @Override + public String getHashMD5(@NonNull AmazeFile f, @NonNull ContextProvider contextProvider) { + return SshClientUtils.execute( + new SshClientSessionTemplate(f.getPath()) { + @Override + public String execute(Session session) throws IOException { + Session.Command cmd = + session.exec( + String.format( + "md5sum -b \"%s\" | cut -c -32", + SshClientUtils.extractRemotePathFrom(f.getPath()))); + String result = + new String(IOUtils.readFully(cmd.getInputStream()).toByteArray()); + cmd.close(); + if (cmd.getExitStatus() == 0) return result; + else { + return null; + } + } + }); + } + + @NonNull + @Override + public String getHashSHA256(@NonNull AmazeFile f, @NonNull ContextProvider contextProvider) { + return SshClientUtils.execute( + new SshClientSessionTemplate(f.getPath()) { + @Override + public String execute(Session session) throws IOException { + Session.Command cmd = + session.exec( + String.format( + "sha256sum -b \"%s\" | cut -c -64", + SshClientUtils.extractRemotePathFrom(f.getPath()))); + String result = IOUtils.readFully(cmd.getInputStream()).toString(); + cmd.close(); + if (cmd.getExitStatus() == 0) return result; + else { + return null; + } + } + }); + } + @Override public long getLength(AmazeFile f, @NonNull ContextProvider contextProvider) throws IOException { if(f.isDirectory(contextProvider)) { diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/GeneralDialogCreation.java b/app/src/main/java/com/amaze/filemanager/ui/dialogs/GeneralDialogCreation.java index 74be06edd4..0dd2900ddd 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/GeneralDialogCreation.java +++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/GeneralDialogCreation.java @@ -74,6 +74,7 @@ import com.amaze.filemanager.databinding.DialogSigninWithGoogleBinding; import com.amaze.filemanager.file_operations.exceptions.ShellNotRunningException; import com.amaze.filemanager.file_operations.filesystem.OpenMode; +import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile; import com.amaze.filemanager.filesystem.FileProperties; import com.amaze.filemanager.filesystem.HybridFile; import com.amaze.filemanager.filesystem.HybridFileParcelable; @@ -533,7 +534,7 @@ private static void showPropertiesDialog( new CountItemsOrAndSizeTask(c, itemsText, baseFile, forStorage); countItemsOrAndSizeTask.executeOnExecutor(executor); - TaskKt.fromTask(new CalculateHashTask(baseFile, c, v)); + TaskKt.fromTask(new CalculateHashTask(new AmazeFile(baseFile.getPath()), c, v)); /*Chart creation and data loading*/ { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index 376ef3227b..e157745178 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2014 Arpit Khurana , Vishal Nehra , + * Copyright (C) 2014-2022 Arpit Khurana , Vishal Nehra , * Emmanuel Messulam, Raymond Lai and Contributors. * * This file is part of Amaze File Manager. @@ -595,9 +595,30 @@ class AmazeFile : Comparable { } /** - * Returns the length of the file denoted by this abstract pathname. The return value is - * unspecified if this pathname denotes a directory. - * + * Computes the hash MD5 of a file + */ + fun getHashMD5(contextProvider: ContextProvider): String? { + if(!isFile(contextProvider)) { + return null + } + + return fs.getHashMD5(this, contextProvider) + } + + + /** + * Computes the hash SHA256 of a file + */ + fun getHashSHA256(contextProvider: ContextProvider): String? { + if(!isFile(contextProvider)) { + return null + } + + return fs.getHashSHA256(this, contextProvider) + } + + /** + * Returns the length of the file or directory denoted by this abstract pathname. * * Where it is required to distinguish an I/O exception from the case that `0L` is * returned, or where several attributes of the same file are required at the same time, then the @@ -1173,7 +1194,7 @@ class AmazeFile : Comparable { Log.w(TAG, "Call to unimplemented fuction", e) -1 } - + /* -- Basic infrastructure -- */ /** * Compares two abstract pathnames lexicographically. The ordering defined by this method depends diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt index 283a9fa59a..d7057ad18a 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt @@ -25,6 +25,9 @@ import java.io.IOException import java.io.InputStream import java.io.OutputStream import java.lang.annotation.Native +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException +import kotlin.experimental.and abstract class AmazeFilesystem { /* -- Normalization and construction -- */ @@ -153,6 +156,55 @@ abstract class AmazeFilesystem { */ abstract fun getLastModifiedTime(f: AmazeFile): Long + /** + * Computes the hash MD5 of a file + */ + open fun getHashMD5(f: AmazeFile, contextProvider: ContextProvider): String { + val fis: InputStream = f.getInputStream(contextProvider) + val buffer = ByteArray(8192) + val complete = MessageDigest.getInstance("MD5") + var numRead: Int + do { + numRead = fis.read(buffer) + if (numRead > 0) { + complete.update(buffer, 0, numRead) + } + } while (numRead != -1) + fis.close() + + val b = complete.digest() + var result = "" + for (aB in b) { + result += Integer.toString((aB.and(127)) + 0x100, 16).substring(1) + } + return result + } + + /** + * Computes the hash SHA256 of a file + */ + open fun getHashSHA256(f: AmazeFile, contextProvider: ContextProvider): String { + val DEFAULT_BUFFER_SIZE = 8192 + + val messageDigest = MessageDigest.getInstance("SHA-256") + val input = ByteArray(DEFAULT_BUFFER_SIZE) + var length: Int + val inputStream: InputStream = f.getInputStream(contextProvider) + while (inputStream.read(input).also { length = it } != -1) { + if (length > 0) messageDigest.update(input, 0, length) + } + val hash = messageDigest.digest() + val hexString = StringBuilder() + for (aHash in hash) { + // convert hash to base 16 + val hex = Integer.toHexString(0xff and aHash.toInt()) + if (hex.length == 1) hexString.append('0') + hexString.append(hex) + } + inputStream.close() + return hexString.toString() + } + /** * Return the length in bytes of the file denoted by the given abstract pathname, or zero if it * does not exist, or some other I/O error occurs. From 6270e7a77ef027bf97882b56d0af4d84ce0e6504 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Thu, 21 Apr 2022 19:46:21 -0300 Subject: [PATCH 44/46] Add human readable path --- .../filesystem/files/FileAmazeFilesystem.kt | 2 ++ .../filemanager/filesystem/ssh/SshAmazeFilesystem.java | 7 +++++++ .../file_operations/filesystem/filetypes/AmazeFile.kt | 5 +++++ .../filesystem/filetypes/AmazeFilesystem.kt | 10 ++++++++++ .../filesystem/filetypes/smb/SmbAmazeFilesystem.kt | 7 +++++-- 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileAmazeFilesystem.kt b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileAmazeFilesystem.kt index 7d1b321468..2773d75494 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileAmazeFilesystem.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileAmazeFilesystem.kt @@ -22,6 +22,8 @@ import java.io.* object FileAmazeFilesystem : AmazeFilesystem() { const val PREFERENCE_ROOTMODE = "rootmode" + override val nomediaFileEnabled: Boolean = true + @JvmStatic val TAG = FileAmazeFilesystem::class.java.simpleName diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java index 7dccdd7796..530241d5b3 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/ssh/SshAmazeFilesystem.java @@ -65,6 +65,13 @@ public String normalize(@NonNull String path) { return PREFIX + new File(removePrefix(path)).getAbsolutePath(); } + @NonNull + @Override + public String getHumanReadablePath(@NonNull AmazeFile f) { + SshConnectionPool.ConnectionInfo connInfo = new SshConnectionPool.ConnectionInfo(f.getPath()); + return connInfo.toString(); + } + @NonNull @Override public String resolve(@NotNull String parent, @NotNull String child) { diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index e157745178..e1b2eb6f15 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -359,6 +359,11 @@ class AmazeFile : Comparable { return path.substring(index + 1) } + val humanReadablePath: String + get() { + return fs.getHumanReadablePath(this) + } + /** * Returns the pathname string of this abstract pathname's parent, or `null` if this * pathname does not name a parent directory. diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt index d7057ad18a..1c62298cbe 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt @@ -34,6 +34,8 @@ abstract class AmazeFilesystem { /** filesystem prefix */ abstract val prefix: String + open val nomediaFileEnabled: Boolean = false + /** Is the path of this filesystem? */ open fun isPathOfThisFilesystem(path: String): Boolean { return path.startsWith(prefix) @@ -47,6 +49,14 @@ abstract class AmazeFilesystem { */ abstract fun normalize(path: String): String + /** + * Convert the given [AmazeFile]'s path to human readable. Override if file has a weird path, + * as if it has an IP or username. + */ + open fun getHumanReadablePath(f: AmazeFile): String { + return f.path + } + /** * Compute the length of this pathname string's prefix. The pathname string must be in normal * form. diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt index 3107841a77..e02e80d004 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/smb/SmbAmazeFilesystem.kt @@ -36,8 +36,6 @@ import java.io.IOException import java.io.InputStream import java.io.OutputStream import java.lang.Boolean.parseBoolean -import java.lang.IllegalArgumentException -import java.lang.IllegalStateException import java.net.MalformedURLException import java.util.regex.Pattern @@ -139,6 +137,11 @@ object SmbAmazeFilesystem : AmazeFilesystem() { return canonical } + override fun getHumanReadablePath(f: AmazeFile): String { + val uri = Uri.parse(f.path) + return String.format("%s://%s%s", uri.scheme, uri.host, uri.path) + } + override fun prefixLength(path: String): Int { require(path.isNotEmpty()) { "This should never happen, all paths must start with SMB prefix" From a103ecec7a1ba9595d2d3591e7a89b7f93881838 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Thu, 21 Apr 2022 23:08:06 -0300 Subject: [PATCH 45/46] Add method to calculate folder size --- .../filesystem/filetypes/AmazeFile.kt | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index e1b2eb6f15..482313c440 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -32,7 +32,6 @@ import java.io.File import java.io.IOException import java.io.InputStream import java.io.OutputStream -import java.security.SecureRandom import java.util.* // Android-added: Info about UTF-8 usage in filenames. @@ -160,7 +159,7 @@ class AmazeFile : Comparable { } /** The FileSystem object representing the platform's local file system. */ - private lateinit var fs: AmazeFilesystem + lateinit var fs: AmazeFilesystem /** * Converts this abstract pathname into a pathname string. The resulting string uses the [separator] @@ -640,7 +639,7 @@ class AmazeFile : Comparable { } /** - * Returns the length of the file or directory denoted by this abstract pathname. + * Returns the length of the file denoted by this abstract pathname. * * Where it is required to distinguish an I/O exception from the case that `0L` is * returned, or where several attributes of the same file are required at the same time, then the @@ -660,6 +659,41 @@ class AmazeFile : Comparable { } } + /** + * Returns the length of the directory denoted by this abstract pathname. + * + * Where it is required to distinguish an I/O exception from the case that `0L` is + * returned. + * + * @return The length, in bytes, of the directory denoted by this abstract pathname, or `0L` + * if the directory does not exist. + */ + @JvmOverloads + fun safeLengthFolder(contextProvider: ContextProvider, callback: ((Long) -> Unit)? = null): Long { + if(!isDirectory(contextProvider)) { + return 0L + } + + try { + var length: Long = 0 + listFiles(contextProvider) { file: AmazeFile -> + if(file.isFile(contextProvider)) { + length += file.length(contextProvider) + callback?.invoke(length) + } else { + file.safeLengthFolder(contextProvider) { + length += it + callback?.invoke(length) + } + } + } + return length + } catch (e: IOException) { + Log.e(TAG, "Error getting file size", e) + return 0L + } + } + /* -- File operations -- */ /** * Atomically creates a new, empty file named by this abstract pathname if and only if a file with From 9d935aedc6100640698b28db42239b2f658b4e93 Mon Sep 17 00:00:00 2001 From: EmmanuelMess Date: Fri, 22 Apr 2022 18:05:28 -0300 Subject: [PATCH 46/46] Add getUri for AmazeFile --- .../filesystem/otg/OtgAmazeFilesystem.kt | 16 ++++++++++++++++ .../filesystem/filetypes/AmazeFile.kt | 6 ++++++ .../filesystem/filetypes/AmazeFilesystem.kt | 13 +++++++++++++ 3 files changed, 35 insertions(+) diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt index 8373e1d112..e668343ff8 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt +++ b/app/src/main/java/com/amaze/filemanager/filesystem/otg/OtgAmazeFilesystem.kt @@ -1,6 +1,9 @@ package com.amaze.filemanager.filesystem.otg +import android.net.Uri +import android.os.Build import android.util.Log +import androidx.core.content.FileProvider import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFile import com.amaze.filemanager.file_operations.filesystem.filetypes.AmazeFilesystem import com.amaze.filemanager.file_operations.filesystem.filetypes.ContextProvider @@ -29,6 +32,19 @@ object OtgAmazeFilesystem : AmazeFilesystem() { return simpleUnixNormalize(path) } + override fun getUriForFile(f: AmazeFile, contextProvider: ContextProvider): Uri? { + val context = contextProvider.getContext() ?: return null + + val documentFile = getDocumentFile(f.path, context, true) + + if(documentFile == null) { + Log.e(TAG, "Null DocumentFile getting Uri!") + return null + } + + return documentFile.uri + } + override fun resolve(parent: String, child: String): String { val prefix = parent.substring(0, prefixLength(parent)) val simplePathParent = parent.substring(prefixLength(parent)) diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt index 482313c440..7442fd773b 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFile.kt @@ -20,6 +20,7 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes +import android.net.Uri import android.os.Parcel import android.util.Log import com.amaze.filemanager.file_operations.filesystem.filetypes.cloud.box.BoxAmazeFilesystem @@ -497,6 +498,11 @@ class AmazeFile : Comparable { val canonPath = canonicalPath return AmazeFile(canonPath, fs.prefixLength(canonPath)) } + + fun getUriForFile(contextProvider: ContextProvider): Uri? { + return fs.getUriForFile(this, contextProvider) + } + /* -- Attribute accessors -- */ // Android-changed. Removed javadoc comment about special privileges // that doesn't make sense on android /** diff --git a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt index 1c62298cbe..036d25639c 100644 --- a/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt +++ b/file_operations/src/main/java/com/amaze/filemanager/file_operations/filesystem/filetypes/AmazeFilesystem.kt @@ -20,6 +20,8 @@ package com.amaze.filemanager.file_operations.filesystem.filetypes +import android.net.Uri +import android.util.Log import java.io.File import java.io.IOException import java.io.InputStream @@ -30,6 +32,7 @@ import java.security.NoSuchAlgorithmException import kotlin.experimental.and abstract class AmazeFilesystem { + /* -- Normalization and construction -- */ /** filesystem prefix */ abstract val prefix: String @@ -57,6 +60,14 @@ abstract class AmazeFilesystem { return f.path } + /** + * Convert the given [AmazeFile]'s path to an [Uri]. + */ + open fun getUriForFile(f: AmazeFile, contextProvider: ContextProvider): Uri? { + Log.e(TAG, "get Uri failed for file") + return null + } + /** * Compute the length of this pathname string's prefix. The pathname string must be in normal * form. @@ -297,6 +308,8 @@ abstract class AmazeFilesystem { } companion object { + private val TAG = AmazeFilesystem::class.java.simpleName + /** Return the local filesystem's name-separator character. */ const val STANDARD_SEPARATOR = '/'