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

Commit 56dbeff1 authored by Austin Kolander's avatar Austin Kolander Committed by android-build-merger
Browse files

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

am: d1384dd8

Change-Id: Ibfd821b105e86788964437dcfa938d9f7fa466f9
parents 0148e83e d1384dd8
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