FileDocCategorySizeDatePackage
RootsCache.javaAPI DocAndroid 5.1 API14122Thu Mar 12 22:22:40 GMT 2015com.android.documentsui

RootsCache

public class RootsCache extends Object
Cache of known storage backends and their roots.

Fields Summary
private static final boolean
LOGD
public static final android.net.Uri
sNotificationUri
private final android.content.Context
mContext
private final android.database.ContentObserver
mObserver
private final com.android.documentsui.model.RootInfo
mRecentsRoot
private final Object
mLock
private final CountDownLatch
mFirstLoad
private Multimap
mRoots
private HashSet
mStoppedAuthorities
private final HashSet
mObservedAuthorities
Constructors Summary
public RootsCache(android.content.Context context)


       
        mContext = context;
        mObserver = new RootsChangedObserver();
    
Methods Summary
static java.util.ListgetMatchingRoots(java.util.Collection roots, com.android.documentsui.DocumentsActivity.State state)

        final List<RootInfo> matching = Lists.newArrayList();
        for (RootInfo root : roots) {
            final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 0;
            final boolean supportsIsChild = (root.flags & Root.FLAG_SUPPORTS_IS_CHILD) != 0;
            final boolean advanced = (root.flags & Root.FLAG_ADVANCED) != 0;
            final boolean localOnly = (root.flags & Root.FLAG_LOCAL_ONLY) != 0;
            final boolean empty = (root.flags & Root.FLAG_EMPTY) != 0;

            // Exclude read-only devices when creating
            if (state.action == State.ACTION_CREATE && !supportsCreate) continue;
            // Exclude roots that don't support directory picking
            if (state.action == State.ACTION_OPEN_TREE && !supportsIsChild) continue;
            // Exclude advanced devices when not requested
            if (!state.showAdvanced && advanced) continue;
            // Exclude non-local devices when local only
            if (state.localOnly && !localOnly) continue;
            // Only show empty roots when creating
            if (state.action != State.ACTION_CREATE && empty) continue;

            // Only include roots that serve requested content
            final boolean overlap =
                    MimePredicate.mimeMatches(root.derivedMimeTypes, state.acceptMimes) ||
                    MimePredicate.mimeMatches(state.acceptMimes, root.derivedMimeTypes);
            if (!overlap) {
                continue;
            }

            matching.add(root);
        }
        return matching;
    
public java.util.CollectiongetMatchingRootsBlocking(com.android.documentsui.DocumentsActivity.State state)

        waitForFirstLoad();
        loadStoppedAuthorities();
        synchronized (mLock) {
            return getMatchingRoots(mRoots.values(), state);
        }
    
public com.android.documentsui.model.RootInfogetRecentsRoot()

        return mRecentsRoot;
    
public com.android.documentsui.model.RootInfogetRootBlocking(java.lang.String authority, java.lang.String rootId)

        waitForFirstLoad();
        loadStoppedAuthorities();
        synchronized (mLock) {
            return getRootLocked(authority, rootId);
        }
    
private com.android.documentsui.model.RootInfogetRootLocked(java.lang.String authority, java.lang.String rootId)

        for (RootInfo root : mRoots.get(authority)) {
            if (Objects.equals(root.rootId, rootId)) {
                return root;
            }
        }
        return null;
    
public com.android.documentsui.model.RootInfogetRootOneshot(java.lang.String authority, java.lang.String rootId)
Return the requested {@link RootInfo}, but only loading the roots for the requested authority. This is useful when we want to load fast without waiting for all the other roots to come back.

        synchronized (mLock) {
            RootInfo root = getRootLocked(authority, rootId);
            if (root == null) {
                mRoots.putAll(
                        authority, loadRootsForAuthority(mContext.getContentResolver(), authority));
                root = getRootLocked(authority, rootId);
            }
            return root;
        }
    
public java.util.CollectiongetRootsBlocking()

        waitForFirstLoad();
        loadStoppedAuthorities();
        synchronized (mLock) {
            return mRoots.values();
        }
    
public booleanisIconUniqueBlocking(com.android.documentsui.model.RootInfo root)

        waitForFirstLoad();
        loadStoppedAuthorities();
        synchronized (mLock) {
            final int rootIcon = root.derivedIcon != 0 ? root.derivedIcon : root.icon;
            for (RootInfo test : mRoots.get(root.authority)) {
                if (Objects.equals(test.rootId, root.rootId)) {
                    continue;
                }
                final int testIcon = test.derivedIcon != 0 ? test.derivedIcon : test.icon;
                if (testIcon == rootIcon) {
                    return false;
                }
            }
            return true;
        }
    
public booleanisRecentsRoot(com.android.documentsui.model.RootInfo root)

        return mRecentsRoot == root;
    
private java.util.CollectionloadRootsForAuthority(android.content.ContentResolver resolver, java.lang.String authority)
Bring up requested provider and query for all active roots.

        if (LOGD) Log.d(TAG, "Loading roots for " + authority);

        synchronized (mObservedAuthorities) {
            if (mObservedAuthorities.add(authority)) {
                // Watch for any future updates
                final Uri rootsUri = DocumentsContract.buildRootsUri(authority);
                mContext.getContentResolver().registerContentObserver(rootsUri, true, mObserver);
            }
        }

        final List<RootInfo> roots = Lists.newArrayList();
        final Uri rootsUri = DocumentsContract.buildRootsUri(authority);

        ContentProviderClient client = null;
        Cursor cursor = null;
        try {
            client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
            cursor = client.query(rootsUri, null, null, null, null);
            while (cursor.moveToNext()) {
                final RootInfo root = RootInfo.fromRootsCursor(authority, cursor);
                roots.add(root);
            }
        } catch (Exception e) {
            Log.w(TAG, "Failed to load some roots from " + authority + ": " + e);
        } finally {
            IoUtils.closeQuietly(cursor);
            ContentProviderClient.releaseQuietly(client);
        }
        return roots;
    
private voidloadStoppedAuthorities()
Load roots from authorities that are in stopped state. Normal {@link UpdateTask} passes ignore stopped applications.

        final ContentResolver resolver = mContext.getContentResolver();
        synchronized (mLock) {
            for (String authority : mStoppedAuthorities) {
                if (LOGD) Log.d(TAG, "Loading stopped authority " + authority);
                mRoots.putAll(authority, loadRootsForAuthority(resolver, authority));
            }
            mStoppedAuthorities.clear();
        }
    
public voidupdateAsync()
Gather roots from all known storage providers.

        // Special root for recents
        mRecentsRoot.authority = null;
        mRecentsRoot.rootId = null;
        mRecentsRoot.derivedIcon = R.drawable.ic_root_recent;
        mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE
                | Root.FLAG_SUPPORTS_IS_CHILD;
        mRecentsRoot.title = mContext.getString(R.string.root_recent);
        mRecentsRoot.availableBytes = -1;

        new UpdateTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    
public voidupdateAuthorityAsync(java.lang.String authority)
Gather roots from storage providers belonging to given authority.

        final ProviderInfo info = mContext.getPackageManager().resolveContentProvider(authority, 0);
        if (info != null) {
            updatePackageAsync(info.packageName);
        }
    
public voidupdatePackageAsync(java.lang.String packageName)
Gather roots from storage providers belonging to given package name.

        new UpdateTask(packageName).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    
private voidwaitForFirstLoad()

        boolean success = false;
        try {
            success = mFirstLoad.await(15, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
        }
        if (!success) {
            Log.w(TAG, "Timeout waiting for first update");
        }