mirror of
https://github.com/zhanghai/MaterialFiles
synced 2024-07-01 06:04:19 +00:00
[Feature] Use SevenZipJBinding for opening archives, but it's buggy.
This commit is contained in:
parent
2012dc8d93
commit
8e48609e7b
|
@ -65,7 +65,7 @@ dependencies {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-alpha03'
|
||||
implementation 'androidx.lifecycle:lifecycle-common-java8:2.2.0-alpha03'
|
||||
|
|
BIN
app/libs/me.zhanghai.android.sevenzipjbinding.aar
Normal file
BIN
app/libs/me.zhanghai.android.sevenzipjbinding.aar
Normal file
Binary file not shown.
|
@ -15,6 +15,7 @@ import me.zhanghai.android.files.firebase.CrashlyticsUtils;
|
|||
import me.zhanghai.android.files.provider.FileSystemProviders;
|
||||
import me.zhanghai.android.files.theme.custom.CustomThemeHelper;
|
||||
import me.zhanghai.android.files.theme.night.NightModeHelper;
|
||||
import me.zhanghai.android.sevenzipjbinding.SevenZip;
|
||||
|
||||
public class AppApplication extends Application {
|
||||
|
||||
|
@ -38,6 +39,7 @@ public class AppApplication extends Application {
|
|||
CrashlyticsUtils.init(this);
|
||||
Stetho.initializeWithDefaults(this);
|
||||
|
||||
SevenZip.init();
|
||||
FileSystemProviders.install();
|
||||
FileSystemProviders.setOverflowWatchEvents(true);
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ public class FilePropertiesBasicTabFragment extends AppCompatDialogFragment {
|
|||
ViewGroup mArchiveFileAndEntryLayout;
|
||||
@BindView(R.id.archive_file)
|
||||
TextView mArchiveFileText;
|
||||
@BindView(R.id.archive_entry)
|
||||
TextView mArchiveEntryText;
|
||||
@BindView(R.id.archive_item)
|
||||
TextView mArchiveItemText;
|
||||
@BindView(R.id.type)
|
||||
TextView mTypeText;
|
||||
@BindView(R.id.symbolic_link_target_layout)
|
||||
|
@ -113,7 +113,7 @@ public class FilePropertiesBasicTabFragment extends AppCompatDialogFragment {
|
|||
Path archiveFile = ArchiveFileSystemProvider.getArchiveFile(path);
|
||||
mArchiveFileText.setText(archiveFile.toFile().getPath());
|
||||
ArchiveFileAttributes attributes = (ArchiveFileAttributes) mExtraFile.getAttributes();
|
||||
mArchiveEntryText.setText(attributes.getEntryName());
|
||||
mArchiveItemText.setText(attributes.getItemPath());
|
||||
}
|
||||
mTypeText.setText(getTypeText(mExtraFile));
|
||||
boolean isSymbolicLink = mExtraFile.getAttributesNoFollowLinks().isSymbolicLink();
|
||||
|
|
|
@ -7,30 +7,29 @@ package me.zhanghai.android.files.provider.archive;
|
|||
|
||||
import android.os.Parcel;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import java8.nio.file.Path;
|
||||
import me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding.ArchiveItem;
|
||||
import me.zhanghai.android.files.provider.common.ParcelablePosixFileAttributes;
|
||||
|
||||
public class ArchiveFileAttributes extends ParcelablePosixFileAttributes {
|
||||
|
||||
@NonNull
|
||||
private final String mEntryName;
|
||||
private final String mItemPath;
|
||||
|
||||
ArchiveFileAttributes(@NonNull Path archiveFile, @NonNull ArchiveEntry entry) {
|
||||
this(new ArchiveFileAttributesImpl(archiveFile, entry));
|
||||
ArchiveFileAttributes(@NonNull Path archiveFile, @NonNull ArchiveItem item) {
|
||||
this(new ArchiveFileAttributesImpl(archiveFile, item));
|
||||
}
|
||||
|
||||
private ArchiveFileAttributes(@NonNull ArchiveFileAttributesImpl attributes) {
|
||||
super(attributes);
|
||||
|
||||
mEntryName = attributes.getEntryName();
|
||||
mItemPath = attributes.getItemPath();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getEntryName() {
|
||||
return mEntryName;
|
||||
public String getItemPath() {
|
||||
return mItemPath;
|
||||
}
|
||||
|
||||
|
||||
|
@ -49,13 +48,13 @@ public class ArchiveFileAttributes extends ParcelablePosixFileAttributes {
|
|||
protected ArchiveFileAttributes(Parcel in) {
|
||||
super(in);
|
||||
|
||||
mEntryName = in.readString();
|
||||
mItemPath = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
|
||||
dest.writeString(mEntryName);
|
||||
dest.writeString(mItemPath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
|
||||
package me.zhanghai.android.files.provider.archive;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.dump.DumpArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.threeten.bp.Instant;
|
||||
|
||||
import java.util.Set;
|
||||
|
@ -18,9 +13,9 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import java8.nio.file.Path;
|
||||
import java8.nio.file.attribute.FileTime;
|
||||
import me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding.ArchiveItem;
|
||||
import me.zhanghai.android.files.provider.common.ByteString;
|
||||
import me.zhanghai.android.files.provider.common.PosixFileAttributes;
|
||||
import me.zhanghai.android.files.provider.common.PosixFileMode;
|
||||
import me.zhanghai.android.files.provider.common.PosixFileModeBit;
|
||||
import me.zhanghai.android.files.provider.common.PosixFileType;
|
||||
import me.zhanghai.android.files.provider.common.PosixFileTypes;
|
||||
|
@ -32,41 +27,34 @@ class ArchiveFileAttributesImpl implements PosixFileAttributes {
|
|||
@NonNull
|
||||
private final Path mArchiveFile;
|
||||
@NonNull
|
||||
private final ArchiveEntry mEntry;
|
||||
private final ArchiveItem mItem;
|
||||
|
||||
ArchiveFileAttributesImpl(@NonNull Path archiveFile, @NonNull ArchiveEntry entry) {
|
||||
ArchiveFileAttributesImpl(@NonNull Path archiveFile, @NonNull ArchiveItem item) {
|
||||
mArchiveFile = archiveFile;
|
||||
mEntry = entry;
|
||||
mItem = item;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getEntryName() {
|
||||
return mEntry.getName();
|
||||
public String getItemPath() {
|
||||
return mItem.getPath();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public FileTime lastModifiedTime() {
|
||||
return FileTime.from(Instant.ofEpochMilli(mEntry.getLastModifiedDate().getTime()));
|
||||
Instant lastModifiedTime = mItem.getLastModifiedTime();
|
||||
if (lastModifiedTime == null) {
|
||||
lastModifiedTime = Instant.EPOCH;
|
||||
}
|
||||
return FileTime.from(lastModifiedTime);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public FileTime lastAccessTime() {
|
||||
if (mEntry instanceof DumpArchiveEntry) {
|
||||
DumpArchiveEntry dumpEntry = (DumpArchiveEntry) mEntry;
|
||||
return FileTime.from(Instant.ofEpochMilli(dumpEntry.getAccessTime().getTime()));
|
||||
} else if (mEntry instanceof SevenZArchiveEntry) {
|
||||
SevenZArchiveEntry sevenZEntry = (SevenZArchiveEntry) mEntry;
|
||||
if (sevenZEntry.getHasAccessDate()) {
|
||||
return FileTime.from(Instant.ofEpochMilli(sevenZEntry.getAccessDate().getTime()));
|
||||
}
|
||||
} else if (mEntry instanceof TarArchiveEntry) {
|
||||
TarArchiveEntry tarEntry = (TarArchiveEntry) mEntry;
|
||||
Long atimeMillis = getTarEntryTimeMillis(tarEntry, "atime");
|
||||
if (atimeMillis != null) {
|
||||
return FileTime.from(Instant.ofEpochMilli(atimeMillis));
|
||||
}
|
||||
Instant lastAccessTime = mItem.getLastAccessTime();
|
||||
if (lastAccessTime != null) {
|
||||
return FileTime.from(lastAccessTime);
|
||||
}
|
||||
return lastModifiedTime();
|
||||
}
|
||||
|
@ -74,103 +62,48 @@ class ArchiveFileAttributesImpl implements PosixFileAttributes {
|
|||
@NonNull
|
||||
@Override
|
||||
public FileTime creationTime() {
|
||||
if (mEntry instanceof DumpArchiveEntry) {
|
||||
DumpArchiveEntry dumpEntry = (DumpArchiveEntry) mEntry;
|
||||
return FileTime.from(Instant.ofEpochMilli(dumpEntry.getCreationTime().getTime()));
|
||||
} else if (mEntry instanceof SevenZArchiveEntry) {
|
||||
SevenZArchiveEntry sevenZEntry = (SevenZArchiveEntry) mEntry;
|
||||
if (sevenZEntry.getHasCreationDate()) {
|
||||
return FileTime.from(Instant.ofEpochMilli(sevenZEntry.getCreationDate().getTime()));
|
||||
}
|
||||
} else if (mEntry instanceof TarArchiveEntry) {
|
||||
TarArchiveEntry tarEntry = (TarArchiveEntry) mEntry;
|
||||
Long ctimeMillis = getTarEntryTimeMillis(tarEntry, "ctime");
|
||||
if (ctimeMillis != null) {
|
||||
return FileTime.from(Instant.ofEpochMilli(ctimeMillis));
|
||||
}
|
||||
Instant creationTime = mItem.getCreationTime();
|
||||
if (creationTime != null) {
|
||||
return FileTime.from(creationTime);
|
||||
}
|
||||
return lastModifiedTime();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Long getTarEntryTimeMillis(@NonNull TarArchiveEntry entry,
|
||||
@NonNull String name) {
|
||||
String atime = entry.getExtraPaxHeader(name);
|
||||
if (atime == null) {
|
||||
return null;
|
||||
}
|
||||
double atimeSeconds;
|
||||
try {
|
||||
atimeSeconds = Double.parseDouble(atime);
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return (long) (atimeSeconds * 1000);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PosixFileType type() {
|
||||
return PosixFileTypes.fromArchiveEntry(mEntry);
|
||||
return PosixFileTypes.fromArchiveItem(mItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() {
|
||||
return mEntry.getSize();
|
||||
return mItem.getSize();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ArchiveFileKey fileKey() {
|
||||
return new ArchiveFileKey(mArchiveFile, mEntry.getName());
|
||||
return new ArchiveFileKey(mArchiveFile, mItem.getPath());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PosixUser owner() {
|
||||
if (mEntry instanceof DumpArchiveEntry) {
|
||||
DumpArchiveEntry dumpEntry = (DumpArchiveEntry) mEntry;
|
||||
//noinspection deprecation
|
||||
return new PosixUser(dumpEntry.getUserId(), null);
|
||||
} else if (mEntry instanceof TarArchiveEntry) {
|
||||
TarArchiveEntry tarEntry = (TarArchiveEntry) mEntry;
|
||||
//noinspection deprecation
|
||||
return new PosixUser(tarEntry.getUserId(), ByteString.fromStringOrNull(
|
||||
tarEntry.getUserName()));
|
||||
}
|
||||
String owner = mItem.getOwner();
|
||||
// TODO: Where is ID?
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PosixGroup group() {
|
||||
if (mEntry instanceof DumpArchiveEntry) {
|
||||
DumpArchiveEntry dumpEntry = (DumpArchiveEntry) mEntry;
|
||||
//noinspection deprecation
|
||||
return new PosixGroup(dumpEntry.getGroupId(), null);
|
||||
} else if (mEntry instanceof TarArchiveEntry) {
|
||||
TarArchiveEntry tarEntry = (TarArchiveEntry) mEntry;
|
||||
//noinspection deprecation
|
||||
return new PosixGroup(tarEntry.getGroupId(), ByteString.fromStringOrNull(
|
||||
tarEntry.getGroupName()));
|
||||
}
|
||||
String group = mItem.getGroup();
|
||||
// TODO: Where is ID?
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Set<PosixFileModeBit> mode() {
|
||||
if (mEntry instanceof DumpArchiveEntry) {
|
||||
DumpArchiveEntry dumpEntry = (DumpArchiveEntry) mEntry;
|
||||
return PosixFileMode.fromInt(dumpEntry.getMode());
|
||||
} else if (mEntry instanceof TarArchiveEntry) {
|
||||
TarArchiveEntry tarEntry = (TarArchiveEntry) mEntry;
|
||||
return PosixFileMode.fromInt(tarEntry.getMode());
|
||||
} else if (mEntry instanceof ZipArchiveEntry) {
|
||||
ZipArchiveEntry zipEntry = (ZipArchiveEntry) mEntry;
|
||||
if (zipEntry.getPlatform() == ZipArchiveEntry.PLATFORM_UNIX) {
|
||||
return PosixFileMode.fromInt(zipEntry.getUnixMode());
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,14 +8,13 @@ package me.zhanghai.android.files.provider.archive;
|
|||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import java8.nio.file.Path;
|
||||
import me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding.ArchiveItem;
|
||||
import me.zhanghai.android.files.provider.common.ByteString;
|
||||
import me.zhanghai.android.files.provider.common.ByteStringListPathFactory;
|
||||
import me.zhanghai.android.files.provider.remote.RemoteFileSystemException;
|
||||
|
@ -65,8 +64,8 @@ class ArchiveFileSystem extends RootableFileSystem implements ByteStringListPath
|
|||
}
|
||||
|
||||
@NonNull
|
||||
ArchiveEntry getEntryAsLocal(@NonNull Path path) throws IOException {
|
||||
return getLocalFileSystem().getEntry(path);
|
||||
ArchiveItem getItemAsLocal(@NonNull Path path) throws IOException {
|
||||
return getLocalFileSystem().getItem(path);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
|
||||
package me.zhanghai.android.files.provider.archive;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -18,6 +16,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import java8.nio.file.Path;
|
||||
import java8.nio.file.attribute.FileTime;
|
||||
import me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding.ArchiveItem;
|
||||
import me.zhanghai.android.files.provider.common.ByteString;
|
||||
import me.zhanghai.android.files.provider.common.PosixFileAttributeView;
|
||||
import me.zhanghai.android.files.provider.common.PosixFileModeBit;
|
||||
|
@ -48,8 +47,8 @@ public class LocalArchiveFileAttributeView implements PosixFileAttributeView {
|
|||
@Override
|
||||
public ArchiveFileAttributes readAttributes() throws IOException {
|
||||
ArchiveFileSystem fileSystem = (ArchiveFileSystem) mPath.getFileSystem();
|
||||
ArchiveEntry entry = fileSystem.getEntryAsLocal(mPath);
|
||||
return new ArchiveFileAttributes(fileSystem.getArchiveFile(), entry);
|
||||
ArchiveItem item = fileSystem.getItemAsLocal(mPath);
|
||||
return new ArchiveFileAttributes(fileSystem.getArchiveFile(), item);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,8 +7,6 @@ package me.zhanghai.android.files.provider.archive;
|
|||
|
||||
import android.util.Pair;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
|
@ -28,7 +26,8 @@ import java8.nio.file.PathMatcher;
|
|||
import java8.nio.file.WatchService;
|
||||
import java8.nio.file.attribute.UserPrincipalLookupService;
|
||||
import java8.nio.file.spi.FileSystemProvider;
|
||||
import me.zhanghai.android.files.provider.archive.archiver.ArchiveReader;
|
||||
import me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding.ArchiveItem;
|
||||
import me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding.ArchiveReader;
|
||||
import me.zhanghai.android.files.provider.common.ByteString;
|
||||
import me.zhanghai.android.files.provider.common.ByteStringBuilder;
|
||||
import me.zhanghai.android.files.provider.common.ByteStringListPathFactory;
|
||||
|
@ -59,7 +58,7 @@ class LocalArchiveFileSystem extends FileSystem implements ByteStringListPathFac
|
|||
|
||||
private boolean mNeedRefresh = true;
|
||||
|
||||
private Map<Path, ArchiveEntry> mEntries;
|
||||
private Map<Path, ArchiveItem> mItems;
|
||||
|
||||
private Map<Path, List<Path>> mTree;
|
||||
|
||||
|
@ -94,21 +93,21 @@ class LocalArchiveFileSystem extends FileSystem implements ByteStringListPathFac
|
|||
}
|
||||
|
||||
@NonNull
|
||||
ArchiveEntry getEntry(@NonNull Path path) throws IOException {
|
||||
ArchiveItem getItem(@NonNull Path path) throws IOException {
|
||||
synchronized (mLock) {
|
||||
ensureEntriesLocked();
|
||||
return getEntryLocked(path);
|
||||
return getItemLocked(path);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ArchiveEntry getEntryLocked(@NonNull Path path) throws IOException {
|
||||
private ArchiveItem getItemLocked(@NonNull Path path) throws IOException {
|
||||
synchronized (mLock) {
|
||||
ArchiveEntry entry = mEntries.get(path);
|
||||
if (entry == null) {
|
||||
ArchiveItem item = mItems.get(path);
|
||||
if (item == null) {
|
||||
throw new NoSuchFileException(path.toString());
|
||||
}
|
||||
return entry;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,8 +115,8 @@ class LocalArchiveFileSystem extends FileSystem implements ByteStringListPathFac
|
|||
InputStream newInputStream(@NonNull Path file) throws IOException {
|
||||
synchronized (mLock) {
|
||||
ensureEntriesLocked();
|
||||
ArchiveEntry entry = getEntryLocked(file);
|
||||
return ArchiveReader.newInputStream(mArchiveFile, entry);
|
||||
ArchiveItem item = getItemLocked(file);
|
||||
return ArchiveReader.newInputStream(mArchiveFile, item);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,8 +124,8 @@ class LocalArchiveFileSystem extends FileSystem implements ByteStringListPathFac
|
|||
List<Path> getDirectoryChildren(@NonNull Path directory) throws IOException {
|
||||
synchronized (mLock) {
|
||||
ensureEntriesLocked();
|
||||
ArchiveEntry entry = getEntryLocked(directory);
|
||||
if (!entry.isDirectory()) {
|
||||
ArchiveItem item = getItemLocked(directory);
|
||||
if (!item.isDirectory()) {
|
||||
throw new NotDirectoryException(directory.toString());
|
||||
}
|
||||
return mTree.get(directory);
|
||||
|
@ -137,8 +136,8 @@ class LocalArchiveFileSystem extends FileSystem implements ByteStringListPathFac
|
|||
String readSymbolicLink(@NonNull Path link) throws IOException {
|
||||
synchronized (mLock) {
|
||||
ensureEntriesLocked();
|
||||
ArchiveEntry entry = getEntryLocked(link);
|
||||
return ArchiveReader.readSymbolicLink(mArchiveFile, entry);
|
||||
ArchiveItem item = getItemLocked(link);
|
||||
return ArchiveReader.readSymbolicLink(mArchiveFile, item);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,10 +155,10 @@ class LocalArchiveFileSystem extends FileSystem implements ByteStringListPathFac
|
|||
throw new ClosedFileSystemException();
|
||||
}
|
||||
if (mNeedRefresh) {
|
||||
Pair<Map<Path, ArchiveEntry>, Map<Path, List<Path>>> entriesAndTree =
|
||||
ArchiveReader.readEntries(mArchiveFile, mRootDirectory);
|
||||
mEntries = entriesAndTree.first;
|
||||
mTree = entriesAndTree.second;
|
||||
Pair<Map<Path, ArchiveItem>, Map<Path, List<Path>>> itemsAndTree =
|
||||
ArchiveReader.readItems(mArchiveFile, mRootDirectory);
|
||||
mItems = itemsAndTree.first;
|
||||
mTree = itemsAndTree.second;
|
||||
mNeedRefresh = false;
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +177,7 @@ class LocalArchiveFileSystem extends FileSystem implements ByteStringListPathFac
|
|||
}
|
||||
mProvider.removeFileSystem(mFileSystem);
|
||||
mNeedRefresh = false;
|
||||
mEntries = null;
|
||||
mItems = null;
|
||||
mTree = null;
|
||||
mOpen = false;
|
||||
}
|
||||
|
|
|
@ -314,7 +314,7 @@ class LocalArchiveFileSystemProvider extends FileSystemProvider implements Searc
|
|||
Objects.requireNonNull(modes);
|
||||
AccessModes accessModes = AccessModes.fromArray(modes);
|
||||
ArchiveFileSystem fileSystem = (ArchiveFileSystem) path.getFileSystem();
|
||||
fileSystem.getEntryAsLocal(path);
|
||||
fileSystem.getItemAsLocal(path);
|
||||
if (accessModes.hasWrite() || accessModes.hasExecute()) {
|
||||
throw new AccessDeniedException(path.toString());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>
|
||||
* All Rights Reserved.
|
||||
*/
|
||||
|
||||
package me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding;
|
||||
|
||||
import org.threeten.bp.Instant;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public interface ArchiveItem {
|
||||
|
||||
@NonNull
|
||||
String getPath();
|
||||
|
||||
long getSize();
|
||||
|
||||
long getPackedSize();
|
||||
|
||||
boolean isDirectory();
|
||||
|
||||
int getAttributes();
|
||||
|
||||
@Nullable
|
||||
Instant getCreationTime();
|
||||
|
||||
@Nullable
|
||||
Instant getLastAccessTime();
|
||||
|
||||
@Nullable
|
||||
Instant getLastModifiedTime();
|
||||
|
||||
boolean isEncrypted();
|
||||
|
||||
boolean isCommented();
|
||||
|
||||
@Nullable
|
||||
Integer getCrc();
|
||||
|
||||
@Nullable
|
||||
String getMethod();
|
||||
|
||||
@Nullable
|
||||
String getHostOs();
|
||||
|
||||
@Nullable
|
||||
String getOwner();
|
||||
|
||||
@Nullable
|
||||
String getGroup();
|
||||
|
||||
@Nullable
|
||||
String getComment();
|
||||
|
||||
int getIndex();
|
||||
|
||||
@Nullable
|
||||
String getLink();
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>
|
||||
* All Rights Reserved.
|
||||
*/
|
||||
|
||||
package me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Pair;
|
||||
|
||||
import net.sf.sevenzipjbinding.IInArchive;
|
||||
import net.sf.sevenzipjbinding.SevenZip;
|
||||
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
|
||||
|
||||
import org.threeten.bp.Instant;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import eu.chainfire.librootjava.RootJava;
|
||||
import java8.nio.channels.SeekableByteChannel;
|
||||
import java8.nio.charset.StandardCharsets;
|
||||
import java8.nio.file.NoSuchFileException;
|
||||
import java8.nio.file.NotLinkException;
|
||||
import java8.nio.file.Path;
|
||||
import me.zhanghai.android.files.BuildConfig;
|
||||
import me.zhanghai.android.files.R;
|
||||
import me.zhanghai.android.files.compat.MapCompat;
|
||||
import me.zhanghai.android.files.provider.common.IsDirectoryException;
|
||||
import me.zhanghai.android.files.provider.common.MoreFiles;
|
||||
import me.zhanghai.android.files.provider.common.PosixFileType;
|
||||
import me.zhanghai.android.files.provider.common.PosixFileTypes;
|
||||
import me.zhanghai.android.files.provider.root.RootUtils;
|
||||
import me.zhanghai.android.files.settings.Settings;
|
||||
import me.zhanghai.android.files.util.IoUtils;
|
||||
|
||||
public class ArchiveReader {
|
||||
|
||||
private ArchiveReader() {}
|
||||
|
||||
@NonNull
|
||||
public static Pair<Map<Path, ArchiveItem>, Map<Path, List<Path>>> readItems(
|
||||
@NonNull Path file, @NonNull Path rootPath) throws IOException {
|
||||
Map<Path, ArchiveItem> items = new HashMap<>();
|
||||
List<ArchiveItem> rawItems = readItems(file);
|
||||
for (ArchiveItem item : rawItems) {
|
||||
Path path = rootPath.resolve(item.getPath());
|
||||
// Normalize an absolute path to prevent path traversal attack.
|
||||
if (!path.isAbsolute()) {
|
||||
throw new AssertionError("Path must be absolute: " + path.toString());
|
||||
}
|
||||
if (path.getNameCount() > 0) {
|
||||
path = path.normalize();
|
||||
if (path.getNameCount() == 0) {
|
||||
// Don't allow a path to become the root path only after normalization.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
MapCompat.putIfAbsent(items, path, item);
|
||||
}
|
||||
if (!items.containsKey(rootPath)) {
|
||||
items.put(rootPath, new DirectoryArchiveItem("/"));
|
||||
}
|
||||
Map<Path, List<Path>> tree = new HashMap<>();
|
||||
tree.put(rootPath, new ArrayList<>());
|
||||
List<Path> paths = new ArrayList<>(items.keySet());
|
||||
for (Path path : paths) {
|
||||
while (true) {
|
||||
Path parentPath = path.getParent();
|
||||
if (parentPath == null) {
|
||||
break;
|
||||
}
|
||||
ArchiveItem item = items.get(path);
|
||||
if (item.isDirectory()) {
|
||||
MapCompat.computeIfAbsent(tree, path, _1 -> new ArrayList<>());
|
||||
}
|
||||
MapCompat.computeIfAbsent(tree, parentPath, _1 -> new ArrayList<>())
|
||||
.add(path);
|
||||
if (items.containsKey(parentPath)) {
|
||||
break;
|
||||
}
|
||||
items.put(parentPath, new DirectoryArchiveItem(parentPath.toString()));
|
||||
path = parentPath;
|
||||
}
|
||||
}
|
||||
return new Pair<>(items, tree);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<ArchiveItem> readItems(@NonNull Path file) throws IOException {
|
||||
try (SeekableByteChannel channel = MoreFiles.newByteChannel(file);
|
||||
IInArchive archive = SevenZip.openInArchive(null, new ByteChannelInStream(channel))) {
|
||||
List<ArchiveItem> items = new ArrayList<>();
|
||||
for (ISimpleInArchiveItem item : archive.getSimpleInterface().getArchiveItems()) {
|
||||
items.add(new SimpleArchiveItem(archive, item));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String getArchiveFileNameEncoding() {
|
||||
if (RootUtils.isRunningAsRoot()) {
|
||||
try {
|
||||
Context context = RootJava.getPackageContext(BuildConfig.APPLICATION_ID);
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(
|
||||
context);
|
||||
String key = context.getString(R.string.pref_key_archive_file_name_encoding);
|
||||
String defaultValue = context.getString(
|
||||
R.string.pref_default_value_archive_file_name_encoding);
|
||||
return sharedPreferences.getString(key, defaultValue);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return StandardCharsets.UTF_8.name();
|
||||
}
|
||||
} else {
|
||||
return Settings.ARCHIVE_FILE_NAME_ENCODING.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static InputStream newInputStream(@NonNull Path file, @NonNull ArchiveItem item)
|
||||
throws IOException {
|
||||
if (item.isDirectory()) {
|
||||
throw new IsDirectoryException(file.toString());
|
||||
}
|
||||
try (SeekableByteChannel channel = MoreFiles.newByteChannel(file);
|
||||
IInArchive archive = SevenZip.openInArchive(null, new ByteChannelInStream(channel))) {
|
||||
for (ISimpleInArchiveItem simpleItem : archive.getSimpleInterface().getArchiveItems()) {
|
||||
if (Objects.equals(simpleItem.getPath(), item.getPath())) {
|
||||
PipedInputStream inputStream = new PipedInputStream();
|
||||
// TODO: Might deadlock if on same thread?
|
||||
simpleItem.extractSlow(new StreamOutStream(new PipedOutputStream(inputStream)));
|
||||
return inputStream;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NoSuchFileException(file.toString());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String readSymbolicLink(@NonNull Path file, @NonNull ArchiveItem entry)
|
||||
throws IOException {
|
||||
if (!isSymbolicLink(entry)) {
|
||||
throw new NotLinkException(file.toString());
|
||||
}
|
||||
try (InputStream inputStream = newInputStream(file, entry)) {
|
||||
return IoUtils.inputStreamToString(inputStream, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isSymbolicLink(@NonNull ArchiveItem item) {
|
||||
return PosixFileTypes.fromArchiveItem(item) == PosixFileType.SYMBOLIC_LINK;
|
||||
}
|
||||
|
||||
private static class DirectoryArchiveItem implements ArchiveItem {
|
||||
|
||||
@NonNull
|
||||
private String mPath;
|
||||
|
||||
public DirectoryArchiveItem(@NonNull String path) {
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getPath() {
|
||||
return mPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPackedSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAttributes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Instant getCreationTime() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Instant getLastAccessTime() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Instant getLastModifiedTime() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEncrypted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCommented() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Integer getCrc() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getHostOs() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getOwner() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getGroup() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getComment() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndex() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getLink() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>
|
||||
* All Rights Reserved.
|
||||
*/
|
||||
|
||||
package me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding;
|
||||
|
||||
import net.sf.sevenzipjbinding.IInStream;
|
||||
import net.sf.sevenzipjbinding.SevenZipException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import java8.nio.channels.SeekableByteChannel;
|
||||
|
||||
public class ByteChannelInStream implements IInStream {
|
||||
|
||||
@NonNull
|
||||
private final SeekableByteChannel mChannel;
|
||||
|
||||
public ByteChannelInStream(@NonNull SeekableByteChannel channel) {
|
||||
mChannel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized long seek(long offset, int seekOrigin) throws SevenZipException {
|
||||
try {
|
||||
switch (seekOrigin) {
|
||||
case SEEK_SET:
|
||||
mChannel.position(offset);
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
mChannel.position(mChannel.position() + offset);
|
||||
break;
|
||||
case SEEK_END:
|
||||
mChannel.position(mChannel.size() + offset);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError(seekOrigin);
|
||||
}
|
||||
return mChannel.position();
|
||||
} catch (IOException e) {
|
||||
throw new SevenZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int read(@NonNull byte[] data) throws SevenZipException {
|
||||
try {
|
||||
return mChannel.read(ByteBuffer.wrap(data));
|
||||
} catch (IOException e) {
|
||||
throw new SevenZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
mChannel.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>
|
||||
* All Rights Reserved.
|
||||
*/
|
||||
|
||||
package me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding;
|
||||
|
||||
import net.sf.sevenzipjbinding.IOutStream;
|
||||
import net.sf.sevenzipjbinding.SevenZipException;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import java8.nio.channels.SeekableByteChannel;
|
||||
|
||||
public class ByteChannelOutStream implements Closeable, IOutStream {
|
||||
|
||||
@NonNull
|
||||
private final SeekableByteChannel mChannel;
|
||||
|
||||
public ByteChannelOutStream(@NonNull SeekableByteChannel channel) {
|
||||
mChannel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized long seek(long offset, int seekOrigin) throws SevenZipException {
|
||||
try {
|
||||
switch (seekOrigin) {
|
||||
case SEEK_SET:
|
||||
mChannel.position(offset);
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
mChannel.position(mChannel.position() + offset);
|
||||
break;
|
||||
case SEEK_END:
|
||||
mChannel.position(mChannel.size() + offset);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError(seekOrigin);
|
||||
}
|
||||
return mChannel.position();
|
||||
} catch (IOException e) {
|
||||
throw new SevenZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setSize(long newSize) throws SevenZipException {
|
||||
try {
|
||||
if (newSize <= mChannel.size()) {
|
||||
mChannel.truncate(newSize);
|
||||
} else {
|
||||
long oldPosition = mChannel.position();
|
||||
mChannel.position(newSize - 1);
|
||||
try {
|
||||
mChannel.write(ByteBuffer.wrap(new byte[] {0}));
|
||||
} finally {
|
||||
mChannel.position(oldPosition);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SevenZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int write(@NonNull byte[] data) throws SevenZipException {
|
||||
try {
|
||||
return mChannel.write(ByteBuffer.wrap(data));
|
||||
} catch (IOException e) {
|
||||
throw new SevenZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
mChannel.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>
|
||||
* All Rights Reserved.
|
||||
*/
|
||||
|
||||
package me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import net.sf.sevenzipjbinding.IInArchive;
|
||||
import net.sf.sevenzipjbinding.PropID;
|
||||
import net.sf.sevenzipjbinding.SevenZipException;
|
||||
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
|
||||
|
||||
import org.threeten.bp.Instant;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class SimpleArchiveItem implements ArchiveItem {
|
||||
|
||||
@NonNull
|
||||
private final String mPath;
|
||||
private final long mSize;
|
||||
private final long mPackedSize;
|
||||
private final boolean mIsDirectory;
|
||||
private final int mAttributes;
|
||||
@Nullable
|
||||
private final Instant mCreationTime;
|
||||
@Nullable
|
||||
private final Instant mLastAccessTime;
|
||||
@Nullable
|
||||
private final Instant mLastModifiedTime;
|
||||
private final boolean mEncrypted;
|
||||
private final boolean mCommented;
|
||||
@Nullable
|
||||
private final Integer mCrc;
|
||||
@Nullable
|
||||
private final String mMethod;
|
||||
@Nullable
|
||||
private final String mHostOs;
|
||||
@Nullable
|
||||
private final String mOwner;
|
||||
@Nullable
|
||||
private final String mGroup;
|
||||
@Nullable
|
||||
private final String mComment;
|
||||
private final int mIndex;
|
||||
@Nullable
|
||||
private final String mLink;
|
||||
|
||||
public SimpleArchiveItem(@NonNull IInArchive archive, @NonNull ISimpleInArchiveItem item)
|
||||
throws SevenZipException {
|
||||
String path = item.getPath();
|
||||
if (TextUtils.isEmpty(path)) {
|
||||
throw new SevenZipException("ISimpleInArchiveItem.getPath() returned null or empty");
|
||||
}
|
||||
mPath = path;
|
||||
Long size = item.getSize();
|
||||
mSize = size != null && size >= 0 ? size : 0;
|
||||
Long packedSize = item.getPackedSize();
|
||||
mPackedSize = packedSize != null && packedSize >= 0 ? packedSize : 0;
|
||||
mIsDirectory = item.isFolder() || path.endsWith("/");
|
||||
Integer attributes = item.getAttributes();
|
||||
mAttributes = attributes != null ? attributes : 0;
|
||||
mCreationTime = toInstant(item.getCreationTime());
|
||||
mLastAccessTime = toInstant(item.getLastAccessTime());
|
||||
mLastModifiedTime = toInstant(item.getLastWriteTime());
|
||||
mEncrypted = item.isEncrypted();
|
||||
Boolean commented = item.isCommented();
|
||||
mCommented = commented != null ? commented : false;
|
||||
mCrc = item.getCRC();
|
||||
mMethod = item.getMethod();
|
||||
mHostOs = item.getHostOS();
|
||||
mOwner = item.getUser();
|
||||
mGroup = item.getGroup();
|
||||
mComment = item.getComment();
|
||||
mIndex = item.getItemIndex();
|
||||
mLink = archive.getStringProperty(mIndex, PropID.LINK);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Instant toInstant(@Nullable Date date) {
|
||||
if (date != null) {
|
||||
long millis = date.getTime();
|
||||
if (millis > 0) {
|
||||
return Instant.ofEpochMilli(millis);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String getPath() {
|
||||
return mPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPackedSize() {
|
||||
return mPackedSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return mIsDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAttributes() {
|
||||
return mAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Instant getCreationTime() {
|
||||
return mCreationTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Instant getLastAccessTime() {
|
||||
return mLastAccessTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Instant getLastModifiedTime() {
|
||||
return mLastModifiedTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEncrypted() {
|
||||
return mEncrypted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCommented() {
|
||||
return mCommented;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Integer getCrc() {
|
||||
return mCrc;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getMethod() {
|
||||
return mMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getHostOs() {
|
||||
return mHostOs;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getOwner() {
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getGroup() {
|
||||
return mGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getComment() {
|
||||
return mComment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndex() {
|
||||
return mIndex;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLink() {
|
||||
return mLink;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>
|
||||
* All Rights Reserved.
|
||||
*/
|
||||
|
||||
package me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding;
|
||||
|
||||
import net.sf.sevenzipjbinding.ISequentialOutStream;
|
||||
import net.sf.sevenzipjbinding.SevenZipException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class StreamOutStream implements ISequentialOutStream {
|
||||
|
||||
@NonNull
|
||||
private final OutputStream mStream;
|
||||
|
||||
public StreamOutStream(@NonNull OutputStream stream) {
|
||||
mStream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int write(@NonNull byte[] bytes) throws SevenZipException {
|
||||
try {
|
||||
mStream.write(bytes);
|
||||
} catch (IOException e) {
|
||||
throw new SevenZipException(e);
|
||||
}
|
||||
return bytes.length;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,8 @@ package me.zhanghai.android.files.provider.common;
|
|||
|
||||
import android.system.OsConstants;
|
||||
|
||||
import net.sf.sevenzipjbinding.PropID;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.dump.DumpArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
|
@ -14,6 +16,7 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import java8.nio.file.attribute.BasicFileAttributes;
|
||||
import me.zhanghai.android.files.provider.archive.archiver_sevenzipjbinding.ArchiveItem;
|
||||
|
||||
public class PosixFileTypes {
|
||||
|
||||
|
@ -90,6 +93,27 @@ public class PosixFileTypes {
|
|||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static PosixFileType fromArchiveItem(@NonNull ArchiveItem item) {
|
||||
if (item.getLink() != null) {
|
||||
return PosixFileType.SYMBOLIC_LINK;
|
||||
}
|
||||
int attributes = item.getAttributes();
|
||||
if ((attributes & PropID.AttributesBitMask.FILE_ATTRIBUTE_DIRECTORY)
|
||||
== PropID.AttributesBitMask.FILE_ATTRIBUTE_DIRECTORY) {
|
||||
return PosixFileType.DIRECTORY;
|
||||
}
|
||||
if ((attributes & PropID.AttributesBitMask.FILE_ATTRIBUTE_UNIX_EXTENSION)
|
||||
== PropID.AttributesBitMask.FILE_ATTRIBUTE_UNIX_EXTENSION) {
|
||||
int mode = attributes >> 16;
|
||||
PosixFileType type = fromMode(mode);
|
||||
if (type != PosixFileType.UNKNOWN) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return PosixFileType.REGULAR_FILE;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static PosixFileType fromMode(int mode) {
|
||||
return OsConstants.S_ISDIR(mode) ? PosixFileType.DIRECTORY
|
||||
|
|
|
@ -83,11 +83,11 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="@string/file_properties_basic_archive_entry"
|
||||
android:text="@string/file_properties_basic_archive_item"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/archive_entry"
|
||||
android:id="@+id/archive_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
|
|
|
@ -219,7 +219,7 @@
|
|||
<string name="file_properties_basic_last_modification_time">最后修改</string>
|
||||
<string name="file_properties_basic_parent_directory">父文件夹</string>
|
||||
<string name="file_properties_basic_archive_file">归档文件</string>
|
||||
<string name="file_properties_basic_archive_entry">归档条目</string>
|
||||
<string name="file_properties_basic_archive_item">归档条目</string>
|
||||
<string name="file_properties_basic_free_space">可用空间</string>
|
||||
<string name="file_properties_permissions">权限</string>
|
||||
<string name="file_properties_permissions_owner">所有者</string>
|
||||
|
|
|
@ -219,7 +219,7 @@
|
|||
<string name="file_properties_basic_last_modification_time">最後修改</string>
|
||||
<string name="file_properties_basic_parent_directory">父資料夾</string>
|
||||
<string name="file_properties_basic_archive_file">歸檔檔案</string>
|
||||
<string name="file_properties_basic_archive_entry">歸檔條目</string>
|
||||
<string name="file_properties_basic_archive_item">歸檔條目</string>
|
||||
<string name="file_properties_basic_free_space">可用空間</string>
|
||||
<string name="file_properties_permissions">權限</string>
|
||||
<string name="file_properties_permissions_owner">所有者</string>
|
||||
|
|
|
@ -234,7 +234,7 @@
|
|||
<string name="file_properties_basic_last_modification_time">Last Modified</string>
|
||||
<string name="file_properties_basic_parent_directory">Parent Folder</string>
|
||||
<string name="file_properties_basic_archive_file">Archive File</string>
|
||||
<string name="file_properties_basic_archive_entry">Archive Entry</string>
|
||||
<string name="file_properties_basic_archive_item">Archive Item</string>
|
||||
<string name="file_properties_basic_free_space">Free Space</string>
|
||||
<string name="file_properties_permissions">Permissions</string>
|
||||
<string name="file_properties_permissions_owner">Owner</string>
|
||||
|
|
Loading…
Reference in New Issue
Block a user