Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b3d4c72 authored by Felka Chang's avatar Felka Chang Committed by Android (Google) Code Review
Browse files

Merge "Add feature to support multiple archive"

parents da119930 864699a1
Loading
Loading
Loading
Loading
+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",

MODULE_LICENSE_APACHE2

0 → 100644
+0 −0

Empty file added.

+5 −1
Original line number Diff line number Diff line
@@ -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
+21 −13
Original line number Diff line number Diff line
@@ -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;

/**
@@ -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,
@@ -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 {
@@ -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);
            }
        }
@@ -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();
            }
@@ -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;
            }
@@ -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();
            }
@@ -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());
@@ -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;
        }
+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