DocumentsUI handles GET_CONTENT; hinting, errors.

Document browser now takes over all GET_CONTENT requests that request
openable Uris. It shows both storage backends and includes other apps
that respond to GET_CONTENT. Only grants transient read permissions.

Better guarding against throwing storage backends. Send sort order
and local-only hinting to backends.

Require that OPEN/CREATE_DOC users include openable category.

Bug: 10330112, 10329976, 10340741, 10331689, 10329971
Change-Id: Ieb8768a6d71201816046f4a4c48832061a313c28
diff --git a/src/com/android/documentsui/DirectoryLoader.java b/src/com/android/documentsui/DirectoryLoader.java
index 94c2b61..98f9a4d 100644
--- a/src/com/android/documentsui/DirectoryLoader.java
+++ b/src/com/android/documentsui/DirectoryLoader.java
@@ -26,6 +26,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.CancellationSignal;
+import android.provider.DocumentsContract.DocumentColumns;
 import android.util.Log;
 
 import com.android.documentsui.model.Document;
@@ -38,6 +39,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.LinkedList;
 import java.util.List;
 
 public class DirectoryLoader extends UriDerivativeLoader<List<Document>> {
@@ -46,6 +48,17 @@
     private Predicate<Document> mFilter;
     private Comparator<Document> mSortOrder;
 
+    /**
+     * Stub result that represents an internal error.
+     */
+    public static class ExceptionResult extends LinkedList<Document> {
+        public final Exception e;
+
+        public ExceptionResult(Exception e) {
+            this.e = e;
+        }
+    }
+
     public DirectoryLoader(Context context, Uri uri, int type, Predicate<Document> filter,
             Comparator<Document> sortOrder) {
         super(context, uri);
@@ -56,11 +69,18 @@
 
     @Override
     public List<Document> loadInBackground(Uri uri, CancellationSignal signal) {
+        try {
+            return loadInBackgroundInternal(uri, signal);
+        } catch (Exception e) {
+            return new ExceptionResult(e);
+        }
+    }
+
+    private List<Document> loadInBackgroundInternal(Uri uri, CancellationSignal signal) {
         final ArrayList<Document> result = Lists.newArrayList();
 
-        // TODO: send selection and sorting hints to backend
         final ContentResolver resolver = getContext().getContentResolver();
-        final Cursor cursor = resolver.query(uri, null, null, null, null, signal);
+        final Cursor cursor = resolver.query(uri, null, null, null, getQuerySortOrder(), signal);
         try {
             while (cursor != null && cursor.moveToNext()) {
                 Document doc = null;
@@ -94,4 +114,16 @@
 
         return result;
     }
+
+    private String getQuerySortOrder() {
+        if (mSortOrder instanceof Document.DateComparator) {
+            return DocumentColumns.LAST_MODIFIED + " DESC";
+        } else if (mSortOrder instanceof Document.NameComparator) {
+            return DocumentColumns.DISPLAY_NAME + " ASC";
+        } else if (mSortOrder instanceof Document.SizeComparator) {
+            return DocumentColumns.SIZE + " DESC";
+        } else {
+            return null;
+        }
+    }
 }