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

Commit 45ac5fe9 authored by Austin Kolander's avatar Austin Kolander
Browse files

Add support for getting more infomation about a directory.

Such as number of children in a directory and size of the directory.

Bug: 62635555
Test: DirectoryLoaderTest
Change-Id: Ib3ba26bfe314cc9873d4b3be31654c703640f416
parent 2ea6e648
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -117,6 +117,9 @@
    <!-- Table header for last modified time. [CHAR_LIMIT=18] -->
    <string name="sort_dimension_date">Modified</string>

    <!--Table header for number of children-->
    <string name="directory_children">Number of Children</string>

    <!-- content description to describe ascending sorting used with upward arrow in table header. -->
    <string name="sort_direction_ascending">Ascending</string>
    <!-- content description to describe descending sorting used with downward arrow in table header. -->
+2 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ public class DocumentInfo implements Durable, Parcelable {
    public String summary;
    public long size;
    public int icon;
    public int numberOfChildren;

    /** Derived fields that aren't persisted */
    public Uri derivedUri;
@@ -80,6 +81,7 @@ public class DocumentInfo implements Durable, Parcelable {
        size = -1;
        icon = 0;
        derivedUri = null;
        numberOfChildren = -1;
    }

    @Override
+4 −0
Original line number Diff line number Diff line
@@ -81,5 +81,9 @@ public class DetailsView extends TableView implements Consumer<DocumentInfo> {
        setRow(R.string.sort_dimension_file_type, info.mimeType);
        setRow(R.string.sort_dimension_size, formatSize(info.size));
        setRow(R.string.sort_dimension_date, String.valueOf(info.lastModified));

        if(info.numberOfChildren != -1) {
            setRow(R.string.directory_children, String.valueOf(info.numberOfChildren));
        }
    }
}
 No newline at end of file
+112 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.inspector;

import static com.android.internal.util.Preconditions.checkArgument;

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import com.android.documentsui.base.DocumentInfo;
import java.util.LinkedList;
import java.util.Queue;
import java.util.function.Consumer;

public class DirectoryLoader extends AsyncTask<DocumentInfo, Integer, DocumentInfo> {

    private static int MAXIMUM_FILE_COUNT = 5000;

    private final ContentResolver mResolver;
    private final Consumer<DocumentInfo> mCallback;

    public DirectoryLoader(ContentResolver resolver, Consumer<DocumentInfo> callback) {
        mResolver = resolver;
        mCallback = callback;
    }

    /**
     * Finds the size and number of children.
     */
    @Override
    protected DocumentInfo doInBackground(DocumentInfo... documentInfos) {
        checkArgument(documentInfos.length == 1);

        if (documentInfos[0].isDirectory()) {
            DocumentInfo directory = documentInfos[0];
            directory.numberOfChildren = getChildrenCount(directory);
            directory.size = getDirectorySize(directory);
            return directory;
        }
        else {
            return null;
        }
    }

    @Override
    protected void onPostExecute(DocumentInfo result) {
        mCallback.accept(result);
    }

    private int getChildrenCount(DocumentInfo directory) {
        return getCursor(directory).getCount();
    }

    private long getDirectorySize(DocumentInfo directory) {

        Long size = 0L;
        Queue<DocumentInfo> directories = new LinkedList<>();
        directories.add(directory);
        int count = 0;

        while(directories.size() > 0) {

            //makes a cursor from first directory in queue.
            Cursor cursor = getCursor(directories.remove());
            while (cursor.moveToNext()) {

                //hard stop if we have processed a large amount of files.
                if(count >= MAXIMUM_FILE_COUNT) {
                    return size;
                }

                //iterate through the directory.
                DocumentInfo info = DocumentInfo.fromCursor(cursor, directory.authority);
                if (info.isDirectory()) {
                    directories.add(info);
                } else {
                    size += info.size;
                }
                count++;
            }
            //done checking this directory, close the cursor.
            cursor.close();
        }
        return size;
    }

    private Cursor getCursor(DocumentInfo directory) {
        checkArgument(directory.isDirectory());

        Uri children = DocumentsContract.buildChildDocumentsUri(
            directory.authority, directory.documentId);

        return mResolver
                .query(children, null, null, null, Document.COLUMN_SIZE + " DESC", null);
    }
}
+33 −5
Original line number Diff line number Diff line
@@ -25,12 +25,14 @@ import android.provider.DocumentsContract;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.view.View;

import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.ProviderExecutor;
import com.android.documentsui.R;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.Lookup;
import com.android.documentsui.roots.ProvidersAccess;
import com.android.documentsui.ui.Snackbars;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
 * A controller that coordinates retrieving document information and sending it to the view.
@@ -45,6 +47,7 @@ public final class InspectorController {
    private final Context mContext;
    private final ProvidersAccess mProviders;
    private final Runnable mShowSnackbar;
    private final Lookup<String, Executor> mExecutors;

    /**
     * InspectorControllerTest relies on this controller.
@@ -52,7 +55,8 @@ public final class InspectorController {
    @VisibleForTesting
    public InspectorController(Context context, Loader loader, ProvidersAccess providers,
            boolean showDebug, Consumer<DocumentInfo> header, Consumer<DocumentInfo> details,
            Consumer<DocumentInfo> debugView, Runnable showSnackbar) {
            Consumer<DocumentInfo> debugView, Lookup<String, Executor> executors,
            Runnable showSnackbar) {

        checkArgument(context != null);
        checkArgument(loader != null);
@@ -61,6 +65,7 @@ public final class InspectorController {
        checkArgument(details != null);
        checkArgument(debugView != null);
        checkArgument(showSnackbar != null);
        checkArgument(executors != null);

        mContext = context;
        mLoader = loader;
@@ -69,6 +74,7 @@ public final class InspectorController {
        mHeader = header;
        mDetails = details;
        mDebugView = debugView;
        mExecutors = executors;
        mShowSnackbar = showSnackbar;
    }

@@ -81,6 +87,7 @@ public final class InspectorController {
                (HeaderView) layout.findViewById(R.id.inspector_header_view),
                (DetailsView) layout.findViewById(R.id.inspector_details_view),
                (DebugView) layout.findViewById(R.id.inspector_debug_view),
                    ProviderExecutor::forAuthority,
                () -> {
                    // using a runnable to support unit testing this feature.
                    Snackbars.showInspectorError(activity);
@@ -100,24 +107,45 @@ public final class InspectorController {
    }

    /**
     * Updates the view.
     * Updates the view with documentInfo.
     */
    @Nullable
    public void updateView(@Nullable DocumentInfo docInfo) {

        if (docInfo == null) {
            mShowSnackbar.run();
        }
        else {
        } else {
            mHeader.accept(docInfo);
            mDetails.accept(docInfo);

            if (docInfo.isDirectory()) {
                new DirectoryLoader(mContext.getContentResolver(),
                    this::displayDirectory)
                    .executeOnExecutor(mExecutors.lookup(docInfo.authority), docInfo);
            }

            if (mShowDebug) {
                mDebugView.accept(docInfo);
            }
        }
    }

    /**
     * Displays a directory's information to the view.
     *
     * @param dirInfo - null if uri was not to a directory.
     */
    @Nullable
    public void displayDirectory(@Nullable DocumentInfo directory) {

        if (directory == null) {
            mShowSnackbar.run();
        } else {
            //update directory information.
            mDetails.accept(directory);
        }
    }

    /**
     * Shows the selected document in it's content provider.
     *
Loading