blob: 299ac578eaf1299f15130c51f2ba0265b0c92f7d [file] [log] [blame]
Jeff Sharkey76112212013-08-06 11:26:10 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Steve McKayd0805062016-09-15 14:30:38 -070017package com.android.documentsui.sidebar;
Jeff Sharkey76112212013-08-06 11:26:10 -070018
Steve McKayd9caa6a2016-09-15 16:36:45 -070019import static com.android.documentsui.base.Shared.DEBUG;
Steve McKay30535bc2016-11-04 14:16:58 -070020import static com.android.documentsui.base.Shared.VERBOSE;
Steve McKaycc460af2015-11-03 14:00:45 -080021
Garfield Tan7d75f7b2016-09-20 16:33:24 -070022import android.annotation.Nullable;
Steve McKay17f7e582016-04-04 15:26:48 -070023import android.app.Activity;
Jeff Sharkey76112212013-08-06 11:26:10 -070024import android.app.Fragment;
25import android.app.FragmentManager;
26import android.app.FragmentTransaction;
Jeff Sharkey8b997042013-09-19 15:25:56 -070027import android.app.LoaderManager.LoaderCallbacks;
Jeff Sharkey76112212013-08-06 11:26:10 -070028import android.content.Context;
Jeff Sharkey1d890e02013-08-15 11:24:03 -070029import android.content.Intent;
Jeff Sharkey8b997042013-09-19 15:25:56 -070030import android.content.Loader;
Jeff Sharkey1d890e02013-08-15 11:24:03 -070031import android.content.pm.PackageManager;
32import android.content.pm.ResolveInfo;
Jeff Sharkey76112212013-08-06 11:26:10 -070033import android.os.Bundle;
Steve McKaycc460af2015-11-03 14:00:45 -080034import android.util.Log;
Ben Lin5668b862016-06-28 17:12:52 -070035import android.view.ContextMenu;
Garfield, Tana5588b62016-07-13 09:23:04 -070036import android.view.DragEvent;
Jeff Sharkey76112212013-08-06 11:26:10 -070037import android.view.LayoutInflater;
Ben Lin5668b862016-06-28 17:12:52 -070038import android.view.MenuItem;
39import android.view.MotionEvent;
Jeff Sharkey76112212013-08-06 11:26:10 -070040import android.view.View;
Garfield, Tan804133e2016-04-20 15:13:56 -070041import android.view.View.OnDragListener;
Ben Lin5668b862016-06-28 17:12:52 -070042import android.view.View.OnGenericMotionListener;
Jeff Sharkey76112212013-08-06 11:26:10 -070043import android.view.ViewGroup;
44import android.widget.AdapterView;
Ben Lin5668b862016-06-28 17:12:52 -070045import android.widget.AdapterView.AdapterContextMenuInfo;
Jeff Sharkey76112212013-08-06 11:26:10 -070046import android.widget.AdapterView.OnItemClickListener;
Jeff Sharkey8f1edfe2013-10-23 16:05:56 -070047import android.widget.AdapterView.OnItemLongClickListener;
Jeff Sharkey76112212013-08-06 11:26:10 -070048import android.widget.ListView;
Jeff Sharkey76112212013-08-06 11:26:10 -070049
Steve McKay6d20d192016-09-21 17:57:10 -070050import com.android.documentsui.ActionHandler;
Steve McKayd0805062016-09-15 14:30:38 -070051import com.android.documentsui.BaseActivity;
52import com.android.documentsui.DocumentsApplication;
Ben Lind0202122016-11-10 18:00:12 -080053import com.android.documentsui.DragAndDropHelper;
Ben Linb0761d32017-02-16 11:04:04 -080054import com.android.documentsui.DragShadowBuilder;
Steve McKay92ae43d2016-11-08 12:06:58 -080055import com.android.documentsui.Injector;
56import com.android.documentsui.Injector.Injected;
Steve McKayd0805062016-09-15 14:30:38 -070057import com.android.documentsui.ItemDragListener;
Steve McKayd0805062016-09-15 14:30:38 -070058import com.android.documentsui.R;
Ben Lin30b0dc12017-03-07 15:37:16 -080059import com.android.documentsui.TimeoutTask;
Steve McKay739f94b2016-09-22 14:54:23 -070060import com.android.documentsui.base.BooleanConsumer;
Ben Lin8e912582016-09-23 14:25:14 -070061import com.android.documentsui.base.DocumentInfo;
Steve McKay739f94b2016-09-22 14:54:23 -070062import com.android.documentsui.base.DocumentStack;
Steve McKayd9caa6a2016-09-15 16:36:45 -070063import com.android.documentsui.base.Events;
Steve McKayd0805062016-09-15 14:30:38 -070064import com.android.documentsui.base.RootInfo;
Steve McKayd9caa6a2016-09-15 16:36:45 -070065import com.android.documentsui.base.Shared;
66import com.android.documentsui.base.State;
Steve McKaydef48682016-10-03 09:07:38 -070067import com.android.documentsui.roots.GetRootDocumentTask;
Steve McKayd9caa6a2016-09-15 16:36:45 -070068import com.android.documentsui.roots.RootsCache;
69import com.android.documentsui.roots.RootsLoader;
Jeff Sharkey76112212013-08-06 11:26:10 -070070
Steve McKayfefcd702015-08-20 16:19:38 +000071import java.util.ArrayList;
Jeff Sharkey8b997042013-09-19 15:25:56 -070072import java.util.Collection;
Jeff Sharkey9dd02622013-09-27 16:44:11 -070073import java.util.Collections;
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -070074import java.util.Comparator;
Jeff Sharkey1d890e02013-08-15 11:24:03 -070075import java.util.List;
Kenny Root16d85982013-12-13 12:00:26 -080076import java.util.Objects;
Jeff Sharkey76112212013-08-06 11:26:10 -070077
78/**
79 * Display list of known storage backend roots.
80 */
Garfield, Tan804133e2016-04-20 15:13:56 -070081public class RootsFragment extends Fragment implements ItemDragListener.DragHost {
Jeff Sharkey76112212013-08-06 11:26:10 -070082
Steve McKaycc460af2015-11-03 14:00:45 -080083 private static final String TAG = "RootsFragment";
84 private static final String EXTRA_INCLUDE_APPS = "includeApps";
Ben Lin8e912582016-09-23 14:25:14 -070085 private static final int CONTEXT_MENU_ITEM_TIMEOUT = 500;
Steve McKaycc460af2015-11-03 14:00:45 -080086
Garfield Tan7d75f7b2016-09-20 16:33:24 -070087 private final OnItemClickListener mItemListener = new OnItemClickListener() {
88 @Override
89 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
90 final Item item = mAdapter.getItem(position);
91 item.open();
92
93 getBaseActivity().setRootsDrawerOpen(false);
94 }
95 };
96
97 private final OnItemLongClickListener mItemLongClickListener = new OnItemLongClickListener() {
98 @Override
99 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
100 final Item item = mAdapter.getItem(position);
Garfield Tanb285b402016-09-21 17:12:18 -0700101 return item.showAppDetails();
Garfield, Tana5588b62016-07-13 09:23:04 -0700102 }
103 };
104
Jeff Sharkey76112212013-08-06 11:26:10 -0700105 private ListView mList;
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700106 private RootsAdapter mAdapter;
Jeff Sharkey8b997042013-09-19 15:25:56 -0700107 private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
Garfield Tan208945c2016-10-04 14:36:38 -0700108 private @Nullable OnDragListener mDragListener;
109
Steve McKay92ae43d2016-11-08 12:06:58 -0800110 @Injected
111 private Injector<?> mInjector;
Ben Lin821727a2016-07-08 10:49:53 -0700112
Steve McKay92ae43d2016-11-08 12:06:58 -0800113 @Injected
114 private ActionHandler mActionHandler;
Garfield Tan208945c2016-10-04 14:36:38 -0700115
Ben Lin821727a2016-07-08 10:49:53 -0700116 public static RootsFragment show(FragmentManager fm, Intent includeApps) {
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700117 final Bundle args = new Bundle();
118 args.putParcelable(EXTRA_INCLUDE_APPS, includeApps);
119
Jeff Sharkey76112212013-08-06 11:26:10 -0700120 final RootsFragment fragment = new RootsFragment();
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700121 fragment.setArguments(args);
Jeff Sharkey76112212013-08-06 11:26:10 -0700122
123 final FragmentTransaction ft = fm.beginTransaction();
124 ft.replace(R.id.container_roots, fragment);
125 ft.commitAllowingStateLoss();
Ben Lin821727a2016-07-08 10:49:53 -0700126
127 return fragment;
Jeff Sharkey76112212013-08-06 11:26:10 -0700128 }
129
130 public static RootsFragment get(FragmentManager fm) {
131 return (RootsFragment) fm.findFragmentById(R.id.container_roots);
132 }
133
134 @Override
135 public View onCreateView(
136 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Jeff Sharkey76112212013-08-06 11:26:10 -0700137
Steve McKay92ae43d2016-11-08 12:06:58 -0800138 mInjector = getBaseActivity().getInjector();
139
Jeff Sharkey76112212013-08-06 11:26:10 -0700140 final View view = inflater.inflate(R.layout.fragment_roots, container, false);
Ben Kwa74e5d412016-02-10 07:46:35 -0800141 mList = (ListView) view.findViewById(R.id.roots_list);
Jeff Sharkey76112212013-08-06 11:26:10 -0700142 mList.setOnItemClickListener(mItemListener);
Ben Lin821727a2016-07-08 10:49:53 -0700143 // ListView does not have right-click specific listeners, so we will have a
144 // GenericMotionListener to listen for it.
145 // Currently, right click is viewed the same as long press, so we will have to quickly
146 // register for context menu when we receive a right click event, and quickly unregister
147 // it afterwards to prevent context menus popping up upon long presses.
148 // All other motion events will then get passed to OnItemClickListener.
Ben Lin5668b862016-06-28 17:12:52 -0700149 mList.setOnGenericMotionListener(
150 new OnGenericMotionListener() {
Ben Lin8e912582016-09-23 14:25:14 -0700151 @Override
152 public boolean onGenericMotion(View v, MotionEvent event) {
153 if (Events.isMouseEvent(event)
154 && event.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
155 int x = (int) event.getX();
156 int y = (int) event.getY();
157 return onRightClick(v, x, y, () -> {
Steve McKay92ae43d2016-11-08 12:06:58 -0800158 mInjector.menuManager.showContextMenu(
159 RootsFragment.this, v, x, y);
Ben Lin8e912582016-09-23 14:25:14 -0700160 });
161 }
162 return false;
Ben Lin5668b862016-06-28 17:12:52 -0700163 }
164 });
Jeff Sharkey42d26792013-09-06 13:22:09 -0700165 mList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Jeff Sharkey76112212013-08-06 11:26:10 -0700166 return view;
167 }
168
Ben Lin8e912582016-09-23 14:25:14 -0700169 private boolean onRightClick(View v, int x, int y, Runnable callback) {
Ben Lind0202122016-11-10 18:00:12 -0800170 final int pos = mList.pointToPosition(x, y);
Ben Lin8e912582016-09-23 14:25:14 -0700171 final Item item = mAdapter.getItem(pos);
Ben Lind0202122016-11-10 18:00:12 -0800172
173 // If a read-only root, no need to see if top level is writable (it's not)
174 if (!(item instanceof RootItem) || !((RootItem) item).root.supportsCreate()) {
Ben Lin8e912582016-09-23 14:25:14 -0700175 return false;
176 }
Ben Lind0202122016-11-10 18:00:12 -0800177
Ben Lin8e912582016-09-23 14:25:14 -0700178 final RootItem rootItem = (RootItem) item;
Ben Lind0202122016-11-10 18:00:12 -0800179 getRootDocument(rootItem, (DocumentInfo doc) -> {
180 rootItem.docInfo = doc;
Ben Lin8e912582016-09-23 14:25:14 -0700181 callback.run();
Ben Lind0202122016-11-10 18:00:12 -0800182 });
Ben Lin8e912582016-09-23 14:25:14 -0700183 return true;
184 }
185
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700186 @Override
Jeff Sharkey8b997042013-09-19 15:25:56 -0700187 public void onActivityCreated(Bundle savedInstanceState) {
188 super.onActivityCreated(savedInstanceState);
189
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700190 final BaseActivity activity = getBaseActivity();
191 final RootsCache roots = DocumentsApplication.getRootsCache(activity);
192 final State state = activity.getDisplayState();
193
Steve McKay92ae43d2016-11-08 12:06:58 -0800194 mActionHandler = mInjector.actions;
Garfield Tan208945c2016-10-04 14:36:38 -0700195
Steve McKay92ae43d2016-11-08 12:06:58 -0800196 if (mInjector.config.dragAndDropEnabled()) {
Garfield Tan208945c2016-10-04 14:36:38 -0700197 mDragListener = new ItemDragListener<RootsFragment>(this) {
198 @Override
199 public boolean handleDropEventChecked(View v, DragEvent event) {
200 final int position = (Integer) v.getTag(R.id.item_position_tag);
201 final Item item = mAdapter.getItem(position);
202
203 assert (item.isDropTarget());
204
Ben Lin174fc2e2017-03-01 17:53:20 -0800205 return item.dropOn(event);
Garfield Tan208945c2016-10-04 14:36:38 -0700206 }
207 };
208 }
209
Jeff Sharkey8b997042013-09-19 15:25:56 -0700210 mCallbacks = new LoaderCallbacks<Collection<RootInfo>>() {
211 @Override
212 public Loader<Collection<RootInfo>> onCreateLoader(int id, Bundle args) {
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700213 return new RootsLoader(activity, roots, state);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700214 }
215
216 @Override
217 public void onLoadFinished(
218 Loader<Collection<RootInfo>> loader, Collection<RootInfo> result) {
Steve McKay323dffb2016-02-17 18:25:47 -0800219 if (!isAdded()) {
220 return;
221 }
Jeff Sharkey8b997042013-09-19 15:25:56 -0700222
Steve McKay323dffb2016-02-17 18:25:47 -0800223 Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700224
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700225 List<Item> sortedItems = sortLoadResult(result, handlerAppIntent);
226 mAdapter = new RootsAdapter(activity, sortedItems, mDragListener);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700227 mList.setAdapter(mAdapter);
228
229 onCurrentRootChanged();
230 }
231
232 @Override
233 public void onLoaderReset(Loader<Collection<RootInfo>> loader) {
234 mAdapter = null;
235 mList.setAdapter(null);
236 }
237 };
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700238 }
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700239
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700240 /**
241 * @param handlerAppIntent When not null, apps capable of handling the original intent will
242 * be included in list of roots (in special section at bottom).
243 */
244 private List<Item> sortLoadResult(
245 Collection<RootInfo> roots, @Nullable Intent handlerAppIntent) {
246 final List<Item> result = new ArrayList<>();
247
248 final List<RootItem> libraries = new ArrayList<>();
249 final List<RootItem> others = new ArrayList<>();
250
251 for (final RootInfo root : roots) {
252 final RootItem item = new RootItem(root, mActionHandler);
253
254 Activity activity = getActivity();
Steve McKaya0b52652016-10-27 12:56:55 -0700255 if (root.isHome() && !Shared.shouldShowDocumentsRoot(activity)) {
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700256 continue;
257 } else if (root.isLibrary()) {
258 libraries.add(item);
259 } else {
260 others.add(item);
261 }
262 }
263
264 final RootComparator comp = new RootComparator();
265 Collections.sort(libraries, comp);
266 Collections.sort(others, comp);
267
Steve McKay30535bc2016-11-04 14:16:58 -0700268 if (VERBOSE) Log.v(TAG, "Adding library roots: " + libraries);
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700269 result.addAll(libraries);
270 // Only add the spacer if it is actually separating something.
271 if (!libraries.isEmpty() && !others.isEmpty()) {
272 result.add(new SpacerItem());
273 }
274
Steve McKay30535bc2016-11-04 14:16:58 -0700275 if (VERBOSE) Log.v(TAG, "Adding plain roots: " + libraries);
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700276 result.addAll(others);
277
278 // Include apps that can handle this intent too.
279 if (handlerAppIntent != null) {
280 includeHandlerApps(handlerAppIntent, result);
281 }
282
283 return result;
284 }
285
286 /**
287 * Adds apps capable of handling the original intent will be included in list of roots (in
288 * special section at bottom).
289 */
290 private void includeHandlerApps(Intent handlerAppIntent, List<Item> result) {
Steve McKay30535bc2016-11-04 14:16:58 -0700291 if (VERBOSE) Log.v(TAG, "Adding handler apps for intent: " + handlerAppIntent);
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700292 Context context = getContext();
293 final PackageManager pm = context.getPackageManager();
294 final List<ResolveInfo> infos = pm.queryIntentActivities(
295 handlerAppIntent, PackageManager.MATCH_DEFAULT_ONLY);
296
297 final List<AppItem> apps = new ArrayList<>();
298
299 // Omit ourselves from the list
300 for (ResolveInfo info : infos) {
301 if (!context.getPackageName().equals(info.activityInfo.packageName)) {
302 apps.add(new AppItem(info, mActionHandler));
303 }
304 }
305
306 if (apps.size() > 0) {
307 result.add(new SpacerItem());
308 result.addAll(apps);
309 }
310 }
311
Jeff Sharkey8b997042013-09-19 15:25:56 -0700312 @Override
313 public void onResume() {
314 super.onResume();
Jeff Sharkeye8d13ea2014-08-08 15:10:03 -0700315 onDisplayStateChanged();
316 }
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700317
Jeff Sharkeye8d13ea2014-08-08 15:10:03 -0700318 public void onDisplayStateChanged() {
Jeff Sharkey8b997042013-09-19 15:25:56 -0700319 final Context context = getActivity();
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700320 final State state = ((BaseActivity) context).getDisplayState();
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700321
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700322 if (state.action == State.ACTION_GET_CONTENT) {
Jeff Sharkey8f1edfe2013-10-23 16:05:56 -0700323 mList.setOnItemLongClickListener(mItemLongClickListener);
324 } else {
325 mList.setOnItemLongClickListener(null);
326 mList.setLongClickable(false);
327 }
328
Jeff Sharkey8b997042013-09-19 15:25:56 -0700329 getLoaderManager().restartLoader(2, null, mCallbacks);
Jeff Sharkey42d26792013-09-06 13:22:09 -0700330 }
331
332 public void onCurrentRootChanged() {
Steve McKay323dffb2016-02-17 18:25:47 -0800333 if (mAdapter == null) {
334 return;
335 }
Jeff Sharkey42d26792013-09-06 13:22:09 -0700336
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700337 final RootInfo root = ((BaseActivity) getActivity()).getCurrentRoot();
Jeff Sharkey42d26792013-09-06 13:22:09 -0700338 for (int i = 0; i < mAdapter.getCount(); i++) {
339 final Object item = mAdapter.getItem(i);
Jeff Sharkey25f10b32013-10-07 14:08:17 -0700340 if (item instanceof RootItem) {
341 final RootInfo testRoot = ((RootItem) item).root;
Kenny Root16d85982013-12-13 12:00:26 -0800342 if (Objects.equals(testRoot, root)) {
Jeff Sharkey25f10b32013-10-07 14:08:17 -0700343 mList.setItemChecked(i, true);
344 return;
345 }
Jeff Sharkey42d26792013-09-06 13:22:09 -0700346 }
347 }
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700348 }
349
Ben Kwa74e5d412016-02-10 07:46:35 -0800350 /**
351 * Attempts to shift focus back to the navigation drawer.
352 */
Ben Lind947f012016-10-18 14:32:49 -0700353 public boolean requestFocus() {
354 return mList.requestFocus();
Ben Kwa74e5d412016-02-10 07:46:35 -0800355 }
356
Steve McKay92ae43d2016-11-08 12:06:58 -0800357 private BaseActivity getBaseActivity() {
358 return (BaseActivity) getActivity();
Ben Lin5668b862016-06-28 17:12:52 -0700359 }
360
Garfield, Tan804133e2016-04-20 15:13:56 -0700361 @Override
362 public void runOnUiThread(Runnable runnable) {
363 getActivity().runOnUiThread(runnable);
364 }
365
Ben Lind0202122016-11-10 18:00:12 -0800366 // In RootsFragment, we check whether the item corresponds to a RootItem, and whether
367 // the currently dragged objects can be droppable or not, and change the drop-shadow
368 // accordingly
Ben Lin7f72a3c2016-09-27 16:37:28 -0700369 @Override
Ben Lin1c456292016-10-07 16:43:18 -0700370 public void onDragEntered(View v, Object localState) {
Ben Lind0202122016-11-10 18:00:12 -0800371 final int pos = (Integer) v.getTag(R.id.item_position_tag);
372 final Item item = mAdapter.getItem(pos);
373
374 // If a read-only root, no need to see if top level is writable (it's not)
375 if (!(item instanceof RootItem) || !((RootItem) item).root.supportsCreate()) {
376 getBaseActivity().getShadowBuilder().setAppearDroppable(false);
377 v.updateDragShadow(getBaseActivity().getShadowBuilder());
378 return;
379 }
380
381 final RootItem rootItem = (RootItem) item;
382 getRootDocument(rootItem, (DocumentInfo doc) -> {
Ben Linb0761d32017-02-16 11:04:04 -0800383 updateDropShadow(v, localState, rootItem, doc);
Ben Lind0202122016-11-10 18:00:12 -0800384 });
Ben Lin7f72a3c2016-09-27 16:37:28 -0700385 }
386
Ben Linb0761d32017-02-16 11:04:04 -0800387 private void updateDropShadow(
388 View v, Object localState, RootItem rootItem, DocumentInfo rootDoc) {
389 final DragShadowBuilder shadowBuilder = getBaseActivity().getShadowBuilder();
390 if (rootDoc == null) {
391 Log.e(TAG, "Root DocumentInfo is null. Defaulting to appear not droppable.");
392 shadowBuilder.setAppearDroppable(false);
393 } else {
394 rootItem.docInfo = rootDoc;
395 shadowBuilder.setAppearDroppable(rootDoc.isCreateSupported()
396 && DragAndDropHelper.canCopyTo(localState, rootDoc));
397 }
398 v.updateDragShadow(shadowBuilder);
399 }
400
Ben Lind0202122016-11-10 18:00:12 -0800401 // In RootsFragment we always reset the drag shadow as it exits a RootItemView.
402 @Override
403 public void onDragExited(View v, Object localState) {
404 getBaseActivity().getShadowBuilder().resetBackground();
405 v.updateDragShadow(getBaseActivity().getShadowBuilder());
406 }
407
408 // In RootsFragment we open the hovered root.
Garfield, Tan804133e2016-04-20 15:13:56 -0700409 @Override
Garfield, Tana5588b62016-07-13 09:23:04 -0700410 public void onViewHovered(View v) {
Garfield, Tan57facaf72016-05-27 15:02:35 -0700411 // SpacerView doesn't have DragListener so this view is guaranteed to be a RootItemView.
Garfield, Tana5588b62016-07-13 09:23:04 -0700412 RootItemView itemView = (RootItemView) v;
Garfield, Tan57facaf72016-05-27 15:02:35 -0700413 itemView.drawRipple();
414
Garfield, Tana5588b62016-07-13 09:23:04 -0700415 final int position = (Integer) v.getTag(R.id.item_position_tag);
Garfield, Tan7d66a862016-05-11 10:28:41 -0700416 final Item item = mAdapter.getItem(position);
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700417 item.open();
Garfield, Tan804133e2016-04-20 15:13:56 -0700418 }
419
420 @Override
Ben Lin166c5c62016-11-01 12:14:38 -0700421 public void setDropTargetHighlight(View v, Object localState, boolean highlight) {
Garfield, Tan57facaf72016-05-27 15:02:35 -0700422 // SpacerView doesn't have DragListener so this view is guaranteed to be a RootItemView.
423 RootItemView itemView = (RootItemView) v;
424 itemView.setHighlight(highlight);
Garfield, Tan804133e2016-04-20 15:13:56 -0700425 }
426
Ben Lin5668b862016-06-28 17:12:52 -0700427 @Override
428 public void onCreateContextMenu(
429 ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
430 super.onCreateContextMenu(menu, v, menuInfo);
431 AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
432 final Item item = mAdapter.getItem(adapterMenuInfo.position);
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700433
Steve McKay92ae43d2016-11-08 12:06:58 -0800434 BaseActivity activity = getBaseActivity();
435 item.createContextMenu(menu, activity.getMenuInflater(), mInjector.menuManager);
Ben Lin5668b862016-06-28 17:12:52 -0700436 }
437
438 @Override
439 public boolean onContextItemSelected(MenuItem item) {
440 AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
Ben Lin035f0c82016-07-14 10:40:00 -0700441 // There is a possibility that this is called from DirectoryFragment since
442 // all fragments' onContextItemSelected gets called when any menu item is selected
443 // This is to guard against it since DirectoryFragment's RecylerView does not have a
444 // menuInfo
445 if (adapterMenuInfo == null) {
446 return false;
447 }
Ben Lin5668b862016-06-28 17:12:52 -0700448 final RootItem rootItem = (RootItem) mAdapter.getItem(adapterMenuInfo.position);
Ben Lin035f0c82016-07-14 10:40:00 -0700449 switch (item.getItemId()) {
Ben Lin5668b862016-06-28 17:12:52 -0700450 case R.id.menu_eject_root:
Ben Lin821727a2016-07-08 10:49:53 -0700451 final View ejectIcon = adapterMenuInfo.targetView.findViewById(R.id.eject_icon);
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700452 ejectClicked(ejectIcon, rootItem.root, mActionHandler);
Ben Lin5668b862016-06-28 17:12:52 -0700453 return true;
Garfield Tanb285b402016-09-21 17:12:18 -0700454 case R.id.menu_open_in_new_window:
Steve McKay739f94b2016-09-22 14:54:23 -0700455 mActionHandler.openInNewWindow(new DocumentStack(rootItem.root));
Garfield Tanb285b402016-09-21 17:12:18 -0700456 return true;
457 case R.id.menu_paste_into_folder:
458 mActionHandler.pasteIntoFolder(rootItem.root);
459 return true;
Ben Lin5668b862016-06-28 17:12:52 -0700460 case R.id.menu_settings:
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700461 mActionHandler.openSettings(rootItem.root);
Ben Lin5668b862016-06-28 17:12:52 -0700462 return true;
463 default:
464 if (DEBUG) Log.d(TAG, "Unhandled menu item selected: " + item);
465 return false;
466 }
467 }
468
Ben Lind0202122016-11-10 18:00:12 -0800469 @FunctionalInterface
470 interface RootUpdater {
471 void updateDocInfoForRoot(DocumentInfo doc);
472 }
473
474 private void getRootDocument(RootItem rootItem, RootUpdater updater) {
475 // We need to start a GetRootDocumentTask so we can know whether items can be directly
476 // pasted into root
Ben Lin30b0dc12017-03-07 15:37:16 -0800477 mActionHandler.getRootDocument(
Ben Lind0202122016-11-10 18:00:12 -0800478 rootItem.root,
Ben Lin30b0dc12017-03-07 15:37:16 -0800479 CONTEXT_MENU_ITEM_TIMEOUT,
Ben Lind0202122016-11-10 18:00:12 -0800480 (DocumentInfo doc) -> {
481 updater.updateDocInfoForRoot(doc);
482 });
Ben Lind0202122016-11-10 18:00:12 -0800483 }
484
Steve McKay739f94b2016-09-22 14:54:23 -0700485 static void ejectClicked(View ejectIcon, RootInfo root, ActionHandler actionHandler) {
Ben Lin5668b862016-06-28 17:12:52 -0700486 assert(ejectIcon != null);
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700487 assert(!root.ejecting);
Ben Lin5668b862016-06-28 17:12:52 -0700488 ejectIcon.setEnabled(false);
489 root.ejecting = true;
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700490 actionHandler.ejectRoot(
491 root,
Steve McKay739f94b2016-09-22 14:54:23 -0700492 new BooleanConsumer() {
Ben Lin5668b862016-06-28 17:12:52 -0700493 @Override
Steve McKay739f94b2016-09-22 14:54:23 -0700494 public void accept(boolean ejected) {
495 // Event if ejected is false, we should reset, since the op failed.
496 // Either way, we are no longer attempting to eject the device.
Ben Lin5668b862016-06-28 17:12:52 -0700497 root.ejecting = false;
Steve McKay739f94b2016-09-22 14:54:23 -0700498
499 // If the view is still visible, we update its state.
500 if (ejectIcon.getVisibility() == View.VISIBLE) {
501 ejectIcon.setEnabled(!ejected);
502 }
Ben Lin5668b862016-06-28 17:12:52 -0700503 }
504 });
505 }
506
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700507 private static class RootComparator implements Comparator<RootItem> {
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700508 @Override
Ben Kwa85c8cdd2015-10-09 07:48:00 -0700509 public int compare(RootItem lhs, RootItem rhs) {
Steve McKay55c00e72016-02-18 15:32:16 -0800510 return lhs.root.compareTo(rhs.root);
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700511 }
512 }
Jeff Sharkey76112212013-08-06 11:26:10 -0700513}