Merge "Removing assertion for UserInputHandler." into nyc-andromeda-dev
diff --git a/src/com/android/documentsui/FilesActivity.java b/src/com/android/documentsui/FilesActivity.java
index 2f7f093..7133115 100644
--- a/src/com/android/documentsui/FilesActivity.java
+++ b/src/com/android/documentsui/FilesActivity.java
@@ -416,8 +416,13 @@
                 return true;
             }
 
-            // Open the Close drawer if it is closed and we're at the top of a root.
-            if (size <= 1) {
+            final Intent intent = getIntent();
+            final boolean launchedExternally = intent != null && intent.getData() != null
+                    && mState.action == State.ACTION_BROWSE;
+
+            // Open the Close drawer if it is closed and we're at the top of a root, but only when
+            // not launched by another app.
+            if (size <= 1 && !launchedExternally) {
                 mDrawer.setOpen(true);
                 // Remember so we don't just close it again if back is pressed again.
                 mDrawerLastFiddled = System.currentTimeMillis();
diff --git a/src/com/android/documentsui/LocalPreferences.java b/src/com/android/documentsui/LocalPreferences.java
index d2e9885..32d0082 100644
--- a/src/com/android/documentsui/LocalPreferences.java
+++ b/src/com/android/documentsui/LocalPreferences.java
@@ -84,6 +84,10 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface PermissionStatus {}
 
+    static void clearPackagePreferences(Context context, String packageName) {
+        clearScopedAccessPreferences(context, packageName);
+    }
+
     /**
      * Methods below are used to keep track of denied user requests on scoped directory access so
      * the dialog is not offered when user checked the 'Do not ask again' box
@@ -108,6 +112,16 @@
       getPrefs(context).edit().putInt(key, status).apply();
     }
 
+    private static void clearScopedAccessPreferences(Context context, String packageName) {
+        final String keySubstring = "|" + packageName + "|";
+        final SharedPreferences prefs = getPrefs(context);
+        for (final String key : prefs.getAll().keySet()) {
+            if (key.contains(keySubstring)) {
+                prefs.edit().remove(key).apply();
+            }
+        }
+    }
+
     private static String getScopedAccessDenialsKey(String packageName, String uuid,
             String directory) {
         final int userId = UserHandle.myUserId();
diff --git a/src/com/android/documentsui/PackageReceiver.java b/src/com/android/documentsui/PackageReceiver.java
index aef63af..b5c7c87 100644
--- a/src/com/android/documentsui/PackageReceiver.java
+++ b/src/com/android/documentsui/PackageReceiver.java
@@ -23,7 +23,7 @@
 import android.net.Uri;
 
 /**
- * Clean up {@link RecentsProvider} when packages are removed.
+ * Clean up {@link RecentsProvider} and {@link LocalPreferences} when packages are removed.
  */
 public class PackageReceiver extends BroadcastReceiver {
     @Override
@@ -31,15 +31,19 @@
         final ContentResolver resolver = context.getContentResolver();
 
         final String action = intent.getAction();
+        final Uri data = intent.getData();
+        final String packageName = data == null ? null : data.getSchemeSpecificPart();
+
         if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
             resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE, null, null);
-
+            if (packageName != null) {
+                LocalPreferences.clearPackagePreferences(context, packageName);
+            }
         } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
-            final Uri data = intent.getData();
-            if (data != null) {
-                final String packageName = data.getSchemeSpecificPart();
+            if (packageName != null) {
                 resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE_PACKAGE,
                         packageName, null);
+                LocalPreferences.clearPackagePreferences(context, packageName);
             }
         }
     }
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 24b62ba..1f96de7 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -147,11 +147,11 @@
     private static final int CACHE_EVICT_LIMIT = 100;
     private static final int REFRESH_SPINNER_DISMISS_DELAY = 500;
 
-    private Model mModel;
+    private final Model mModel = new Model();
+    private final Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
+    private final SelectionModeListener mSelectionModeListener = new SelectionModeListener();
     private MultiSelectManager mSelectionMgr;
-    private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
     private UserInputHandler<InputEvent> mInputHandler;
-    private SelectionModeListener mSelectionModeListener;
     private FocusManager mFocusManager;
 
     private IconHelper mIconHelper;
@@ -303,7 +303,10 @@
                     ? MultiSelectManager.MODE_MULTIPLE
                     : MultiSelectManager.MODE_SINGLE);
 
-        // Make sure this is done after the RecyclerView is set up.
+        mModel.addUpdateListener(mAdapter);
+        mModel.addUpdateListener(mModelUpdateListener);
+
+        // Make sure this is done after the RecyclerView and the Model are set up.
         mFocusManager = new FocusManager(context, mRecView, mModel);
 
         mInputHandler = new UserInputHandler<>(
@@ -344,13 +347,8 @@
             mBandController = new BandController(mRecView, mAdapter, mSelectionMgr);
         }
 
-        mSelectionModeListener = new SelectionModeListener();
         mSelectionMgr.addCallback(mSelectionModeListener);
 
-        mModel = new Model();
-        mModel.addUpdateListener(mAdapter);
-        mModel.addUpdateListener(mModelUpdateListener);
-
         final BaseActivity activity = getBaseActivity();
         mTuner = activity.createFragmentTuner();
         mMenuManager = activity.getMenuManager();
diff --git a/src/com/android/documentsui/dirlist/FocusManager.java b/src/com/android/documentsui/dirlist/FocusManager.java
index 1be2f65..80c933e 100644
--- a/src/com/android/documentsui/dirlist/FocusManager.java
+++ b/src/com/android/documentsui/dirlist/FocusManager.java
@@ -62,6 +62,8 @@
     private int mLastFocusPosition = RecyclerView.NO_POSITION;
 
     public FocusManager(Context context, RecyclerView view, Model model) {
+        assert (view != null);
+        assert (model != null);
         mView = view;
         mAdapter = (DocumentsAdapter) view.getAdapter();
         mLayout = (GridLayoutManager) view.getLayoutManager();
diff --git a/tests/src/com/android/documentsui/KeyboardNavigationUiTest.java b/tests/src/com/android/documentsui/KeyboardNavigationUiTest.java
index 4b589bc..76e8c96 100644
--- a/tests/src/com/android/documentsui/KeyboardNavigationUiTest.java
+++ b/tests/src/com/android/documentsui/KeyboardNavigationUiTest.java
@@ -16,6 +16,8 @@
 
 package com.android.documentsui;
 
+import android.net.Uri;
+import android.os.RemoteException;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
@@ -27,6 +29,17 @@
         super(FilesActivity.class);
     }
 
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        initTestFiles();
+    }
+
+    @Override
+    public void initTestFiles() throws RemoteException {
+        mDocsHelper.createDocument(rootDir0, "image/png", "file1.png");
+    }
+
     // Tests that pressing tab switches focus between the roots and directory listings.
     @Suppress
     public void testKeyboard_tab() throws Exception {
@@ -49,6 +62,19 @@
         }
     }
 
+    public void testKeyboard_tabFocuses() throws Exception {
+        bots.roots.closeDrawer();
+        if (bots.main.inFixedLayout()) {
+            // Tablet devices need to press one more tab since it focuses on root list first
+            bots.keyboard.pressKey(KeyEvent.KEYCODE_TAB);
+        }
+        bots.keyboard.pressKey(KeyEvent.KEYCODE_TAB);
+        bots.directory.assertFirstDocumentHasFocus();
+
+        // This should not cause any exceptions
+        bots.keyboard.pressKey(KeyEvent.KEYCODE_F);
+    }
+
     // Tests that arrow keys do not switch focus away from the roots list.
     public void testKeyboard_arrowsRootsList() throws Exception {
 
diff --git a/tests/src/com/android/documentsui/bots/DirectoryListBot.java b/tests/src/com/android/documentsui/bots/DirectoryListBot.java
index fa1e09c..64d866b 100644
--- a/tests/src/com/android/documentsui/bots/DirectoryListBot.java
+++ b/tests/src/com/android/documentsui/bots/DirectoryListBot.java
@@ -35,6 +35,7 @@
 import android.support.v7.widget.RecyclerView;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.widget.RelativeLayout;
 
 import com.android.documentsui.R;
 
@@ -185,6 +186,18 @@
         return true;
     }
 
+    public void assertFirstDocumentHasFocus() throws UiObjectNotFoundException {
+        final UiSelector docList = new UiSelector().resourceId(
+                "com.android.documentsui:id/container_directory").childSelector(
+                        new UiSelector().resourceId(DIR_LIST_ID));
+
+        // Wait for the first list item to appear
+        UiObject doc = new UiObject(docList.childSelector(new UiSelector()));
+        doc.waitForExists(mTimeout);
+
+        assertTrue(doc.isFocused());
+    }
+
     public UiObject findDocumentsList() {
         return findObject(
                 "com.android.documentsui:id/container_directory",