Loading Android.bp +14 −0 Original line number Diff line number Diff line // Copyright (C) 2019 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. java_defaults { name: "documentsui_defaults", Loading MODULE_LICENSE_APACHE2 0 → 100644 +0 −0 Empty file added. proguard.flags +5 −1 Original line number Diff line number Diff line Loading @@ -16,3 +16,7 @@ -keep public class androidx.core.view.accessibility.AccessibilityNodeInfoCompat { public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat obtain(); } # To prevent class not found exception in org.brotli.dec.Dictionary -keep final class org.brotli.dec.DictionaryData src/com/android/documentsui/archives/Archive.java +21 −13 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; /** Loading @@ -69,11 +70,11 @@ public abstract class Archive implements Closeable { // The container as well as values are guarded by mEntries. @GuardedBy("mEntries") final Map<String, ZipArchiveEntry> mEntries; final Map<String, ArchiveEntry> mEntries; // The container as well as values and elements of values are guarded by mEntries. @GuardedBy("mEntries") final Map<String, List<ZipArchiveEntry>> mTree; final Map<String, List<ArchiveEntry>> mTree; Archive( Context context, Loading @@ -92,9 +93,16 @@ public abstract class Archive implements Closeable { /** * Returns a valid, normalized path for an entry. */ public static String getEntryPath(ZipArchiveEntry entry) { public static String getEntryPath(ArchiveEntry entry) { if (entry instanceof ZipArchiveEntry) { /** * Some of archive entry doesn't have the same naming rule. * For example: The name of 7 zip directory entry doesn't end with '/'. * Only check for Zip archive. */ Preconditions.checkArgument(entry.isDirectory() == entry.getName().endsWith("/"), "Ill-formated ZIP-file."); } if (entry.getName().startsWith("/")) { return entry.getName(); } else { Loading Loading @@ -135,11 +143,11 @@ public abstract class Archive implements Closeable { } synchronized (mEntries) { final List<ZipArchiveEntry> parentList = mTree.get(parsedParentId.mPath); final List<ArchiveEntry> parentList = mTree.get(parsedParentId.mPath); if (parentList == null) { throw new FileNotFoundException(); } for (final ZipArchiveEntry entry : parentList) { for (final ArchiveEntry entry : parentList) { addCursorRow(result, entry); } } Loading @@ -157,7 +165,7 @@ public abstract class Archive implements Closeable { "Mismatching archive Uri. Expected: %s, actual: %s."); synchronized (mEntries) { final ZipArchiveEntry entry = mEntries.get(parsedId.mPath); final ArchiveEntry entry = mEntries.get(parsedId.mPath); if (entry == null) { throw new FileNotFoundException(); } Loading @@ -178,12 +186,12 @@ public abstract class Archive implements Closeable { "Mismatching archive Uri. Expected: %s, actual: %s."); synchronized (mEntries) { final ZipArchiveEntry entry = mEntries.get(parsedId.mPath); final ArchiveEntry entry = mEntries.get(parsedId.mPath); if (entry == null) { return false; } final ZipArchiveEntry parentEntry = mEntries.get(parsedParentId.mPath); final ArchiveEntry parentEntry = mEntries.get(parsedParentId.mPath); if (parentEntry == null || !parentEntry.isDirectory()) { return false; } Loading @@ -210,7 +218,7 @@ public abstract class Archive implements Closeable { "Mismatching archive Uri. Expected: %s, actual: %s."); synchronized (mEntries) { final ZipArchiveEntry entry = mEntries.get(parsedId.mPath); final ArchiveEntry entry = mEntries.get(parsedId.mPath); if (entry == null) { throw new FileNotFoundException(); } Loading Loading @@ -267,7 +275,7 @@ public abstract class Archive implements Closeable { /** * Not thread safe. */ void addCursorRow(MatrixCursor cursor, ZipArchiveEntry entry) { void addCursorRow(MatrixCursor cursor, ArchiveEntry entry) { final MatrixCursor.RowBuilder row = cursor.newRow(); final ArchiveId parsedId = createArchiveId(getEntryPath(entry)); row.add(Document.COLUMN_DOCUMENT_ID, parsedId.toDocumentId()); Loading @@ -286,7 +294,7 @@ public abstract class Archive implements Closeable { row.add(Document.COLUMN_FLAGS, flags); } static String getMimeTypeForEntry(ZipArchiveEntry entry) { static String getMimeTypeForEntry(ArchiveEntry entry) { if (entry.isDirectory()) { return Document.MIME_TYPE_DIR; } Loading src/com/android/documentsui/archives/ArchiveEntryInputStream.java 0 → 100644 +155 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.archives; import android.text.TextUtils; import androidx.annotation.NonNull; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveInputStream; import org.apache.commons.compress.archivers.sevenz.SevenZFile; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; /** * To simulate the input stream by using ZipFile, SevenZFile, or ArchiveInputStream. */ abstract class ArchiveEntryInputStream extends InputStream { private final long mSize; private final ReadSource mReadSource; private ArchiveEntryInputStream(ReadSource readSource, @NonNull ArchiveEntry archiveEntry) { mReadSource = readSource; mSize = archiveEntry.getSize(); } @Override public int read() throws IOException { throw new UnsupportedOperationException(); } @Override public int read(byte[] b, int off, int len) throws IOException { if (mReadSource != null) { return mReadSource.read(b, off, len); } return -1; /* end of input stream */ } @Override public int available() throws IOException { return mReadSource == null ? 0 : StrictMath.toIntExact(mSize); } /** * The interface describe how to make the ArchiveHandle to next entry. */ private interface NextEntryIterator { ArchiveEntry getNextEntry() throws IOException; } /** * The interface provide where to read the data. */ private interface ReadSource { int read(byte[] b, int off, int len) throws IOException; } private static boolean moveToArchiveEntry( NextEntryIterator nextEntryIterator, ArchiveEntry archiveEntry) throws IOException { ArchiveEntry entry; while ((entry = nextEntryIterator.getNextEntry()) != null) { if (TextUtils.equals(entry.getName(), archiveEntry.getName())) { return true; } } return false; } private static class WrapArchiveInputStream extends ArchiveEntryInputStream { private WrapArchiveInputStream(ReadSource readSource, ArchiveEntry archiveEntry, NextEntryIterator iterator) throws IOException { super(readSource, archiveEntry); moveToArchiveEntry(iterator, archiveEntry); } } private static class WrapZipFileInputStream extends ArchiveEntryInputStream { private final Closeable mCloseable; private WrapZipFileInputStream( ReadSource readSource, @NonNull ArchiveEntry archiveEntry, Closeable closeable) throws IOException { super(readSource, archiveEntry); mCloseable = closeable; } @Override public void close() throws IOException { super.close(); if (mCloseable != null) { mCloseable.close(); } } } static InputStream create(@NonNull ArchiveHandle archiveHandle, @NonNull ArchiveEntry archiveEntry) throws IOException { if (archiveHandle == null) { throw new IllegalArgumentException("archiveHandle is null"); } if (archiveEntry == null) { throw new IllegalArgumentException("ArchiveEntry is empty"); } if (archiveEntry.isDirectory() || archiveEntry.getSize() <= 0 || TextUtils.isEmpty(archiveEntry.getName())) { throw new IllegalArgumentException("ArchiveEntry is an invalid file entry"); } Object commonArchive = archiveHandle.getCommonArchive(); if (commonArchive instanceof SevenZFile) { return new WrapArchiveInputStream( (b, off, len) -> ((SevenZFile) commonArchive).read(b, off, len), archiveEntry, () -> ((SevenZFile) commonArchive).getNextEntry()); } else if (commonArchive instanceof ZipFile) { final InputStream inputStream = ((ZipFile) commonArchive).getInputStream((ZipArchiveEntry) archiveEntry); return new WrapZipFileInputStream( (b, off, len) -> inputStream.read(b, off, len), archiveEntry, () -> inputStream.close()); } else if (commonArchive instanceof ArchiveInputStream) { return new WrapArchiveInputStream( (b, off, len) -> ((ArchiveInputStream) commonArchive).read(b, off, len), archiveEntry, () -> ((ArchiveInputStream) commonArchive).getNextEntry()); } return null; } } Loading
Android.bp +14 −0 Original line number Diff line number Diff line // Copyright (C) 2019 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. java_defaults { name: "documentsui_defaults", Loading
proguard.flags +5 −1 Original line number Diff line number Diff line Loading @@ -16,3 +16,7 @@ -keep public class androidx.core.view.accessibility.AccessibilityNodeInfoCompat { public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat obtain(); } # To prevent class not found exception in org.brotli.dec.Dictionary -keep final class org.brotli.dec.DictionaryData
src/com/android/documentsui/archives/Archive.java +21 −13 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; /** Loading @@ -69,11 +70,11 @@ public abstract class Archive implements Closeable { // The container as well as values are guarded by mEntries. @GuardedBy("mEntries") final Map<String, ZipArchiveEntry> mEntries; final Map<String, ArchiveEntry> mEntries; // The container as well as values and elements of values are guarded by mEntries. @GuardedBy("mEntries") final Map<String, List<ZipArchiveEntry>> mTree; final Map<String, List<ArchiveEntry>> mTree; Archive( Context context, Loading @@ -92,9 +93,16 @@ public abstract class Archive implements Closeable { /** * Returns a valid, normalized path for an entry. */ public static String getEntryPath(ZipArchiveEntry entry) { public static String getEntryPath(ArchiveEntry entry) { if (entry instanceof ZipArchiveEntry) { /** * Some of archive entry doesn't have the same naming rule. * For example: The name of 7 zip directory entry doesn't end with '/'. * Only check for Zip archive. */ Preconditions.checkArgument(entry.isDirectory() == entry.getName().endsWith("/"), "Ill-formated ZIP-file."); } if (entry.getName().startsWith("/")) { return entry.getName(); } else { Loading Loading @@ -135,11 +143,11 @@ public abstract class Archive implements Closeable { } synchronized (mEntries) { final List<ZipArchiveEntry> parentList = mTree.get(parsedParentId.mPath); final List<ArchiveEntry> parentList = mTree.get(parsedParentId.mPath); if (parentList == null) { throw new FileNotFoundException(); } for (final ZipArchiveEntry entry : parentList) { for (final ArchiveEntry entry : parentList) { addCursorRow(result, entry); } } Loading @@ -157,7 +165,7 @@ public abstract class Archive implements Closeable { "Mismatching archive Uri. Expected: %s, actual: %s."); synchronized (mEntries) { final ZipArchiveEntry entry = mEntries.get(parsedId.mPath); final ArchiveEntry entry = mEntries.get(parsedId.mPath); if (entry == null) { throw new FileNotFoundException(); } Loading @@ -178,12 +186,12 @@ public abstract class Archive implements Closeable { "Mismatching archive Uri. Expected: %s, actual: %s."); synchronized (mEntries) { final ZipArchiveEntry entry = mEntries.get(parsedId.mPath); final ArchiveEntry entry = mEntries.get(parsedId.mPath); if (entry == null) { return false; } final ZipArchiveEntry parentEntry = mEntries.get(parsedParentId.mPath); final ArchiveEntry parentEntry = mEntries.get(parsedParentId.mPath); if (parentEntry == null || !parentEntry.isDirectory()) { return false; } Loading @@ -210,7 +218,7 @@ public abstract class Archive implements Closeable { "Mismatching archive Uri. Expected: %s, actual: %s."); synchronized (mEntries) { final ZipArchiveEntry entry = mEntries.get(parsedId.mPath); final ArchiveEntry entry = mEntries.get(parsedId.mPath); if (entry == null) { throw new FileNotFoundException(); } Loading Loading @@ -267,7 +275,7 @@ public abstract class Archive implements Closeable { /** * Not thread safe. */ void addCursorRow(MatrixCursor cursor, ZipArchiveEntry entry) { void addCursorRow(MatrixCursor cursor, ArchiveEntry entry) { final MatrixCursor.RowBuilder row = cursor.newRow(); final ArchiveId parsedId = createArchiveId(getEntryPath(entry)); row.add(Document.COLUMN_DOCUMENT_ID, parsedId.toDocumentId()); Loading @@ -286,7 +294,7 @@ public abstract class Archive implements Closeable { row.add(Document.COLUMN_FLAGS, flags); } static String getMimeTypeForEntry(ZipArchiveEntry entry) { static String getMimeTypeForEntry(ArchiveEntry entry) { if (entry.isDirectory()) { return Document.MIME_TYPE_DIR; } Loading
src/com/android/documentsui/archives/ArchiveEntryInputStream.java 0 → 100644 +155 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.archives; import android.text.TextUtils; import androidx.annotation.NonNull; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveInputStream; import org.apache.commons.compress.archivers.sevenz.SevenZFile; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; /** * To simulate the input stream by using ZipFile, SevenZFile, or ArchiveInputStream. */ abstract class ArchiveEntryInputStream extends InputStream { private final long mSize; private final ReadSource mReadSource; private ArchiveEntryInputStream(ReadSource readSource, @NonNull ArchiveEntry archiveEntry) { mReadSource = readSource; mSize = archiveEntry.getSize(); } @Override public int read() throws IOException { throw new UnsupportedOperationException(); } @Override public int read(byte[] b, int off, int len) throws IOException { if (mReadSource != null) { return mReadSource.read(b, off, len); } return -1; /* end of input stream */ } @Override public int available() throws IOException { return mReadSource == null ? 0 : StrictMath.toIntExact(mSize); } /** * The interface describe how to make the ArchiveHandle to next entry. */ private interface NextEntryIterator { ArchiveEntry getNextEntry() throws IOException; } /** * The interface provide where to read the data. */ private interface ReadSource { int read(byte[] b, int off, int len) throws IOException; } private static boolean moveToArchiveEntry( NextEntryIterator nextEntryIterator, ArchiveEntry archiveEntry) throws IOException { ArchiveEntry entry; while ((entry = nextEntryIterator.getNextEntry()) != null) { if (TextUtils.equals(entry.getName(), archiveEntry.getName())) { return true; } } return false; } private static class WrapArchiveInputStream extends ArchiveEntryInputStream { private WrapArchiveInputStream(ReadSource readSource, ArchiveEntry archiveEntry, NextEntryIterator iterator) throws IOException { super(readSource, archiveEntry); moveToArchiveEntry(iterator, archiveEntry); } } private static class WrapZipFileInputStream extends ArchiveEntryInputStream { private final Closeable mCloseable; private WrapZipFileInputStream( ReadSource readSource, @NonNull ArchiveEntry archiveEntry, Closeable closeable) throws IOException { super(readSource, archiveEntry); mCloseable = closeable; } @Override public void close() throws IOException { super.close(); if (mCloseable != null) { mCloseable.close(); } } } static InputStream create(@NonNull ArchiveHandle archiveHandle, @NonNull ArchiveEntry archiveEntry) throws IOException { if (archiveHandle == null) { throw new IllegalArgumentException("archiveHandle is null"); } if (archiveEntry == null) { throw new IllegalArgumentException("ArchiveEntry is empty"); } if (archiveEntry.isDirectory() || archiveEntry.getSize() <= 0 || TextUtils.isEmpty(archiveEntry.getName())) { throw new IllegalArgumentException("ArchiveEntry is an invalid file entry"); } Object commonArchive = archiveHandle.getCommonArchive(); if (commonArchive instanceof SevenZFile) { return new WrapArchiveInputStream( (b, off, len) -> ((SevenZFile) commonArchive).read(b, off, len), archiveEntry, () -> ((SevenZFile) commonArchive).getNextEntry()); } else if (commonArchive instanceof ZipFile) { final InputStream inputStream = ((ZipFile) commonArchive).getInputStream((ZipArchiveEntry) archiveEntry); return new WrapZipFileInputStream( (b, off, len) -> inputStream.read(b, off, len), archiveEntry, () -> inputStream.close()); } else if (commonArchive instanceof ArchiveInputStream) { return new WrapArchiveInputStream( (b, off, len) -> ((ArchiveInputStream) commonArchive).read(b, off, len), archiveEntry, () -> ((ArchiveInputStream) commonArchive).getNextEntry()); } return null; } }