Merge "Follow up changes from ag/1190582." into nyc-andromeda-dev
diff --git a/res/layout/item_root.xml b/res/layout/item_root.xml
index 9671813..114fd09 100644
--- a/res/layout/item_root.xml
+++ b/res/layout/item_root.xml
@@ -77,7 +77,7 @@
         android:duplicateParentState="true">
 
         <ImageView
-            android:id="@+id/unmount_icon"
+            android:id="@+id/eject_icon"
             android:layout_width="@dimen/root_icon_size"
             android:layout_height="match_parent"
             android:scaleType="centerInside"
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index f40e771..1622f64 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -103,7 +103,6 @@
 
     abstract void onTaskFinished(Uri... uris);
     abstract void refreshDirectory(int anim);
-    abstract void openRootSettings(RootInfo root);
     /** Allows sub-classes to include information in a newly created State instance. */
     abstract void includeState(State initialState);
 
diff --git a/src/com/android/documentsui/CheckedTask.java b/src/com/android/documentsui/CheckedTask.java
index ae15902..ecd57cc 100644
--- a/src/com/android/documentsui/CheckedTask.java
+++ b/src/com/android/documentsui/CheckedTask.java
@@ -34,7 +34,7 @@
 abstract class CheckedTask<Input, Output>
         extends AsyncTask<Input, Void, Output> {
 
-    private Check mCheck ;
+    private Check mCheck;
 
     public CheckedTask(Check check) {
         mCheck = check;
diff --git a/src/com/android/documentsui/DocumentsActivity.java b/src/com/android/documentsui/DocumentsActivity.java
index 8041a1b..05f36e8 100644
--- a/src/com/android/documentsui/DocumentsActivity.java
+++ b/src/com/android/documentsui/DocumentsActivity.java
@@ -97,7 +97,7 @@
                    mState.action == ACTION_CREATE ||
                    mState.action == ACTION_OPEN_TREE ||
                    mState.action == ACTION_PICK_COPY_DESTINATION) {
-            RootsFragment.show(getFragmentManager(), null);
+            RootsFragment.show(getFragmentManager(), (Intent) null);
         }
 
         if (mState.restored) {
@@ -233,11 +233,6 @@
     }
 
     @Override
-    void openRootSettings(RootInfo root) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
     void refreshDirectory(int anim) {
         final FragmentManager fm = getFragmentManager();
         final RootInfo root = getCurrentRoot();
diff --git a/src/com/android/documentsui/EjectRootTask.java b/src/com/android/documentsui/EjectRootTask.java
index fcee472..e47a262 100644
--- a/src/com/android/documentsui/EjectRootTask.java
+++ b/src/com/android/documentsui/EjectRootTask.java
@@ -23,25 +23,30 @@
 import android.provider.DocumentsContract;
 import android.util.Log;
 
+import java.util.function.BooleanSupplier;
 import java.util.function.Consumer;
 
-final class EjectRootTask
-        extends CheckedTask<Void, Boolean> {
+final class EjectRootTask extends CheckedTask<Void, Boolean> {
     private final String mAuthority;
     private final String mRootId;
-    private final Consumer<Boolean> mListener;
+    private final Consumer<Boolean> mCallback;
     private Context mContext;
 
-    public EjectRootTask(Check check,
+    /**
+     * @param ejectCanceledCheck The method reference we use to see whether eject should be stopped
+     * at any point
+     * @param finishCallback The end callback necessary when the eject task finishes
+     */
+    public EjectRootTask(Context context,
             String authority,
             String rootId,
-            Context context,
-            Consumer<Boolean> listener) {
-        super(check);
+            BooleanSupplier ejectCanceledCheck,
+            Consumer<Boolean> finishCallback) {
+        super(ejectCanceledCheck::getAsBoolean);
         mAuthority = authority;
         mRootId = rootId;
         mContext = context;
-        mListener = listener;
+        mCallback = finishCallback;
     }
 
     @Override
@@ -65,6 +70,6 @@
 
     @Override
     protected void finish(Boolean ejected) {
-        mListener.accept(ejected);
+        mCallback.accept(ejected);
     }
 }
\ No newline at end of file
diff --git a/src/com/android/documentsui/FilesActivity.java b/src/com/android/documentsui/FilesActivity.java
index 01dd237..41f5d24 100644
--- a/src/com/android/documentsui/FilesActivity.java
+++ b/src/com/android/documentsui/FilesActivity.java
@@ -91,7 +91,7 @@
             }
         };
 
-        RootsFragment.show(getFragmentManager(), null);
+        RootsFragment.show(getFragmentManager(), this::openRootSettings);
 
         final Intent intent = getIntent();
         final Uri uri = intent.getData();
@@ -241,7 +241,6 @@
         return true;
     }
 
-    @Override
     void openRootSettings(RootInfo root) {
         Metrics.logUserAction(this, Metrics.USER_ACTION_SETTINGS);
         final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS);
diff --git a/src/com/android/documentsui/FilesMenuManager.java b/src/com/android/documentsui/FilesMenuManager.java
index e1da944..a7e4905 100644
--- a/src/com/android/documentsui/FilesMenuManager.java
+++ b/src/com/android/documentsui/FilesMenuManager.java
@@ -16,7 +16,6 @@
 
 package com.android.documentsui;
 
-import android.provider.DocumentsContract.Root;
 import android.view.Menu;
 import android.view.MenuItem;
 
@@ -45,7 +44,7 @@
     @Override
     void updateEject(MenuItem eject, RootInfo root) {
         eject.setVisible(true);
-        eject.setEnabled(((root.flags & Root.FLAG_SUPPORTS_EJECT) > 0) && !root.ejecting);
+        eject.setEnabled(root.supportsEject() && !root.ejecting);
     }
 
     @Override
diff --git a/src/com/android/documentsui/RootsFragment.java b/src/com/android/documentsui/RootsFragment.java
index a33b35b..ac33288 100644
--- a/src/com/android/documentsui/RootsFragment.java
+++ b/src/com/android/documentsui/RootsFragment.java
@@ -84,8 +84,16 @@
     private ListView mList;
     private RootsAdapter mAdapter;
     private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
+    private Consumer<RootInfo> mOpenSettings = (RootInfo) -> {
+        throw new UnsupportedOperationException("Can't open settings.");
+    };
 
-    public static void show(FragmentManager fm, Intent includeApps) {
+    public static void show(FragmentManager fm, Consumer<RootInfo> openSettings) {
+        RootsFragment fragment = show(fm, (Intent) null);
+        fragment.mOpenSettings = openSettings;
+    }
+
+    public static RootsFragment show(FragmentManager fm, Intent includeApps) {
         final Bundle args = new Bundle();
         args.putParcelable(EXTRA_INCLUDE_APPS, includeApps);
 
@@ -95,6 +103,8 @@
         final FragmentTransaction ft = fm.beginTransaction();
         ft.replace(R.id.container_roots, fragment);
         ft.commitAllowingStateLoss();
+
+        return fragment;
     }
 
     public static RootsFragment get(FragmentManager fm) {
@@ -108,8 +118,12 @@
         final View view = inflater.inflate(R.layout.fragment_roots, container, false);
         mList = (ListView) view.findViewById(R.id.roots_list);
         mList.setOnItemClickListener(mItemListener);
-        // For right-clicks, we want to trap the click and not pass it to OnClickListener
-        // For all other clicks, we will pass the events down
+        // ListView does not have right-click specific listeners, so we will have a
+        // GenericMotionListener to listen for it.
+        // Currently, right click is viewed the same as long press, so we will have to quickly
+        // register for context menu when we receive a right click event, and quickly unregister
+        // it afterwards to prevent context menus popping up upon long presses.
+        // All other motion events will then get passed to OnItemClickListener.
         mList.setOnGenericMotionListener(
                 new OnGenericMotionListener() {
             @Override
@@ -270,12 +284,11 @@
         final RootItem rootItem = (RootItem) mAdapter.getItem(adapterMenuInfo.position);
         switch(item.getItemId()) {
             case R.id.menu_eject_root:
-                final View unmountIcon = adapterMenuInfo.targetView.findViewById(R.id.unmount_icon);
-                ejectClicked(unmountIcon, rootItem.root);
+                final View ejectIcon = adapterMenuInfo.targetView.findViewById(R.id.eject_icon);
+                ejectClicked(ejectIcon, rootItem.root);
                 return true;
             case R.id.menu_settings:
-                final RootInfo root = rootItem.root;
-                getBaseActivity().openRootSettings(root);
+                mOpenSettings.accept(rootItem.root);
                 return true;
             default:
                 if (DEBUG) Log.d(TAG, "Unhandled menu item selected: " + item);
@@ -286,6 +299,7 @@
     private static void ejectClicked(View ejectIcon, RootInfo root) {
         assert(ejectIcon != null);
         assert(ejectIcon.getContext() instanceof BaseActivity);
+        assert (!root.ejecting);
         ejectIcon.setEnabled(false);
         root.ejecting = true;
         ejectRoot(
@@ -306,10 +320,10 @@
         BooleanSupplier predicate = () -> {
             return !(ejectIcon.getVisibility() == View.VISIBLE);
         };
-        new EjectRootTask(predicate::getAsBoolean,
+        new EjectRootTask(ejectIcon.getContext(),
                 authority,
                 rootId,
-                ejectIcon.getContext(),
+                predicate,
                 listener).executeOnExecutor(ProviderExecutor.forAuthority(authority));
     }
 
@@ -385,24 +399,24 @@
             final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
             final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
-            final ImageView unmountIcon = (ImageView) convertView.findViewById(R.id.unmount_icon);
+            final ImageView ejectIcon = (ImageView) convertView.findViewById(R.id.eject_icon);
 
             final Context context = convertView.getContext();
             icon.setImageDrawable(root.loadDrawerIcon(context));
             title.setText(root.title);
 
             if (root.supportsEject()) {
-                unmountIcon.setVisibility(View.VISIBLE);
-                unmountIcon.setImageDrawable(root.loadEjectIcon(context));
-                unmountIcon.setOnClickListener(new OnClickListener() {
+                ejectIcon.setVisibility(View.VISIBLE);
+                ejectIcon.setImageDrawable(root.loadEjectIcon(context));
+                ejectIcon.setOnClickListener(new OnClickListener() {
                     @Override
                     public void onClick(View unmountIcon) {
                         RootsFragment.ejectClicked(unmountIcon, root);
                     }
                 });
             } else {
-                unmountIcon.setVisibility(View.GONE);
-                unmountIcon.setOnClickListener(null);
+                ejectIcon.setVisibility(View.GONE);
+                ejectIcon.setOnClickListener(null);
             }
             // Show available space if no summary
             String summaryText = root.summary;
diff --git a/tests/src/com/android/documentsui/FilesMenuManagerTest.java b/tests/src/com/android/documentsui/FilesMenuManagerTest.java
index 76ca2f3..3644abc 100644
--- a/tests/src/com/android/documentsui/FilesMenuManagerTest.java
+++ b/tests/src/com/android/documentsui/FilesMenuManagerTest.java
@@ -266,11 +266,21 @@
     }
 
     @Test
-    public void testRootContextMenu_canEject() {
+    public void testRootContextMenu_eject() {
         testRootInfo.flags = Root.FLAG_SUPPORTS_EJECT;
         FilesMenuManager mgr = new FilesMenuManager(testSearchManager, state);
         mgr.updateRootContextMenu(testMenu, testRootInfo);
 
         eject.assertEnabled();
     }
+
+    @Test
+    public void testRootContextMenu_ejectInProcess() {
+        testRootInfo.flags = Root.FLAG_SUPPORTS_EJECT;
+        testRootInfo.ejecting = true;
+        FilesMenuManager mgr = new FilesMenuManager(testSearchManager, state);
+        mgr.updateRootContextMenu(testMenu, testRootInfo);
+
+        eject.assertDisabled();
+    }
 }