1761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey/*
2761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey * Copyright (C) 2013 The Android Open Source Project
3761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey *
4761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey * you may not use this file except in compliance with the License.
6761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey * You may obtain a copy of the License at
7761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey *
8761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey *
10761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
11761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey * See the License for the specific language governing permissions and
14761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey * limitations under the License.
15761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey */
16761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey
17d080506e3aa8547605cd4783eb660775d7d2b8eeSteve McKaypackage com.android.documentsui.sidebar;
18761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey
19d9caa6ab53aa784acaf241c0ded3c4ae2d342bf8Steve McKayimport static com.android.documentsui.base.Shared.DEBUG;
2030535bce902104c97bbf70783d684ee673cb7637Steve McKayimport static com.android.documentsui.base.Shared.VERBOSE;
21cc460af3f57dd9ffe58edb5f0e6c45cc80e06e75Steve McKay
227d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tanimport android.annotation.Nullable;
2317f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKayimport android.app.Activity;
24761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkeyimport android.app.Fragment;
25761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkeyimport android.app.FragmentManager;
26761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkeyimport android.app.FragmentTransaction;
278b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkeyimport android.app.LoaderManager.LoaderCallbacks;
28761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkeyimport android.content.Context;
291d890e0daf2f46dc83580280cfa22031dfebc216Jeff Sharkeyimport android.content.Intent;
308b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkeyimport android.content.Loader;
311d890e0daf2f46dc83580280cfa22031dfebc216Jeff Sharkeyimport android.content.pm.PackageManager;
321d890e0daf2f46dc83580280cfa22031dfebc216Jeff Sharkeyimport android.content.pm.ResolveInfo;
33761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkeyimport android.os.Bundle;
34cc460af3f57dd9ffe58edb5f0e6c45cc80e06e75Steve McKayimport android.util.Log;
355668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Linimport android.view.ContextMenu;
36a5588b65d55bc1b8b5ba943f8b660db26a7eac5cGarfield, Tanimport android.view.DragEvent;
37761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkeyimport android.view.LayoutInflater;
385668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Linimport android.view.MenuItem;
395668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Linimport android.view.MotionEvent;
40761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkeyimport android.view.View;
41804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport android.view.View.OnDragListener;
425668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Linimport android.view.View.OnGenericMotionListener;
43761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkeyimport android.view.ViewGroup;
44761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkeyimport android.widget.AdapterView;
455668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Linimport android.widget.AdapterView.AdapterContextMenuInfo;
46761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkeyimport android.widget.AdapterView.OnItemClickListener;
478f1edfe6c6a39b8fec7521694328fe8f31d6496eJeff Sharkeyimport android.widget.AdapterView.OnItemLongClickListener;
48761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkeyimport android.widget.ListView;
49761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey
506d20d190bc576bea1da48f79d868a5f383d3458bSteve McKayimport com.android.documentsui.ActionHandler;
51d080506e3aa8547605cd4783eb660775d7d2b8eeSteve McKayimport com.android.documentsui.BaseActivity;
52d080506e3aa8547605cd4783eb660775d7d2b8eeSteve McKayimport com.android.documentsui.DocumentsApplication;
53d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Linimport com.android.documentsui.DragAndDropHelper;
54b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Linimport com.android.documentsui.DragShadowBuilder;
5592ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKayimport com.android.documentsui.Injector;
5692ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKayimport com.android.documentsui.Injector.Injected;
57d080506e3aa8547605cd4783eb660775d7d2b8eeSteve McKayimport com.android.documentsui.ItemDragListener;
58d080506e3aa8547605cd4783eb660775d7d2b8eeSteve McKayimport com.android.documentsui.R;
59739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKayimport com.android.documentsui.base.BooleanConsumer;
608e9125847b29bbb436108c3bfda50a0018c48796Ben Linimport com.android.documentsui.base.DocumentInfo;
61739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKayimport com.android.documentsui.base.DocumentStack;
62d9caa6ab53aa784acaf241c0ded3c4ae2d342bf8Steve McKayimport com.android.documentsui.base.Events;
63d080506e3aa8547605cd4783eb660775d7d2b8eeSteve McKayimport com.android.documentsui.base.RootInfo;
64d9caa6ab53aa784acaf241c0ded3c4ae2d342bf8Steve McKayimport com.android.documentsui.base.Shared;
65d9caa6ab53aa784acaf241c0ded3c4ae2d342bf8Steve McKayimport com.android.documentsui.base.State;
669bd4099ee27f8302a06fc66980079f29127318f8Jon Mannimport com.android.documentsui.roots.ProvidersCache;
67d9caa6ab53aa784acaf241c0ded3c4ae2d342bf8Steve McKayimport com.android.documentsui.roots.RootsLoader;
68761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey
69fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKayimport java.util.ArrayList;
708b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkeyimport java.util.Collection;
719dd02629d12d05c2e77780dc6b9fcc7639192de6Jeff Sharkeyimport java.util.Collections;
72b6a7f2cd05f8c507dfac684aa4c693088e8c01c7Jeff Sharkeyimport java.util.Comparator;
731d890e0daf2f46dc83580280cfa22031dfebc216Jeff Sharkeyimport java.util.List;
7416d8598bdec0015382519ecece459d8da653dcbcKenny Rootimport java.util.Objects;
75761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey
76761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey/**
77761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey * Display list of known storage backend roots.
78761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey */
79804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanpublic class RootsFragment extends Fragment implements ItemDragListener.DragHost {
80761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey
81cc460af3f57dd9ffe58edb5f0e6c45cc80e06e75Steve McKay    private static final String TAG = "RootsFragment";
82cc460af3f57dd9ffe58edb5f0e6c45cc80e06e75Steve McKay    private static final String EXTRA_INCLUDE_APPS = "includeApps";
838e9125847b29bbb436108c3bfda50a0018c48796Ben Lin    private static final int CONTEXT_MENU_ITEM_TIMEOUT = 500;
84cc460af3f57dd9ffe58edb5f0e6c45cc80e06e75Steve McKay
857d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan    private final OnItemClickListener mItemListener = new OnItemClickListener() {
867d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        @Override
877d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
887d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            final Item item = mAdapter.getItem(position);
897d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            item.open();
907d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
917d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            getBaseActivity().setRootsDrawerOpen(false);
927d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        }
937d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan    };
947d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
957d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan    private final OnItemLongClickListener mItemLongClickListener = new OnItemLongClickListener() {
967d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        @Override
977d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
987d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            final Item item = mAdapter.getItem(position);
99b285b40dedbde396aea8080c3d09577677bc71bcGarfield Tan            return item.showAppDetails();
100a5588b65d55bc1b8b5ba943f8b660db26a7eac5cGarfield, Tan        }
101a5588b65d55bc1b8b5ba943f8b660db26a7eac5cGarfield, Tan    };
102a5588b65d55bc1b8b5ba943f8b660db26a7eac5cGarfield, Tan
103761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey    private ListView mList;
1049dd02629d12d05c2e77780dc6b9fcc7639192de6Jeff Sharkey    private RootsAdapter mAdapter;
1058b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey    private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
106208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan    private @Nullable OnDragListener mDragListener;
107208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan
10892ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay    @Injected
10992ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay    private Injector<?> mInjector;
1108b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey
11192ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay    @Injected
11292ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay    private ActionHandler mActionHandler;
113208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan
114821727a0fd0fa608a3046db8dad6ea588e16828fBen Lin    public static RootsFragment show(FragmentManager fm, Intent includeApps) {
1151d890e0daf2f46dc83580280cfa22031dfebc216Jeff Sharkey        final Bundle args = new Bundle();
1161d890e0daf2f46dc83580280cfa22031dfebc216Jeff Sharkey        args.putParcelable(EXTRA_INCLUDE_APPS, includeApps);
1171d890e0daf2f46dc83580280cfa22031dfebc216Jeff Sharkey
118761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey        final RootsFragment fragment = new RootsFragment();
1191d890e0daf2f46dc83580280cfa22031dfebc216Jeff Sharkey        fragment.setArguments(args);
120761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey
121761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey        final FragmentTransaction ft = fm.beginTransaction();
122761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey        ft.replace(R.id.container_roots, fragment);
123761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey        ft.commitAllowingStateLoss();
124821727a0fd0fa608a3046db8dad6ea588e16828fBen Lin
125821727a0fd0fa608a3046db8dad6ea588e16828fBen Lin        return fragment;
126761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey    }
127761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey
128761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey    public static RootsFragment get(FragmentManager fm) {
129761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey        return (RootsFragment) fm.findFragmentById(R.id.container_roots);
130761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey    }
131761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey
132761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey    @Override
133761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey    public View onCreateView(
134761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
135761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey
13692ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay        mInjector = getBaseActivity().getInjector();
13792ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay
138761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey        final View view = inflater.inflate(R.layout.fragment_roots, container, false);
13974e5d4173a1cd060b16c663108a1eeabeae25540Ben Kwa        mList = (ListView) view.findViewById(R.id.roots_list);
140761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey        mList.setOnItemClickListener(mItemListener);
141821727a0fd0fa608a3046db8dad6ea588e16828fBen Lin        // ListView does not have right-click specific listeners, so we will have a
142821727a0fd0fa608a3046db8dad6ea588e16828fBen Lin        // GenericMotionListener to listen for it.
143821727a0fd0fa608a3046db8dad6ea588e16828fBen Lin        // Currently, right click is viewed the same as long press, so we will have to quickly
144821727a0fd0fa608a3046db8dad6ea588e16828fBen Lin        // register for context menu when we receive a right click event, and quickly unregister
145821727a0fd0fa608a3046db8dad6ea588e16828fBen Lin        // it afterwards to prevent context menus popping up upon long presses.
146821727a0fd0fa608a3046db8dad6ea588e16828fBen Lin        // All other motion events will then get passed to OnItemClickListener.
1475668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin        mList.setOnGenericMotionListener(
1485668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin                new OnGenericMotionListener() {
1498e9125847b29bbb436108c3bfda50a0018c48796Ben Lin                    @Override
1508e9125847b29bbb436108c3bfda50a0018c48796Ben Lin                    public boolean onGenericMotion(View v, MotionEvent event) {
1518e9125847b29bbb436108c3bfda50a0018c48796Ben Lin                        if (Events.isMouseEvent(event)
1528e9125847b29bbb436108c3bfda50a0018c48796Ben Lin                                && event.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
1538e9125847b29bbb436108c3bfda50a0018c48796Ben Lin                            int x = (int) event.getX();
1548e9125847b29bbb436108c3bfda50a0018c48796Ben Lin                            int y = (int) event.getY();
1558e9125847b29bbb436108c3bfda50a0018c48796Ben Lin                            return onRightClick(v, x, y, () -> {
15692ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay                                mInjector.menuManager.showContextMenu(
15792ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay                                        RootsFragment.this, v, x, y);
1588e9125847b29bbb436108c3bfda50a0018c48796Ben Lin                            });
1598e9125847b29bbb436108c3bfda50a0018c48796Ben Lin                        }
1608e9125847b29bbb436108c3bfda50a0018c48796Ben Lin                        return false;
1615668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin            }
1625668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin        });
16342d267920811d75bda7fad2d2064b5ae48240f83Jeff Sharkey        mList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
164761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey        return view;
165761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey    }
166761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey
1678e9125847b29bbb436108c3bfda50a0018c48796Ben Lin    private boolean onRightClick(View v, int x, int y, Runnable callback) {
168d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        final int pos = mList.pointToPosition(x, y);
1698e9125847b29bbb436108c3bfda50a0018c48796Ben Lin        final Item item = mAdapter.getItem(pos);
170d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin
171d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        // If a read-only root, no need to see if top level is writable (it's not)
172d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        if (!(item instanceof RootItem) || !((RootItem) item).root.supportsCreate()) {
1738e9125847b29bbb436108c3bfda50a0018c48796Ben Lin            return false;
1748e9125847b29bbb436108c3bfda50a0018c48796Ben Lin        }
1758e9125847b29bbb436108c3bfda50a0018c48796Ben Lin
176d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        final RootItem rootItem = (RootItem) item;
177d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        getRootDocument(rootItem, (DocumentInfo doc) -> {
178d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin            rootItem.docInfo = doc;
1798e9125847b29bbb436108c3bfda50a0018c48796Ben Lin            callback.run();
180d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        });
1818e9125847b29bbb436108c3bfda50a0018c48796Ben Lin        return true;
1828e9125847b29bbb436108c3bfda50a0018c48796Ben Lin    }
1838e9125847b29bbb436108c3bfda50a0018c48796Ben Lin
184c6cbdf11d976ff5845d12c46fb2d601ca7fb0ab2Jeff Sharkey    @Override
1858b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey    public void onActivityCreated(Bundle savedInstanceState) {
1868b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey        super.onActivityCreated(savedInstanceState);
1878b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey
1887d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        final BaseActivity activity = getBaseActivity();
1899bd4099ee27f8302a06fc66980079f29127318f8Jon Mann        final ProvidersCache providers = DocumentsApplication.getProvidersCache(activity);
1907d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        final State state = activity.getDisplayState();
1917d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
19292ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay        mActionHandler = mInjector.actions;
193208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan
19492ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay        if (mInjector.config.dragAndDropEnabled()) {
195208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan            mDragListener = new ItemDragListener<RootsFragment>(this) {
196208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan                @Override
197208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan                public boolean handleDropEventChecked(View v, DragEvent event) {
198208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan                    final int position = (Integer) v.getTag(R.id.item_position_tag);
199208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan                    final Item item = mAdapter.getItem(position);
200208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan
201208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan                    assert (item.isDropTarget());
202208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan
203174fc2e99583c863287eb1f3bcaefd66565f4392Ben Lin                    return item.dropOn(event);
204208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan                }
205208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan            };
206208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan        }
207208945c4e262868d3ebe0f55f1b895cd5a201cbbGarfield Tan
2088b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey        mCallbacks = new LoaderCallbacks<Collection<RootInfo>>() {
2098b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey            @Override
2108b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey            public Loader<Collection<RootInfo>> onCreateLoader(int id, Bundle args) {
2119bd4099ee27f8302a06fc66980079f29127318f8Jon Mann                return new RootsLoader(activity, providers, state);
2128b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey            }
2138b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey
2148b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey            @Override
2158b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey            public void onLoadFinished(
2168b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey                    Loader<Collection<RootInfo>> loader, Collection<RootInfo> result) {
217323dffb4cffde781d325ddd3a36da9ed7b5bfec6Steve McKay                if (!isAdded()) {
218323dffb4cffde781d325ddd3a36da9ed7b5bfec6Steve McKay                    return;
219323dffb4cffde781d325ddd3a36da9ed7b5bfec6Steve McKay                }
2208b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey
221323dffb4cffde781d325ddd3a36da9ed7b5bfec6Steve McKay                Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
2228b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey
2237d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan                List<Item> sortedItems = sortLoadResult(result, handlerAppIntent);
2247d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan                mAdapter = new RootsAdapter(activity, sortedItems, mDragListener);
2258b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey                mList.setAdapter(mAdapter);
2268b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey
2278b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey                onCurrentRootChanged();
2288b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey            }
2298b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey
2308b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey            @Override
2318b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey            public void onLoaderReset(Loader<Collection<RootInfo>> loader) {
2328b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey                mAdapter = null;
2338b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey                mList.setAdapter(null);
2348b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey            }
2358b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey        };
2368b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey    }
2378b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey
2387d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan    /**
2397d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan     * @param handlerAppIntent When not null, apps capable of handling the original intent will
2407d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan     *            be included in list of roots (in special section at bottom).
2417d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan     */
2427d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan    private List<Item> sortLoadResult(
2437d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            Collection<RootInfo> roots, @Nullable Intent handlerAppIntent) {
2447d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        final List<Item> result = new ArrayList<>();
2457d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
2467d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        final List<RootItem> libraries = new ArrayList<>();
2477d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        final List<RootItem> others = new ArrayList<>();
2487d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
2497d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        for (final RootInfo root : roots) {
2507d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            final RootItem item = new RootItem(root, mActionHandler);
2517d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
2527d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            Activity activity = getActivity();
253a0b52655668ccbe3f12ca13ced4b34118a483783Steve McKay            if (root.isHome() && !Shared.shouldShowDocumentsRoot(activity)) {
2547d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan                continue;
2557d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            } else if (root.isLibrary()) {
2567d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan                libraries.add(item);
2577d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            } else {
2587d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan                others.add(item);
2597d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            }
2607d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        }
2617d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
2627d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        final RootComparator comp = new RootComparator();
2637d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        Collections.sort(libraries, comp);
2647d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        Collections.sort(others, comp);
2657d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
26630535bce902104c97bbf70783d684ee673cb7637Steve McKay        if (VERBOSE) Log.v(TAG, "Adding library roots: " + libraries);
2677d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        result.addAll(libraries);
2687d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        // Only add the spacer if it is actually separating something.
2697d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        if (!libraries.isEmpty() && !others.isEmpty()) {
2707d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            result.add(new SpacerItem());
2717d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        }
2727d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
27330535bce902104c97bbf70783d684ee673cb7637Steve McKay        if (VERBOSE) Log.v(TAG, "Adding plain roots: " + libraries);
2747d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        result.addAll(others);
2757d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
2767d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        // Include apps that can handle this intent too.
2777d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        if (handlerAppIntent != null) {
2787d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            includeHandlerApps(handlerAppIntent, result);
2797d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        }
2807d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
2817d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        return result;
2827d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan    }
2837d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
2847d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan    /**
2857d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan     * Adds apps capable of handling the original intent will be included in list of roots (in
2867d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan     * special section at bottom).
2877d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan     */
2887d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan    private void includeHandlerApps(Intent handlerAppIntent, List<Item> result) {
28930535bce902104c97bbf70783d684ee673cb7637Steve McKay        if (VERBOSE) Log.v(TAG, "Adding handler apps for intent: " + handlerAppIntent);
2907d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        Context context = getContext();
2917d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        final PackageManager pm = context.getPackageManager();
2927d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        final List<ResolveInfo> infos = pm.queryIntentActivities(
2937d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan                handlerAppIntent, PackageManager.MATCH_DEFAULT_ONLY);
2947d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
2957d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        final List<AppItem> apps = new ArrayList<>();
2967d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
2977d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        // Omit ourselves from the list
2987d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        for (ResolveInfo info : infos) {
2997d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            if (!context.getPackageName().equals(info.activityInfo.packageName)) {
3007d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan                apps.add(new AppItem(info, mActionHandler));
3017d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            }
3027d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        }
3037d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
3047d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        if (apps.size() > 0) {
3057d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            result.add(new SpacerItem());
3067d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan            result.addAll(apps);
3077d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        }
3087d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan    }
3097d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
3108b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey    @Override
3117d58fc6e8875ccc3dbbb34bcd379a0f9c9ee481eJeff Sharkey    public void onResume() {
3127d58fc6e8875ccc3dbbb34bcd379a0f9c9ee481eJeff Sharkey        super.onResume();
313e8d13ea5fbd07e9a9c0d2867a0eb3e6d761815cfJeff Sharkey        onDisplayStateChanged();
314e8d13ea5fbd07e9a9c0d2867a0eb3e6d761815cfJeff Sharkey    }
315c6cbdf11d976ff5845d12c46fb2d601ca7fb0ab2Jeff Sharkey
316e8d13ea5fbd07e9a9c0d2867a0eb3e6d761815cfJeff Sharkey    public void onDisplayStateChanged() {
317c6cbdf11d976ff5845d12c46fb2d601ca7fb0ab2Jeff Sharkey        final Context context = getActivity();
318d0a2a2c4e061da0de1b0008b841cacd9fc4416edSteve McKay        final State state = ((BaseActivity) context).getDisplayState();
3191c903ccb39bc3f96452b41cad91fb6efc053b664Jeff Sharkey
320d0a2a2c4e061da0de1b0008b841cacd9fc4416edSteve McKay        if (state.action == State.ACTION_GET_CONTENT) {
3218f1edfe6c6a39b8fec7521694328fe8f31d6496eJeff Sharkey            mList.setOnItemLongClickListener(mItemLongClickListener);
3228f1edfe6c6a39b8fec7521694328fe8f31d6496eJeff Sharkey        } else {
3238f1edfe6c6a39b8fec7521694328fe8f31d6496eJeff Sharkey            mList.setOnItemLongClickListener(null);
3248f1edfe6c6a39b8fec7521694328fe8f31d6496eJeff Sharkey            mList.setLongClickable(false);
3258f1edfe6c6a39b8fec7521694328fe8f31d6496eJeff Sharkey        }
3268f1edfe6c6a39b8fec7521694328fe8f31d6496eJeff Sharkey
3278b9970489d03ee0bda3d8d4773bf672c182922c6Jeff Sharkey        getLoaderManager().restartLoader(2, null, mCallbacks);
32842d267920811d75bda7fad2d2064b5ae48240f83Jeff Sharkey    }
32942d267920811d75bda7fad2d2064b5ae48240f83Jeff Sharkey
33042d267920811d75bda7fad2d2064b5ae48240f83Jeff Sharkey    public void onCurrentRootChanged() {
331323dffb4cffde781d325ddd3a36da9ed7b5bfec6Steve McKay        if (mAdapter == null) {
332323dffb4cffde781d325ddd3a36da9ed7b5bfec6Steve McKay            return;
333323dffb4cffde781d325ddd3a36da9ed7b5bfec6Steve McKay        }
33442d267920811d75bda7fad2d2064b5ae48240f83Jeff Sharkey
335d0a2a2c4e061da0de1b0008b841cacd9fc4416edSteve McKay        final RootInfo root = ((BaseActivity) getActivity()).getCurrentRoot();
33642d267920811d75bda7fad2d2064b5ae48240f83Jeff Sharkey        for (int i = 0; i < mAdapter.getCount(); i++) {
33742d267920811d75bda7fad2d2064b5ae48240f83Jeff Sharkey            final Object item = mAdapter.getItem(i);
33825f10b31243ed6f8de5f32ba2cb2dd0d6de911f5Jeff Sharkey            if (item instanceof RootItem) {
33925f10b31243ed6f8de5f32ba2cb2dd0d6de911f5Jeff Sharkey                final RootInfo testRoot = ((RootItem) item).root;
34016d8598bdec0015382519ecece459d8da653dcbcKenny Root                if (Objects.equals(testRoot, root)) {
34125f10b31243ed6f8de5f32ba2cb2dd0d6de911f5Jeff Sharkey                    mList.setItemChecked(i, true);
34225f10b31243ed6f8de5f32ba2cb2dd0d6de911f5Jeff Sharkey                    return;
34325f10b31243ed6f8de5f32ba2cb2dd0d6de911f5Jeff Sharkey                }
34442d267920811d75bda7fad2d2064b5ae48240f83Jeff Sharkey            }
34542d267920811d75bda7fad2d2064b5ae48240f83Jeff Sharkey        }
346c6cbdf11d976ff5845d12c46fb2d601ca7fb0ab2Jeff Sharkey    }
347c6cbdf11d976ff5845d12c46fb2d601ca7fb0ab2Jeff Sharkey
34874e5d4173a1cd060b16c663108a1eeabeae25540Ben Kwa    /**
34974e5d4173a1cd060b16c663108a1eeabeae25540Ben Kwa     * Attempts to shift focus back to the navigation drawer.
35074e5d4173a1cd060b16c663108a1eeabeae25540Ben Kwa     */
351d947f0192142c7db40d7dfaa8d0c6caaa1cf7c36Ben Lin    public boolean requestFocus() {
352d947f0192142c7db40d7dfaa8d0c6caaa1cf7c36Ben Lin        return mList.requestFocus();
35374e5d4173a1cd060b16c663108a1eeabeae25540Ben Kwa    }
35474e5d4173a1cd060b16c663108a1eeabeae25540Ben Kwa
35592ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay    private BaseActivity getBaseActivity() {
35692ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay        return (BaseActivity) getActivity();
3575668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin    }
3585668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin
359804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    @Override
360804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    public void runOnUiThread(Runnable runnable) {
361804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        getActivity().runOnUiThread(runnable);
362804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
363804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
364d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    // In RootsFragment, we check whether the item corresponds to a RootItem, and whether
365d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    // the currently dragged objects can be droppable or not, and change the drop-shadow
366d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    // accordingly
3677f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin    @Override
3681c45629929c31cf7903cf955a48f170a8cdee255Ben Lin    public void onDragEntered(View v, Object localState) {
369d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        final int pos = (Integer) v.getTag(R.id.item_position_tag);
370d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        final Item item = mAdapter.getItem(pos);
371d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin
372d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        // If a read-only root, no need to see if top level is writable (it's not)
373d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        if (!(item instanceof RootItem) || !((RootItem) item).root.supportsCreate()) {
374d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin            getBaseActivity().getShadowBuilder().setAppearDroppable(false);
375d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin            v.updateDragShadow(getBaseActivity().getShadowBuilder());
376d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin            return;
377d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        }
378d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin
379d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        final RootItem rootItem = (RootItem) item;
380d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        getRootDocument(rootItem, (DocumentInfo doc) -> {
381b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin            updateDropShadow(v, localState, rootItem, doc);
382d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        });
3837f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin    }
3847f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin
385b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin    private void updateDropShadow(
386b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin            View v, Object localState, RootItem rootItem, DocumentInfo rootDoc) {
387b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin        final DragShadowBuilder shadowBuilder = getBaseActivity().getShadowBuilder();
388b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin        if (rootDoc == null) {
389b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin            Log.e(TAG, "Root DocumentInfo is null. Defaulting to appear not droppable.");
390b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin            shadowBuilder.setAppearDroppable(false);
391b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin        } else {
392b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin            rootItem.docInfo = rootDoc;
393b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin            shadowBuilder.setAppearDroppable(rootDoc.isCreateSupported()
394b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin                    && DragAndDropHelper.canCopyTo(localState, rootDoc));
395b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin        }
396b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin        v.updateDragShadow(shadowBuilder);
397b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin    }
398b0761d3b04ed16800bc96a32a759fd6374d1e20fBen Lin
399d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    // In RootsFragment we always reset the drag shadow as it exits a RootItemView.
400d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    @Override
401d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    public void onDragExited(View v, Object localState) {
402d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        getBaseActivity().getShadowBuilder().resetBackground();
403d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        v.updateDragShadow(getBaseActivity().getShadowBuilder());
404d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    }
405d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin
406d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    // In RootsFragment we open the hovered root.
407804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    @Override
408a5588b65d55bc1b8b5ba943f8b660db26a7eac5cGarfield, Tan    public void onViewHovered(View v) {
40957facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan        // SpacerView doesn't have DragListener so this view is guaranteed to be a RootItemView.
410a5588b65d55bc1b8b5ba943f8b660db26a7eac5cGarfield, Tan        RootItemView itemView = (RootItemView) v;
41157facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan        itemView.drawRipple();
41257facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan
413a5588b65d55bc1b8b5ba943f8b660db26a7eac5cGarfield, Tan        final int position = (Integer) v.getTag(R.id.item_position_tag);
4147d66a862d5f4c96e339f8f6dbfc0406ed16ee9b6Garfield, Tan        final Item item = mAdapter.getItem(position);
4157d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        item.open();
416804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
417804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
418804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    @Override
419166c5c6fc17461cb52f2b83eaec86cfc15b53004Ben Lin    public void setDropTargetHighlight(View v, Object localState, boolean highlight) {
42057facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan        // SpacerView doesn't have DragListener so this view is guaranteed to be a RootItemView.
42157facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan        RootItemView itemView = (RootItemView) v;
42257facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan        itemView.setHighlight(highlight);
423804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
424804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
4255668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin    @Override
4265668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin    public void onCreateContextMenu(
4275668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin            ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
4285668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin        super.onCreateContextMenu(menu, v, menuInfo);
4295668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin        AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
4305668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin        final Item item = mAdapter.getItem(adapterMenuInfo.position);
4317d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan
43292ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay        BaseActivity activity = getBaseActivity();
43392ae43d5d22331aad83e1a4302a7e1975f66354eSteve McKay        item.createContextMenu(menu, activity.getMenuInflater(), mInjector.menuManager);
4345668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin    }
4355668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin
4365668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin    @Override
4375668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin    public boolean onContextItemSelected(MenuItem item) {
4385668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin        AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
439035f0c8a6f8f2f5515ee30272b5343ec4095f8c0Ben Lin        // There is a possibility that this is called from DirectoryFragment since
440035f0c8a6f8f2f5515ee30272b5343ec4095f8c0Ben Lin        // all fragments' onContextItemSelected gets called when any menu item is selected
441035f0c8a6f8f2f5515ee30272b5343ec4095f8c0Ben Lin        // This is to guard against it since DirectoryFragment's RecylerView does not have a
442035f0c8a6f8f2f5515ee30272b5343ec4095f8c0Ben Lin        // menuInfo
443035f0c8a6f8f2f5515ee30272b5343ec4095f8c0Ben Lin        if (adapterMenuInfo == null) {
444035f0c8a6f8f2f5515ee30272b5343ec4095f8c0Ben Lin            return false;
445035f0c8a6f8f2f5515ee30272b5343ec4095f8c0Ben Lin        }
4465668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin        final RootItem rootItem = (RootItem) mAdapter.getItem(adapterMenuInfo.position);
447035f0c8a6f8f2f5515ee30272b5343ec4095f8c0Ben Lin        switch (item.getItemId()) {
4485668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin            case R.id.menu_eject_root:
449821727a0fd0fa608a3046db8dad6ea588e16828fBen Lin                final View ejectIcon = adapterMenuInfo.targetView.findViewById(R.id.eject_icon);
4507d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan                ejectClicked(ejectIcon, rootItem.root, mActionHandler);
4515668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin                return true;
452b285b40dedbde396aea8080c3d09577677bc71bcGarfield Tan            case R.id.menu_open_in_new_window:
453739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKay                mActionHandler.openInNewWindow(new DocumentStack(rootItem.root));
454b285b40dedbde396aea8080c3d09577677bc71bcGarfield Tan                return true;
455b285b40dedbde396aea8080c3d09577677bc71bcGarfield Tan            case R.id.menu_paste_into_folder:
456b285b40dedbde396aea8080c3d09577677bc71bcGarfield Tan                mActionHandler.pasteIntoFolder(rootItem.root);
457b285b40dedbde396aea8080c3d09577677bc71bcGarfield Tan                return true;
4585668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin            case R.id.menu_settings:
4597d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan                mActionHandler.openSettings(rootItem.root);
4605668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin                return true;
4615668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin            default:
4625668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin                if (DEBUG) Log.d(TAG, "Unhandled menu item selected: " + item);
4635668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin                return false;
4645668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin        }
4655668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin    }
4665668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin
467d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    @FunctionalInterface
468d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    interface RootUpdater {
469d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        void updateDocInfoForRoot(DocumentInfo doc);
470d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    }
471d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin
472d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    private void getRootDocument(RootItem rootItem, RootUpdater updater) {
473d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        // We need to start a GetRootDocumentTask so we can know whether items can be directly
474d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin        // pasted into root
47530b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin        mActionHandler.getRootDocument(
476d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin                rootItem.root,
47730b0dc1896abc67a970b61ebfd420275a31c1e18Ben Lin                CONTEXT_MENU_ITEM_TIMEOUT,
478d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin                (DocumentInfo doc) -> {
479d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin                    updater.updateDocInfoForRoot(doc);
480d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin                });
481d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin    }
482d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin
483739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKay    static void ejectClicked(View ejectIcon, RootInfo root, ActionHandler actionHandler) {
4845668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin        assert(ejectIcon != null);
4857d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        assert(!root.ejecting);
4865668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin        ejectIcon.setEnabled(false);
4875668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin        root.ejecting = true;
4887d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan        actionHandler.ejectRoot(
4897d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan                root,
490739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKay                new BooleanConsumer() {
4915668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin                    @Override
492739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKay                    public void accept(boolean ejected) {
493739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKay                        // Event if ejected is false, we should reset, since the op failed.
494739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKay                        // Either way, we are no longer attempting to eject the device.
4955668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin                        root.ejecting = false;
496739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKay
497739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKay                        // If the view is still visible, we update its state.
498739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKay                        if (ejectIcon.getVisibility() == View.VISIBLE) {
499739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKay                            ejectIcon.setEnabled(!ejected);
500739f94ba0dc9be264d9596dac551fd63082d2fddSteve McKay                        }
5015668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin                    }
5025668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin                });
5035668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin    }
5045668b866d52a4f8907474922dc9b1ba1b4db85a3Ben Lin
5057d75f7bbd1693a33abb8de6b472ec1950e33eb8bGarfield Tan    private static class RootComparator implements Comparator<RootItem> {
506b6a7f2cd05f8c507dfac684aa4c693088e8c01c7Jeff Sharkey        @Override
50785c8cdd2e79e96f719932db64b61d3c51d4aeefcBen Kwa        public int compare(RootItem lhs, RootItem rhs) {
50855c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay            return lhs.root.compareTo(rhs.root);
509b6a7f2cd05f8c507dfac684aa4c693088e8c01c7Jeff Sharkey        }
510b6a7f2cd05f8c507dfac684aa4c693088e8c01c7Jeff Sharkey    }
511761122118b2437aee3a44e9263b73766986c12b8Jeff Sharkey}
512