Merge "Make the executor pool size as 1" into mainline-prod
diff --git a/res/drawable/launcher_screen.xml b/res/drawable/launcher_screen.xml
index 4851606..c0d8146 100644
--- a/res/drawable/launcher_screen.xml
+++ b/res/drawable/launcher_screen.xml
@@ -3,7 +3,7 @@
   <item android:drawable="@android:color/white"/>
 
   <item
-      android:drawable="@drawable/app_icon"
+      android:drawable="@drawable/splash_screen"
       android:height="150dp"
       android:width="150dp"
       android:gravity="center"/>
diff --git a/res/drawable/launcher_screen_night.xml b/res/drawable/launcher_screen_night.xml
index 8a9ffd3..983c497 100644
--- a/res/drawable/launcher_screen_night.xml
+++ b/res/drawable/launcher_screen_night.xml
@@ -3,7 +3,7 @@
   <item android:drawable="@color/app_background_color"/>
 
   <item
-      android:drawable="@drawable/app_icon"
+      android:drawable="@drawable/splash_screen"
       android:height="150dp"
       android:width="150dp"
       android:gravity="center"/>
diff --git a/res/drawable/list_divider.xml b/res/drawable/list_divider.xml
index 5768aff..5067af0 100644
--- a/res/drawable/list_divider.xml
+++ b/res/drawable/list_divider.xml
@@ -16,7 +16,7 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:tint="?android:attr/colorForeground">
-    <solid android:color="#1f000000" />
+    <solid android:color="@color/list_divider_color" />
     <size
         android:height="1dp"
         android:width="1dp" />
diff --git a/res/drawable/splash_screen.xml b/res/drawable/splash_screen.xml
new file mode 100644
index 0000000..3f0c48b
--- /dev/null
+++ b/res/drawable/splash_screen.xml
@@ -0,0 +1,64 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
+      android:fillColor="#4285F4"/>
+  <path
+      android:pathData="M23,12c0,6.1 -4.9,11 -11,11S1,18.1 1,12c0,0 0,0 0,-0.1c0,6 4.9,10.9 11,10.9S23,18 23,12C23,12 23,12 23,12z"
+      android:strokeAlpha="0.2"
+      android:fillColor="#263238"
+      android:fillAlpha="0.2"/>
+  <path
+      android:pathData="M23,12C23,12 23,12 23,12c0,-6 -4.9,-10.9 -11,-10.9S1,6 1,12.1c0,0 0,0 0,-0.1C1,5.9 5.9,1 12,1S23,5.9 23,12z"
+      android:strokeAlpha="0.2"
+      android:fillColor="#FFFFFF"
+      android:fillAlpha="0.2"/>
+  <path
+      android:pathData="M22.8,14.2c-1,4.8 -5,8.4 -9.9,8.8l-6.4,-6.4L17.6,9C17.6,9 22.8,14.2 22.8,14.2z"
+      android:fillColor="#4285F4"/>
+  <path
+      android:pathData="M22.8,14.2c-1,4.8 -5,8.4 -9.9,8.8l-6.4,-6.4L17.6,9C17.6,9 22.8,14.2 22.8,14.2z">
+    <aapt:attr name="android:fillColor">
+      <gradient
+          android:startY="12.203438"
+          android:startX="11.452812"
+          android:endY="20.219812"
+          android:endX="19.469187"
+          android:type="linear">
+        <item android:offset="0" android:color="#33263238"/>
+        <item android:offset="1" android:color="#05263238"/>
+      </gradient>
+    </aapt:attr>
+  </path>
+  <path
+      android:pathData="M16.5,8.5H12L10.8,7H7.5C6.7,7 6,7.7 6,8.5v7C6,16.3 6.7,17 7.5,17h9c0.8,0 1.5,-0.7 1.5,-1.5V10C18,9.2 17.3,8.5 16.5,8.5z"
+      android:fillColor="#F5F5F5"/>
+  <path
+      android:pathData="M18,10v0.1c0,-0.8 -0.7,-1.5 -1.5,-1.5H12l-1.2,-1.5H7.5C6.7,7.1 6,7.8 6,8.6V8.5C6,7.7 6.7,7 7.5,7h3.2L12,8.5h4.5C17.3,8.5 18,9.2 18,10z"
+      android:strokeAlpha="0.4"
+      android:fillColor="#FFFFFF"
+      android:fillAlpha="0.4"/>
+  <path
+      android:pathData="M18,15.5v0.1c0,0.8 -0.7,1.5 -1.5,1.5h-9c-0.8,0 -1.5,-0.7 -1.5,-1.5v-0.1C6,16.3 6.7,17 7.5,17h9C17.3,17 18,16.3 18,15.5z"
+      android:strokeAlpha="0.2"
+      android:fillColor="#263238"
+      android:fillAlpha="0.2"/>
+  <path
+      android:pathData="M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
+      android:fillAlpha="0.1">
+    <aapt:attr name="android:fillColor">
+      <gradient
+          android:gradientRadius="22.333876"
+          android:centerX="3.238875"
+          android:centerY="5.0445"
+          android:type="radial">
+        <item android:offset="0" android:color="#FFFFFFFF"/>
+        <item android:offset="1" android:color="#00FFFFFF"/>
+      </gradient>
+    </aapt:attr>
+  </path>
+</vector>
diff --git a/res/layout/dialog_file_name.xml b/res/layout/dialog_file_name.xml
index 0ebd936..7f2a959 100644
--- a/res/layout/dialog_file_name.xml
+++ b/res/layout/dialog_file_name.xml
@@ -35,6 +35,7 @@
             android:id="@android:id/text1"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:maxLength="255"
             android:inputType="textCapSentences"/>
 
     </com.google.android.material.textfield.TextInputLayout>
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index 86bcbbc..1da85cd 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -33,4 +33,6 @@
     <!-- AppCompat.textColorSecondary -->
     <color name="doc_list_item_subtitle_enabled">#b3ffffff</color>
     <color name="doc_list_item_subtitle_disabled">#36ffffff</color>
+
+    <color name="list_divider_color">#9aa0a6</color>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index e00984a..aa0c3a4 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -52,4 +52,6 @@
 
     <color name="doc_list_item_subtitle_enabled">#5F6368</color> <!-- Gray 700 -->
     <color name="doc_list_item_subtitle_disabled">#613c4043</color> <!-- 38% Grey800 -->
+
+    <color name="list_divider_color">#1f000000</color>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 088e672..66e16b2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -540,4 +540,11 @@
     <!-- Snackbar shown when users drag and drop files from another app
     to DocumentsUI. [CHAR_LIMIT=100] -->
     <string name="drag_from_another_app">You can\u2019t move files from another app.</string>
+
+    <!-- Accessibility announcement when switching to grid mode of files and directories shown. [CHAR_LIMIT=100] -->
+    <string name="grid_mode_showing">Showing in grid mode.</string>
+
+    <!-- Accessibility announcement when switching to list mode of files and directories shown. [CHAR_LIMIT=100] -->
+    <string name="list_mode_showing">Showing in list mode.</string>
+
 </resources>
diff --git a/src/com/android/documentsui/DirectoryLoader.java b/src/com/android/documentsui/DirectoryLoader.java
index 458f861..2775ec4 100644
--- a/src/com/android/documentsui/DirectoryLoader.java
+++ b/src/com/android/documentsui/DirectoryLoader.java
@@ -56,7 +56,6 @@
 public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
 
     private static final String TAG = "DirectoryLoader";
-
     private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
     private static final String[] PHOTO_PICKING_ACCEPT_MIMES = new String[]
             {Document.MIME_TYPE_DIR, MimeTypes.IMAGE_MIME};
@@ -178,17 +177,16 @@
             }
             cursor.registerContentObserver(mObserver);
 
-            // Filter hidden files.
-            cursor = new FilteringCursorWrapper(cursor, mState.showHiddenFiles);
-
+            FilteringCursorWrapper filteringCursor = new FilteringCursorWrapper(cursor);
+            filteringCursor.filterHiddenFiles(mState.showHiddenFiles);
             if (mSearchMode && !mFeatures.isFoldersInSearchResultsEnabled()) {
                 // There is no findDocumentPath API. Enable filtering on folders in search mode.
-                cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
+                filteringCursor.filterMimes(/* acceptMimes= */ null, SEARCH_REJECT_MIMES);
             }
-
             if (mPhotoPicking) {
-                cursor = new FilteringCursorWrapper(cursor, PHOTO_PICKING_ACCEPT_MIMES, null);
+                filteringCursor.filterMimes(PHOTO_PICKING_ACCEPT_MIMES, /* rejectMimes= */ null);
             }
+            cursor = filteringCursor;
 
             // TODO: When API tweaks have landed, use ContentResolver.EXTRA_HONORED_ARGS
             // instead of checking directly for ContentResolver.QUERY_ARG_SORT_COLUMNS (won't work)
@@ -281,10 +279,11 @@
 
     @Override
     protected void onStartLoading() {
-        if (mResult != null) {
+        boolean isCursorStale = checkIfCursorStale(mResult);
+        if (mResult != null && !isCursorStale) {
             deliverResult(mResult);
         }
-        if (takeContentChanged() || mResult == null) {
+        if (takeContentChanged() || mResult == null || isCursorStale) {
             forceLoad();
         }
     }
@@ -313,4 +312,22 @@
         FileUtils.closeQuietly(mResult);
         mResult = null;
     }
+
+    private boolean checkIfCursorStale(DirectoryResult result) {
+        if (mResult == null) {
+            return true;
+        }
+        Cursor cursor = result.cursor;
+        cursor.moveToPosition(-1);
+        for (int pos = 0; pos < cursor.getCount(); ++pos) {
+            try {
+                if (!cursor.moveToNext()) {
+                    return true;
+                }
+            } catch (Exception e) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/documentsui/MultiRootDocumentsLoader.java b/src/com/android/documentsui/MultiRootDocumentsLoader.java
index 643f122..b25828d 100644
--- a/src/com/android/documentsui/MultiRootDocumentsLoader.java
+++ b/src/com/android/documentsui/MultiRootDocumentsLoader.java
@@ -104,8 +104,6 @@
      * @param state current state
      * @param executors the executors of authorities
      * @param fileTypeMap the map of mime types and file types.
-     * @param lock the selection lock
-     * @param contentChangedCallback callback when content changed
      */
     public MultiRootDocumentsLoader(Context context, ProvidersAccess providers, State state,
             Lookup<String, Executor> executors, Lookup<String, String> fileTypeMap) {
@@ -181,17 +179,18 @@
                             continue;
                         }
 
-                        // Filter hidden files.
-                        cursor = new FilteringCursorWrapper(cursor, mState.showHiddenFiles);
-
-                        final FilteringCursorWrapper filtered = new FilteringCursorWrapper(
-                                cursor, mState.acceptMimes, getRejectMimes(), rejectBefore) {
+                        final FilteringCursorWrapper filteredCursor =
+                                new FilteringCursorWrapper(cursor) {
                             @Override
                             public void close() {
                                 // Ignored, since we manage cursor lifecycle internally
                             }
                         };
-                        cursors.add(filtered);
+                        filteredCursor.filterHiddenFiles(mState.showHiddenFiles);
+                        filteredCursor.filterMimes(mState.acceptMimes, getRejectMimes());
+                        filteredCursor.filterLastModified(rejectBefore);
+
+                        cursors.add(filteredCursor);
                     }
 
                 } catch (InterruptedException e) {
@@ -303,10 +302,11 @@
 
     @Override
     protected void onStartLoading() {
-        if (mResult != null) {
+        boolean isCursorStale = checkIfCursorStale(mResult);
+        if (mResult != null && !isCursorStale) {
             deliverResult(mResult);
         }
-        if (takeContentChanged() || mResult == null) {
+        if (takeContentChanged() || mResult == null || isCursorStale) {
             forceLoad();
         }
     }
@@ -456,4 +456,22 @@
             mIsClosed = true;
         }
     }
+
+    private boolean checkIfCursorStale(DirectoryResult result) {
+        if (mResult == null) {
+            return true;
+        }
+        Cursor cursor = result.cursor;
+        cursor.moveToPosition(-1);
+        for (int pos = 0; pos < cursor.getCount(); ++pos) {
+            try {
+                if (!cursor.moveToNext()) {
+                    return true;
+                }
+            } catch (Exception e) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/documentsui/archives/ArchiveEntryInputStream.java b/src/com/android/documentsui/archives/ArchiveEntryInputStream.java
index 0b1c0c2..2c34e5a 100644
--- a/src/com/android/documentsui/archives/ArchiveEntryInputStream.java
+++ b/src/com/android/documentsui/archives/ArchiveEntryInputStream.java
@@ -20,16 +20,16 @@
 
 import androidx.annotation.NonNull;
 
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.InputStream;
-
 import org.apache.commons.compress.archivers.ArchiveEntry;
 import org.apache.commons.compress.archivers.ArchiveInputStream;
 import org.apache.commons.compress.archivers.sevenz.SevenZFile;
 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
 import org.apache.commons.compress.archivers.zip.ZipFile;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+
 /**
  * To simulate the input stream by using ZipFile, SevenZFile, or ArchiveInputStream.
  */
@@ -124,7 +124,7 @@
             throw new IllegalArgumentException("ArchiveEntry is empty");
         }
 
-        if (archiveEntry.isDirectory() || archiveEntry.getSize() <= 0
+        if (archiveEntry.isDirectory() || archiveEntry.getSize() < 0
                 || TextUtils.isEmpty(archiveEntry.getName())) {
             throw new IllegalArgumentException("ArchiveEntry is an invalid file entry");
         }
diff --git a/src/com/android/documentsui/base/FilteringCursorWrapper.java b/src/com/android/documentsui/base/FilteringCursorWrapper.java
index 67b7533..577e47c 100644
--- a/src/com/android/documentsui/base/FilteringCursorWrapper.java
+++ b/src/com/android/documentsui/base/FilteringCursorWrapper.java
@@ -29,68 +29,62 @@
 import android.util.Log;
 
 /**
- * Cursor wrapper that filters MIME types not matching given list.
+ * Cursor wrapper that filters cursor results by given conditions.
  */
 public class FilteringCursorWrapper extends AbstractCursor {
     private final Cursor mCursor;
 
-    private final int[] mPosition;
+    private int[] mPositions;
     private int mCount;
 
-    public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes) {
-        this(cursor, acceptMimes, null, Long.MIN_VALUE);
-    }
-
-    public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes, String[] rejectMimes) {
-        this(cursor, acceptMimes, rejectMimes, Long.MIN_VALUE);
-    }
-
-    public FilteringCursorWrapper(
-            Cursor cursor, String[] acceptMimes, String[] rejectMimes, long rejectBefore) {
+    public FilteringCursorWrapper(Cursor cursor) {
         mCursor = cursor;
+        mCount = cursor.getCount();
+        mPositions = new int[mCount];
+        for (int i = 0; i < mCount; i++) {
+            mPositions[i] = i;
+        }
+    }
 
-        final int count = cursor.getCount();
-        mPosition = new int[count];
-
-        cursor.moveToPosition(-1);
-        while (cursor.moveToNext() && mCount < count) {
+    /**
+     * Filters cursor according to mimes. If both lists are empty, all mimes will be rejected.
+     *
+     * @param acceptMimes allowed list of mimes
+     * @param rejectMimes blocked list of mimes
+     */
+    public void filterMimes(String[] acceptMimes, String[] rejectMimes) {
+        filterByCondition((cursor) -> {
             final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
-            final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
             if (rejectMimes != null && MimeTypes.mimeMatches(rejectMimes, mimeType)) {
-                continue;
+                return false;
             }
-            if (lastModified < rejectBefore) {
-                continue;
-            }
-            if (MimeTypes.mimeMatches(acceptMimes, mimeType)) {
-                mPosition[mCount++] = cursor.getPosition();
-            }
-        }
-
-        if (DEBUG && mCount != cursor.getCount()) {
-            Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount);
-        }
+            return MimeTypes.mimeMatches(acceptMimes, mimeType);
+        });
     }
 
-    public FilteringCursorWrapper(Cursor cursor, boolean showHiddenFiles) {
-        mCursor = cursor;
+    /** Filters cursor according to last modified time, and reject earlier than given timestamp. */
+    public void filterLastModified(long rejectBeforeTimestamp) {
+        filterByCondition((cursor) -> {
+            final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
+            return lastModified >= rejectBeforeTimestamp;
+        });
+    }
 
-        final int count = cursor.getCount();
-        mPosition = new int[count];
+    /** Filter hidden files based on preference. */
+    public void filterHiddenFiles(boolean showHiddenFiles) {
+        if (showHiddenFiles) {
+            return;
+        }
 
-        cursor.moveToPosition(-1);
-        while (cursor.moveToNext() && mCount < count) {
+        filterByCondition((cursor) -> {
+            // Judge by name and documentId separately because for some providers
+            // e.g. DownloadProvider, documentId may not contain file name.
+            final String name = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
             final String documentId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
-            if (!showHiddenFiles && documentId != null
-                    && (documentId.startsWith(".") || documentId.contains("/."))) {
-                continue;
-            }
-            mPosition[mCount++] = cursor.getPosition();
-        }
-
-        if (DEBUG && mCount != cursor.getCount()) {
-            Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount);
-        }
+            boolean documentIdHidden = documentId != null && documentId.contains("/.");
+            boolean fileNameHidden = name != null && name.startsWith(".");
+            return !(documentIdHidden || fileNameHidden);
+        });
     }
 
     @Override
@@ -106,7 +100,7 @@
 
     @Override
     public boolean onMove(int oldPosition, int newPosition) {
-        return mCursor.moveToPosition(mPosition[newPosition]);
+        return mCursor.moveToPosition(mPositions[newPosition]);
     }
 
     @Override
@@ -168,4 +162,27 @@
     public void unregisterContentObserver(ContentObserver observer) {
         mCursor.unregisterContentObserver(observer);
     }
+
+    private interface FilteringCondition {
+        boolean accept(Cursor cursor);
+    }
+
+    private void filterByCondition(FilteringCondition condition) {
+        final int oldCount = this.getCount();
+        int[] newPositions = new int[oldCount];
+        int newCount = 0;
+
+        this.moveToPosition(-1);
+        while (this.moveToNext() && newCount < oldCount) {
+            if (condition.accept(mCursor)) {
+                newPositions[newCount++] = mPositions[this.getPosition()];
+            }
+        }
+
+        if (DEBUG && newCount != this.getCount()) {
+            Log.d(TAG, "Before filtering " + oldCount + ", after " + newCount);
+        }
+        mCount = newCount;
+        mPositions = newPositions;
+    }
 }
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 8fa02ca..a079ab1 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -180,6 +180,7 @@
     private SelectionMetadata mSelectionMetadata;
     private KeyInputHandler mKeyListener;
     private @Nullable DragHoverListener mDragHoverListener;
+    private View mRootView;
     private IconHelper mIconHelper;
     private SwipeRefreshLayout mRefreshLayout;
     private RecyclerView mRecView;
@@ -348,12 +349,12 @@
 
         mHandler = new Handler(Looper.getMainLooper());
         mActivity = (BaseActivity) getActivity();
-        final View view = inflater.inflate(R.layout.fragment_directory, container, false);
+        mRootView = inflater.inflate(R.layout.fragment_directory, container, false);
 
-        mProgressBar = view.findViewById(R.id.progressbar);
+        mProgressBar = mRootView.findViewById(R.id.progressbar);
         assert mProgressBar != null;
 
-        mRecView = (RecyclerView) view.findViewById(R.id.dir_list);
+        mRecView = (RecyclerView) mRootView.findViewById(R.id.dir_list);
         mRecView.setRecyclerListener(
                 new RecyclerListener() {
                     @Override
@@ -362,7 +363,7 @@
                     }
                 });
 
-        mRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refresh_layout);
+        mRefreshLayout = (SwipeRefreshLayout) mRootView.findViewById(R.id.refresh_layout);
         mRefreshLayout.setOnRefreshListener(this);
         mRecView.setItemAnimator(new DirectoryItemAnimator());
 
@@ -398,7 +399,7 @@
 
         setPreDrawListenerEnabled(true);
 
-        return view;
+        return mRootView;
     }
 
     @Override
@@ -684,6 +685,9 @@
 
     public void onViewModeChanged() {
         // Mode change is just visual change; no need to kick loader.
+        mRootView.announceForAccessibility(getString(
+                mState.derivedMode == State.MODE_GRID ? R.string.grid_mode_showing
+                        : R.string.list_mode_showing));
         onDisplayStateChanged();
     }
 
diff --git a/src/com/android/documentsui/queries/SearchChipViewManager.java b/src/com/android/documentsui/queries/SearchChipViewManager.java
index ebda22b..2305da4 100644
--- a/src/com/android/documentsui/queries/SearchChipViewManager.java
+++ b/src/com/android/documentsui/queries/SearchChipViewManager.java
@@ -24,6 +24,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.HorizontalScrollView;
 
 import androidx.annotation.NonNull;
@@ -35,6 +36,7 @@
 import com.android.documentsui.R;
 import com.android.documentsui.base.MimeTypes;
 import com.android.documentsui.base.Shared;
+import com.android.documentsui.util.VersionUtils;
 
 import com.google.android.material.chip.Chip;
 import com.google.common.primitives.Ints;
@@ -96,9 +98,11 @@
     static {
         sMimeTypesChipItems.put(TYPE_IMAGES,
                 new SearchChipData(TYPE_IMAGES, R.string.chip_title_images, IMAGES_MIMETYPES));
-        sMimeTypesChipItems.put(TYPE_DOCUMENTS,
-                new SearchChipData(TYPE_DOCUMENTS, R.string.chip_title_documents,
-                        DOCUMENTS_MIMETYPES));
+        if (VersionUtils.isAtLeastR()) {
+            sMimeTypesChipItems.put(TYPE_DOCUMENTS,
+                    new SearchChipData(TYPE_DOCUMENTS, R.string.chip_title_documents,
+                            DOCUMENTS_MIMETYPES));
+        }
         sMimeTypesChipItems.put(TYPE_AUDIO,
                 new SearchChipData(TYPE_AUDIO, R.string.chip_title_audio, AUDIO_MIMETYPES));
         sMimeTypesChipItems.put(TYPE_VIDEOS,
@@ -465,6 +469,10 @@
             if (parent instanceof HorizontalScrollView) {
                 final int scrollToX = isRtl ? parent.getWidth() : 0;
                 ((HorizontalScrollView) parent).smoothScrollTo(scrollToX, 0);
+                if (mChipGroup.getChildCount() > 0) {
+                    mChipGroup.getChildAt(0)
+                            .sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+                }
             }
         }
     }
diff --git a/src/com/android/documentsui/services/CompressJob.java b/src/com/android/documentsui/services/CompressJob.java
index 1f2ea44..e9ba6e4 100644
--- a/src/com/android/documentsui/services/CompressJob.java
+++ b/src/com/android/documentsui/services/CompressJob.java
@@ -16,6 +16,8 @@
 
 package com.android.documentsui.services;
 
+import static android.content.ContentResolver.wrap;
+
 import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE;
 
 import android.app.Notification;
@@ -45,6 +47,8 @@
     private static final String TAG = "CompressJob";
     private static final String NEW_ARCHIVE_EXTENSION = ".zip";
 
+    private Uri mArchiveUri;
+
     /**
      * Moves files to a destination identified by {@code destination}.
      * Performs most work by delegating to CopyJob, then deleting
@@ -99,17 +103,16 @@
             displayName = service.getString(R.string.new_archive_file_name, NEW_ARCHIVE_EXTENSION);
         }
 
-        Uri archiveUri;
         try {
-            archiveUri = DocumentsContract.createDocument(
-                resolver, mDstInfo.derivedUri, "application/zip", displayName);
+            mArchiveUri = DocumentsContract.createDocument(
+                    resolver, mDstInfo.derivedUri, "application/zip", displayName);
         } catch (Exception e) {
-            archiveUri = null;
+            mArchiveUri = null;
         }
 
         try {
             mDstInfo = DocumentInfo.fromUri(resolver, ArchivesProvider.buildUriForArchive(
-                    archiveUri, ParcelFileDescriptor.MODE_WRITE_ONLY), UserId.DEFAULT_USER);
+                    mArchiveUri, ParcelFileDescriptor.MODE_WRITE_ONLY), UserId.DEFAULT_USER);
             ArchivesProvider.acquireArchive(getClient(mDstInfo), mDstInfo.derivedUri);
         } catch (FileNotFoundException e) {
             Log.e(TAG, "Failed to create dstInfo.", e);
@@ -132,7 +135,14 @@
             Log.e(TAG, "Failed to release the archive.");
         }
 
-        // TODO: Remove the archive file in case of an error.
+        // Remove the archive file in case of an error.
+        try {
+            if (!isFinished() || isCanceled()) {
+                DocumentsContract.deleteDocument(wrap(getClient(mArchiveUri)), mArchiveUri);
+            }
+        } catch (RemoteException | FileNotFoundException e) {
+            Log.w(TAG, "Failed to cleanup after compress error: " + mDstInfo.toString(), e);
+        }
 
         super.finish();
     }
diff --git a/tests/unit/com/android/documentsui/queries/SearchChipViewManagerTest.java b/tests/unit/com/android/documentsui/queries/SearchChipViewManagerTest.java
index 9dbef02..da83c38 100644
--- a/tests/unit/com/android/documentsui/queries/SearchChipViewManagerTest.java
+++ b/tests/unit/com/android/documentsui/queries/SearchChipViewManagerTest.java
@@ -34,6 +34,7 @@
 import com.android.documentsui.R;
 import com.android.documentsui.base.MimeTypes;
 import com.android.documentsui.base.Shared;
+import com.android.documentsui.util.VersionUtils;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -49,6 +50,8 @@
 
     private static final String LARGE_FILES_CHIP_MIME_TYPE = "";
     private static final String FROM_THIS_WEEK_CHIP_MIME_TYPE = "";
+    private static final String[] TEST_MIME_TYPES_INCLUDING_DOCUMENT =
+            new String[]{"image/*", "video/*", "text/*"};
     private static final String[] TEST_MIME_TYPES =
             new String[]{"image/*", "video/*"};
     private static final String[] TEST_OTHER_TYPES =
@@ -88,6 +91,18 @@
     }
 
     @Test
+    public void testUpdateChips_documentsFilterOnlyAvailableAboveR() throws Exception {
+        mSearchChipViewManager.updateChips(TEST_MIME_TYPES_INCLUDING_DOCUMENT);
+
+        int totalChipLength = TEST_MIME_TYPES_INCLUDING_DOCUMENT.length + TEST_OTHER_TYPES.length;
+        if (VersionUtils.isAtLeastR()) {
+            assertThat(mChipGroup.getChildCount()).isEqualTo(totalChipLength);
+        } else {
+            assertThat(mChipGroup.getChildCount()).isEqualTo(totalChipLength - 1);
+        }
+    }
+
+    @Test
     public void testUpdateChips_withSingleMimeType_hasCorrectChipCount() throws Exception {
         mSearchChipViewManager.updateChips(new String[]{"image/*"});