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

Commit d1384dd8 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "No longer traverse files to get folder size." into arc-apps

parents 04efd4af 1b49a945
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -117,8 +117,8 @@
    <!-- 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>
    <!--Table header for number of items in a folder.-->
    <string name="directory_items">Number of items</string>

    <!--File properties dialog section title. In this section we show information about the default handler application for this type of file.-->
    <string name="handler_app_file_opens_with">This kind of file opens with</string>
+0 −2
Original line number Diff line number Diff line
@@ -60,7 +60,6 @@ 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;
@@ -81,7 +80,6 @@ public class DocumentInfo implements Durable, Parcelable {
        size = -1;
        icon = 0;
        derivedUri = null;
        numberOfChildren = -1;
    }

    @Override
+7 −7
Original line number Diff line number Diff line
@@ -24,13 +24,12 @@ import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.R;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.Lookup;

import java.util.function.Consumer;
import com.android.documentsui.inspector.InspectorController.DetailsDisplay;

/**
 * Displays the basic details about a file.
 */
public class DetailsView extends TableView implements Consumer<DocumentInfo> {
public class DetailsView extends TableView implements DetailsDisplay {

    public DetailsView(Context context) {
        this(context, null);
@@ -63,12 +62,13 @@ public class DetailsView extends TableView implements Consumer<DocumentInfo> {
                    DateFormat.getDateFormat(getContext()).format(info.lastModified));
        }

        if (info.numberOfChildren != -1) {
            put(R.string.directory_children, String.valueOf(info.numberOfChildren));
        }

        if (info.summary != null) {
            put(R.string.sort_dimension_summary, info.summary);
        }
    }

    @Override
    public void setChildrenCount(int count) {
        put(R.string.directory_items, String.valueOf(count));
    }
}
 No newline at end of file
+0 −116
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;

/**
 * Loads more detailed information regarding a directory, specifically its children count and
 * its total estimated size.
 */
public final 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 getCursorOfChildren(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 children = getCursorOfChildren(directories.remove());
            while (children.moveToNext()) {

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

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

    private Cursor getCursorOfChildren(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);
    }
}
+75 −32
Original line number Diff line number Diff line
@@ -28,22 +28,24 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;

import android.provider.DocumentsContract;
import android.support.annotation.Nullable;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.inspector.InspectorController.Loader;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * Asynchronously loads a document's metadata into a DocumentInfo object.
 * Asynchronously loads a document's metadata for the inspector.
 */
public class DocumentLoader implements Loader {

    private final Context mContext;
    private final LoaderManager mLoader;
    private List<Integer> loaderIds;
    private Callbacks mCallbacks;
    private @Nullable Callbacks mDocCallbacks;
    private @Nullable Callbacks mDirCallbacks;

    public DocumentLoader(Context context, LoaderManager loader) {
        checkArgument(context != null);
@@ -53,29 +55,60 @@ public class DocumentLoader implements Loader {
        loaderIds = new ArrayList<>();
    }

    private int getNextLoaderId() {
        int id = 0;
        while(mLoader.getLoader(id) != null) {
            id++;
            checkArgument(id <= Integer.MAX_VALUE);
    /**
     * Loads documents metadata.
     */
    @Override
    public void loadDocInfo(Uri uri, Consumer<DocumentInfo> updateView) {
        //Check that we have correct Uri type and that the loader is not already created.
        checkArgument(uri.getScheme().equals("content"));

        //get a new loader id.
        int loadId = getNextLoaderId();
        checkArgument(mLoader.getLoader(loadId) == null);
        loaderIds.add(loadId);

        Consumer<Cursor> callback = new Consumer<Cursor>() {
            @Override
            public void accept(Cursor cursor) {
                if (cursor == null || !cursor.moveToFirst()) {
                    updateView.accept(null);
                } else {
                    DocumentInfo docInfo = DocumentInfo.fromCursor(cursor, uri.getAuthority());
                    updateView.accept(docInfo);
                }
        return id;
            }
        };

        mDocCallbacks = new Callbacks(mContext, uri, callback);
        mLoader.restartLoader(loadId, null, mDocCallbacks);
    }

    /**
     * @param uri is a Content Uri.
     * Loads a directories item count.
     */
    @Override
    public void load(Uri uri, Consumer<DocumentInfo> callback) {
        //Check that we have correct Uri type and that the loader is not already created.
        checkArgument(uri.getScheme().equals("content"));
    public void loadDirCount(DocumentInfo directory, Consumer<Integer> updateView) {
        checkArgument(directory.isDirectory());
        Uri children = DocumentsContract.buildChildDocumentsUri(
                directory.authority, directory.documentId);

        //get a new loader id.
        int loadId = getNextLoaderId();
        checkArgument(mLoader.getLoader(loadId) == null);
        loaderIds.add(loadId);
        mCallbacks = new Callbacks(mContext, uri, callback);
        mLoader.restartLoader(loadId, null, mCallbacks);

        Consumer<Cursor> callback = new Consumer<Cursor>() {
            @Override
            public void accept(Cursor cursor) {
                if(cursor != null && cursor.moveToFirst()) {
                    updateView.accept(cursor.getCount());
                }
            }
        };

        mDirCallbacks = new Callbacks(mContext, children, callback);
        mLoader.restartLoader(loadId, null, mDirCallbacks);
    }

    @Override
@@ -84,47 +117,57 @@ public class DocumentLoader implements Loader {
            mLoader.destroyLoader(id);
        }
        loaderIds.clear();
        if (mCallbacks.getObserver() != null) {
            mContext.getContentResolver().unregisterContentObserver(mCallbacks.getObserver());

        if (mDocCallbacks != null && mDocCallbacks.getObserver() != null) {
            mContext.getContentResolver().unregisterContentObserver(mDocCallbacks.getObserver());
        }
        if (mDirCallbacks != null && mDirCallbacks.getObserver() != null) {
            mContext.getContentResolver().unregisterContentObserver(mDocCallbacks.getObserver());
        }
    }

    private int getNextLoaderId() {
        int id = 0;
        while(mLoader.getLoader(id) != null) {
            id++;
            checkArgument(id <= Integer.MAX_VALUE);
        }
        return id;
    }

    /**
     * Implements the callback interface for the Loader.
     * Implements the callback interface for cursor loader.
     */
    static final class Callbacks implements LoaderCallbacks<Cursor> {

        private final Context mContext;
        private final Uri mDocUri;
        private final Consumer<DocumentInfo> mDocConsumer;
        private final Uri mUri;
        private final Consumer<Cursor> mCallback;
        private ContentObserver mObserver;

        Callbacks(Context context, Uri uri, Consumer<DocumentInfo> docConsumer) {
        Callbacks(Context context, Uri uri, Consumer<Cursor> callback) {
            checkArgument(context != null);
            checkArgument(uri != null);
            checkArgument(docConsumer != null);
            checkArgument(callback != null);
            mContext = context;
            mDocUri = uri;
            mDocConsumer = docConsumer;
            mUri = uri;
            mCallback = callback;
        }

        @Override
        public android.content.Loader<Cursor> onCreateLoader(int id, Bundle args) {
            return new CursorLoader(mContext, mDocUri, null, null, null, null);
            return new CursorLoader(mContext, mUri, null, null, null, null);
        }

        @Override
        public void onLoadFinished(android.content.Loader<Cursor> loader, Cursor cursor) {

            //returns DocumentInfo null if the cursor is null or isEmpty.
            if (cursor == null || !cursor.moveToFirst()) {
                mDocConsumer.accept(null);
            } else {
            if (cursor != null) {
                mObserver = new InspectorContentObserver(loader::onContentChanged);
                cursor.registerContentObserver(mObserver);
                DocumentInfo docInfo = DocumentInfo.fromCursor(cursor, mDocUri.getAuthority());
                mDocConsumer.accept(docInfo);
            }

            mCallback.accept(cursor);
        }

        @Override
Loading