Load RootInfo in background, invalidation.

Move all RootInfo queries to background threads to avoid janking
the UI.  Update passes happen on spawed task, which swaps out updated
cache results when finished.  Support partial updates when only a
single package/authority has changed.  Watch for change notifications
for roots, since flags can change over time.

Ignore stopped packages when in background, but query them for roots
when launching any picker UI.

Optimize management launches by treating as one-shot requests that
don't need to wait for all RootInfo.

Bug: 10600454, 10745490
Change-Id: Ibc7b15688ef6b41bd7e9dd0d7564b501e60e49a9
diff --git a/src/com/android/documentsui/DirectoryFragment.java b/src/com/android/documentsui/DirectoryFragment.java
index 4c2c99c..911e9ed 100644
--- a/src/com/android/documentsui/DirectoryFragment.java
+++ b/src/com/android/documentsui/DirectoryFragment.java
@@ -249,8 +249,7 @@
                                 context, mType, root, doc, contentsUri, state.userSortOrder);
                     case TYPE_RECENT_OPEN:
                         final RootsCache roots = DocumentsApplication.getRootsCache(context);
-                        final List<RootInfo> matchingRoots = roots.getMatchingRoots(state);
-                        return new RecentLoader(context, matchingRoots, state.acceptMimes);
+                        return new RecentLoader(context, roots, state);
                     default:
                         throw new IllegalStateException("Unknown type " + mType);
                 }
@@ -797,7 +796,9 @@
 
             Drawable iconDrawable = null;
             if (mType == TYPE_RECENT_OPEN) {
-                final RootInfo root = roots.getRoot(docAuthority, docRootId);
+                // We've already had to enumerate roots before any results can
+                // be shown, so this will never block.
+                final RootInfo root = roots.getRootBlocking(docAuthority, docRootId);
                 iconDrawable = root.loadIcon(context);
 
                 if (summary != null) {
@@ -808,7 +809,7 @@
                         summary.setVisibility(View.VISIBLE);
                         hasLine2 = true;
                     } else {
-                        if (iconDrawable != null && roots.isIconUnique(root)) {
+                        if (iconDrawable != null && roots.isIconUniqueBlocking(root)) {
                             // No summary needed if icon speaks for itself
                             summary.setVisibility(View.INVISIBLE);
                         } else {