blob: 7b9c99c2b69dce83bf379e15f28d9ec64d4a240a [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 McKaycc460af2015-11-03 14:00:45 -080020
Garfield Tan7d75f7b2016-09-20 16:33:24 -070021import android.annotation.Nullable;
Steve McKay17f7e582016-04-04 15:26:48 -070022import android.app.Activity;
Jeff Sharkey76112212013-08-06 11:26:10 -070023import android.app.Fragment;
24import android.app.FragmentManager;
25import android.app.FragmentTransaction;
Jeff Sharkey8b997042013-09-19 15:25:56 -070026import android.app.LoaderManager.LoaderCallbacks;
Jeff Sharkey76112212013-08-06 11:26:10 -070027import android.content.Context;
Jeff Sharkey1d890e02013-08-15 11:24:03 -070028import android.content.Intent;
Jeff Sharkey8b997042013-09-19 15:25:56 -070029import android.content.Loader;
Jeff Sharkey1d890e02013-08-15 11:24:03 -070030import android.content.pm.PackageManager;
31import android.content.pm.ResolveInfo;
Ben Lin8e912582016-09-23 14:25:14 -070032import android.os.AsyncTask;
Jeff Sharkey76112212013-08-06 11:26:10 -070033import android.os.Bundle;
Ben Lin8e912582016-09-23 14:25:14 -070034import android.os.Handler;
Steve McKaycc460af2015-11-03 14:00:45 -080035import android.util.Log;
Ben Lin5668b862016-06-28 17:12:52 -070036import android.view.ContextMenu;
Garfield, Tana5588b62016-07-13 09:23:04 -070037import android.view.DragEvent;
Jeff Sharkey76112212013-08-06 11:26:10 -070038import android.view.LayoutInflater;
Ben Lin5668b862016-06-28 17:12:52 -070039import android.view.MenuItem;
40import android.view.MotionEvent;
Jeff Sharkey76112212013-08-06 11:26:10 -070041import android.view.View;
Garfield, Tan804133e2016-04-20 15:13:56 -070042import android.view.View.OnDragListener;
Ben Lin5668b862016-06-28 17:12:52 -070043import android.view.View.OnGenericMotionListener;
Jeff Sharkey76112212013-08-06 11:26:10 -070044import android.view.ViewGroup;
45import android.widget.AdapterView;
Ben Lin5668b862016-06-28 17:12:52 -070046import android.widget.AdapterView.AdapterContextMenuInfo;
Jeff Sharkey76112212013-08-06 11:26:10 -070047import android.widget.AdapterView.OnItemClickListener;
Jeff Sharkey8f1edfe2013-10-23 16:05:56 -070048import android.widget.AdapterView.OnItemLongClickListener;
Jeff Sharkey76112212013-08-06 11:26:10 -070049import android.widget.ListView;
Jeff Sharkey76112212013-08-06 11:26:10 -070050
Steve McKay6d20d192016-09-21 17:57:10 -070051import com.android.documentsui.ActionHandler;
Steve McKayd0805062016-09-15 14:30:38 -070052import com.android.documentsui.BaseActivity;
53import com.android.documentsui.DocumentsApplication;
Ben Lin8e912582016-09-23 14:25:14 -070054import com.android.documentsui.GetRootDocumentTask;
Steve McKayd0805062016-09-15 14:30:38 -070055import com.android.documentsui.ItemDragListener;
Steve McKayd0805062016-09-15 14:30:38 -070056import com.android.documentsui.R;
Steve McKay739f94b2016-09-22 14:54:23 -070057import com.android.documentsui.base.BooleanConsumer;
Ben Lin8e912582016-09-23 14:25:14 -070058import com.android.documentsui.base.DocumentInfo;
Steve McKay739f94b2016-09-22 14:54:23 -070059import com.android.documentsui.base.DocumentStack;
Steve McKayd9caa6a2016-09-15 16:36:45 -070060import com.android.documentsui.base.Events;
Steve McKayd0805062016-09-15 14:30:38 -070061import com.android.documentsui.base.RootInfo;
Steve McKayd9caa6a2016-09-15 16:36:45 -070062import com.android.documentsui.base.Shared;
63import com.android.documentsui.base.State;
Steve McKayd9caa6a2016-09-15 16:36:45 -070064import com.android.documentsui.roots.RootsCache;
65import com.android.documentsui.roots.RootsLoader;
Jeff Sharkey76112212013-08-06 11:26:10 -070066
Steve McKayfefcd702015-08-20 16:19:38 +000067import java.util.ArrayList;
Jeff Sharkey8b997042013-09-19 15:25:56 -070068import java.util.Collection;
Jeff Sharkey9dd02622013-09-27 16:44:11 -070069import java.util.Collections;
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -070070import java.util.Comparator;
Jeff Sharkey1d890e02013-08-15 11:24:03 -070071import java.util.List;
Kenny Root16d85982013-12-13 12:00:26 -080072import java.util.Objects;
Jeff Sharkey76112212013-08-06 11:26:10 -070073
74/**
75 * Display list of known storage backend roots.
76 */
Garfield, Tan804133e2016-04-20 15:13:56 -070077public class RootsFragment extends Fragment implements ItemDragListener.DragHost {
Jeff Sharkey76112212013-08-06 11:26:10 -070078
Steve McKaycc460af2015-11-03 14:00:45 -080079 private static final String TAG = "RootsFragment";
80 private static final String EXTRA_INCLUDE_APPS = "includeApps";
Ben Lin8e912582016-09-23 14:25:14 -070081 private static final int CONTEXT_MENU_ITEM_TIMEOUT = 500;
Steve McKaycc460af2015-11-03 14:00:45 -080082
Garfield, Tana5588b62016-07-13 09:23:04 -070083 private final OnDragListener mDragListener = new ItemDragListener<RootsFragment>(this) {
84 @Override
85 public boolean handleDropEventChecked(View v, DragEvent event) {
86 final int position = (Integer) v.getTag(R.id.item_position_tag);
87 final Item item = mAdapter.getItem(position);
88
89 assert(item.isDropTarget());
90
Garfield Tanb285b402016-09-21 17:12:18 -070091 return item.dropOn(event.getClipData());
Garfield Tan7d75f7b2016-09-20 16:33:24 -070092 }
93 };
94
95 private final OnItemClickListener mItemListener = new OnItemClickListener() {
96 @Override
97 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
98 final Item item = mAdapter.getItem(position);
99 item.open();
100
101 getBaseActivity().setRootsDrawerOpen(false);
102 }
103 };
104
105 private final OnItemLongClickListener mItemLongClickListener = new OnItemLongClickListener() {
106 @Override
107 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
108 final Item item = mAdapter.getItem(position);
Garfield Tanb285b402016-09-21 17:12:18 -0700109 return item.showAppDetails();
Garfield, Tana5588b62016-07-13 09:23:04 -0700110 }
111 };
112
Jeff Sharkey76112212013-08-06 11:26:10 -0700113 private ListView mList;
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700114 private RootsAdapter mAdapter;
Jeff Sharkey8b997042013-09-19 15:25:56 -0700115 private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
Steve McKay739f94b2016-09-22 14:54:23 -0700116 private ActionHandler mActionHandler;
Ben Lin821727a2016-07-08 10:49:53 -0700117
118 public static RootsFragment show(FragmentManager fm, Intent includeApps) {
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700119 final Bundle args = new Bundle();
120 args.putParcelable(EXTRA_INCLUDE_APPS, includeApps);
121
Jeff Sharkey76112212013-08-06 11:26:10 -0700122 final RootsFragment fragment = new RootsFragment();
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700123 fragment.setArguments(args);
Jeff Sharkey76112212013-08-06 11:26:10 -0700124
125 final FragmentTransaction ft = fm.beginTransaction();
126 ft.replace(R.id.container_roots, fragment);
127 ft.commitAllowingStateLoss();
Ben Lin821727a2016-07-08 10:49:53 -0700128
129 return fragment;
Jeff Sharkey76112212013-08-06 11:26:10 -0700130 }
131
132 public static RootsFragment get(FragmentManager fm) {
133 return (RootsFragment) fm.findFragmentById(R.id.container_roots);
134 }
135
136 @Override
137 public View onCreateView(
138 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Jeff Sharkey76112212013-08-06 11:26:10 -0700139
140 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, () -> {
158 getBaseActivity().getMenuManager()
159 .showContextMenu(RootsFragment.this, v, x, y);
160 });
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) {
170 int pos = mList.pointToPosition(x, y);
171 final Item item = mAdapter.getItem(pos);
172 if (!(item instanceof RootItem)) {
173 return false;
174 }
175 final RootItem rootItem = (RootItem) item;
176
177 if (!rootItem.root.supportsCreate()) {
178 // If a read-only root, no need to see if top level is writable (it's not)
179 callback.run();
180 return true;
181 }
182 // We need to start a GetRootDocumentTask so we can know whether items can be directly
183 // pasted into root
184 GetRootDocumentTask task = new GetRootDocumentTask(
185 rootItem.root,
186 getBaseActivity(),
187 (DocumentInfo doc) -> {
188 rootItem.docInfo = doc;
189 callback.run();
190 });
191 task.setTimeout(CONTEXT_MENU_ITEM_TIMEOUT);
192 task.setForceCallback(true);
193 task.executeOnExecutor(getBaseActivity().getExecutorForCurrentDirectory());
194
195 return true;
196 }
197
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700198 @Override
Jeff Sharkey8b997042013-09-19 15:25:56 -0700199 public void onActivityCreated(Bundle savedInstanceState) {
200 super.onActivityCreated(savedInstanceState);
201
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700202 final BaseActivity activity = getBaseActivity();
203 final RootsCache roots = DocumentsApplication.getRootsCache(activity);
204 final State state = activity.getDisplayState();
205
Steve McKay6d20d192016-09-21 17:57:10 -0700206 mActionHandler = activity.getActionHandler(null, null);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700207
208 mCallbacks = new LoaderCallbacks<Collection<RootInfo>>() {
209 @Override
210 public Loader<Collection<RootInfo>> onCreateLoader(int id, Bundle args) {
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700211 return new RootsLoader(activity, roots, state);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700212 }
213
214 @Override
215 public void onLoadFinished(
216 Loader<Collection<RootInfo>> loader, Collection<RootInfo> result) {
Steve McKay323dffb2016-02-17 18:25:47 -0800217 if (!isAdded()) {
218 return;
219 }
Jeff Sharkey8b997042013-09-19 15:25:56 -0700220
Steve McKay323dffb2016-02-17 18:25:47 -0800221 Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700222
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700223 List<Item> sortedItems = sortLoadResult(result, handlerAppIntent);
224 mAdapter = new RootsAdapter(activity, sortedItems, mDragListener);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700225 mList.setAdapter(mAdapter);
226
227 onCurrentRootChanged();
228 }
229
230 @Override
231 public void onLoaderReset(Loader<Collection<RootInfo>> loader) {
232 mAdapter = null;
233 mList.setAdapter(null);
234 }
235 };
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700236 }
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700237
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700238 /**
239 * @param handlerAppIntent When not null, apps capable of handling the original intent will
240 * be included in list of roots (in special section at bottom).
241 */
242 private List<Item> sortLoadResult(
243 Collection<RootInfo> roots, @Nullable Intent handlerAppIntent) {
244 final List<Item> result = new ArrayList<>();
245
246 final List<RootItem> libraries = new ArrayList<>();
247 final List<RootItem> others = new ArrayList<>();
248
249 for (final RootInfo root : roots) {
250 final RootItem item = new RootItem(root, mActionHandler);
251
252 Activity activity = getActivity();
253 if (root.isHome() && !Shared.shouldShowDocumentsRoot(activity, activity.getIntent())) {
254 continue;
255 } else if (root.isLibrary()) {
256 libraries.add(item);
257 } else {
258 others.add(item);
259 }
260 }
261
262 final RootComparator comp = new RootComparator();
263 Collections.sort(libraries, comp);
264 Collections.sort(others, comp);
265
266 if (DEBUG) Log.v(TAG, "Adding library roots: " + libraries);
267 result.addAll(libraries);
268 // Only add the spacer if it is actually separating something.
269 if (!libraries.isEmpty() && !others.isEmpty()) {
270 result.add(new SpacerItem());
271 }
272
273 if (DEBUG) Log.v(TAG, "Adding plain roots: " + libraries);
274 result.addAll(others);
275
276 // Include apps that can handle this intent too.
277 if (handlerAppIntent != null) {
278 includeHandlerApps(handlerAppIntent, result);
279 }
280
281 return result;
282 }
283
284 /**
285 * Adds apps capable of handling the original intent will be included in list of roots (in
286 * special section at bottom).
287 */
288 private void includeHandlerApps(Intent handlerAppIntent, List<Item> result) {
289 if (DEBUG) Log.v(TAG, "Adding handler apps for intent: " + handlerAppIntent);
290 Context context = getContext();
291 final PackageManager pm = context.getPackageManager();
292 final List<ResolveInfo> infos = pm.queryIntentActivities(
293 handlerAppIntent, PackageManager.MATCH_DEFAULT_ONLY);
294
295 final List<AppItem> apps = new ArrayList<>();
296
297 // Omit ourselves from the list
298 for (ResolveInfo info : infos) {
299 if (!context.getPackageName().equals(info.activityInfo.packageName)) {
300 apps.add(new AppItem(info, mActionHandler));
301 }
302 }
303
304 if (apps.size() > 0) {
305 result.add(new SpacerItem());
306 result.addAll(apps);
307 }
308 }
309
Jeff Sharkey8b997042013-09-19 15:25:56 -0700310 @Override
311 public void onResume() {
312 super.onResume();
Jeff Sharkeye8d13ea2014-08-08 15:10:03 -0700313 onDisplayStateChanged();
314 }
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700315
Jeff Sharkeye8d13ea2014-08-08 15:10:03 -0700316 public void onDisplayStateChanged() {
Jeff Sharkey8b997042013-09-19 15:25:56 -0700317 final Context context = getActivity();
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700318 final State state = ((BaseActivity) context).getDisplayState();
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700319
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700320 if (state.action == State.ACTION_GET_CONTENT) {
Jeff Sharkey8f1edfe2013-10-23 16:05:56 -0700321 mList.setOnItemLongClickListener(mItemLongClickListener);
322 } else {
323 mList.setOnItemLongClickListener(null);
324 mList.setLongClickable(false);
325 }
326
Jeff Sharkey8b997042013-09-19 15:25:56 -0700327 getLoaderManager().restartLoader(2, null, mCallbacks);
Jeff Sharkey42d26792013-09-06 13:22:09 -0700328 }
329
330 public void onCurrentRootChanged() {
Steve McKay323dffb2016-02-17 18:25:47 -0800331 if (mAdapter == null) {
332 return;
333 }
Jeff Sharkey42d26792013-09-06 13:22:09 -0700334
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700335 final RootInfo root = ((BaseActivity) getActivity()).getCurrentRoot();
Jeff Sharkey42d26792013-09-06 13:22:09 -0700336 for (int i = 0; i < mAdapter.getCount(); i++) {
337 final Object item = mAdapter.getItem(i);
Jeff Sharkey25f10b32013-10-07 14:08:17 -0700338 if (item instanceof RootItem) {
339 final RootInfo testRoot = ((RootItem) item).root;
Kenny Root16d85982013-12-13 12:00:26 -0800340 if (Objects.equals(testRoot, root)) {
Jeff Sharkey25f10b32013-10-07 14:08:17 -0700341 mList.setItemChecked(i, true);
342 return;
343 }
Jeff Sharkey42d26792013-09-06 13:22:09 -0700344 }
345 }
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700346 }
347
Ben Kwa74e5d412016-02-10 07:46:35 -0800348 /**
349 * Attempts to shift focus back to the navigation drawer.
350 */
351 public void requestFocus() {
352 mList.requestFocus();
353 }
354
Ben Lin5668b862016-06-28 17:12:52 -0700355 private BaseActivity getBaseActivity() {
356 return (BaseActivity) getActivity();
357 }
358
Garfield, Tan804133e2016-04-20 15:13:56 -0700359 @Override
360 public void runOnUiThread(Runnable runnable) {
361 getActivity().runOnUiThread(runnable);
362 }
363
364 /**
365 * {@inheritDoc}
366 *
367 * In RootsFragment we open the hovered root.
368 */
369 @Override
Garfield, Tana5588b62016-07-13 09:23:04 -0700370 public void onViewHovered(View v) {
Garfield, Tan57facaf72016-05-27 15:02:35 -0700371 // SpacerView doesn't have DragListener so this view is guaranteed to be a RootItemView.
Garfield, Tana5588b62016-07-13 09:23:04 -0700372 RootItemView itemView = (RootItemView) v;
Garfield, Tan57facaf72016-05-27 15:02:35 -0700373 itemView.drawRipple();
374
Garfield, Tana5588b62016-07-13 09:23:04 -0700375 final int position = (Integer) v.getTag(R.id.item_position_tag);
Garfield, Tan7d66a862016-05-11 10:28:41 -0700376 final Item item = mAdapter.getItem(position);
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700377 item.open();
Garfield, Tan804133e2016-04-20 15:13:56 -0700378 }
379
380 @Override
381 public void setDropTargetHighlight(View v, boolean highlight) {
Garfield, Tan57facaf72016-05-27 15:02:35 -0700382 // SpacerView doesn't have DragListener so this view is guaranteed to be a RootItemView.
383 RootItemView itemView = (RootItemView) v;
384 itemView.setHighlight(highlight);
Garfield, Tan804133e2016-04-20 15:13:56 -0700385 }
386
Ben Lin5668b862016-06-28 17:12:52 -0700387 @Override
388 public void onCreateContextMenu(
389 ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
390 super.onCreateContextMenu(menu, v, menuInfo);
391 AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
392 final Item item = mAdapter.getItem(adapterMenuInfo.position);
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700393
394 BaseActivity activity = getBaseActivity();
395 item.createContextMenu(menu, activity.getMenuInflater(), activity.getMenuManager());
Ben Lin5668b862016-06-28 17:12:52 -0700396 }
397
398 @Override
399 public boolean onContextItemSelected(MenuItem item) {
400 AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
Ben Lin035f0c82016-07-14 10:40:00 -0700401 // There is a possibility that this is called from DirectoryFragment since
402 // all fragments' onContextItemSelected gets called when any menu item is selected
403 // This is to guard against it since DirectoryFragment's RecylerView does not have a
404 // menuInfo
405 if (adapterMenuInfo == null) {
406 return false;
407 }
Ben Lin5668b862016-06-28 17:12:52 -0700408 final RootItem rootItem = (RootItem) mAdapter.getItem(adapterMenuInfo.position);
Ben Lin035f0c82016-07-14 10:40:00 -0700409 switch (item.getItemId()) {
Ben Lin5668b862016-06-28 17:12:52 -0700410 case R.id.menu_eject_root:
Ben Lin821727a2016-07-08 10:49:53 -0700411 final View ejectIcon = adapterMenuInfo.targetView.findViewById(R.id.eject_icon);
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700412 ejectClicked(ejectIcon, rootItem.root, mActionHandler);
Ben Lin5668b862016-06-28 17:12:52 -0700413 return true;
Garfield Tanb285b402016-09-21 17:12:18 -0700414 case R.id.menu_open_in_new_window:
Steve McKay739f94b2016-09-22 14:54:23 -0700415 mActionHandler.openInNewWindow(new DocumentStack(rootItem.root));
Garfield Tanb285b402016-09-21 17:12:18 -0700416 return true;
417 case R.id.menu_paste_into_folder:
418 mActionHandler.pasteIntoFolder(rootItem.root);
419 return true;
Ben Lin5668b862016-06-28 17:12:52 -0700420 case R.id.menu_settings:
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700421 mActionHandler.openSettings(rootItem.root);
Ben Lin5668b862016-06-28 17:12:52 -0700422 return true;
423 default:
424 if (DEBUG) Log.d(TAG, "Unhandled menu item selected: " + item);
425 return false;
426 }
427 }
428
Steve McKay739f94b2016-09-22 14:54:23 -0700429 static void ejectClicked(View ejectIcon, RootInfo root, ActionHandler actionHandler) {
Ben Lin5668b862016-06-28 17:12:52 -0700430 assert(ejectIcon != null);
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700431 assert(!root.ejecting);
Ben Lin5668b862016-06-28 17:12:52 -0700432 ejectIcon.setEnabled(false);
433 root.ejecting = true;
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700434 actionHandler.ejectRoot(
435 root,
Steve McKay739f94b2016-09-22 14:54:23 -0700436 new BooleanConsumer() {
Ben Lin5668b862016-06-28 17:12:52 -0700437 @Override
Steve McKay739f94b2016-09-22 14:54:23 -0700438 public void accept(boolean ejected) {
439 // Event if ejected is false, we should reset, since the op failed.
440 // Either way, we are no longer attempting to eject the device.
Ben Lin5668b862016-06-28 17:12:52 -0700441 root.ejecting = false;
Steve McKay739f94b2016-09-22 14:54:23 -0700442
443 // If the view is still visible, we update its state.
444 if (ejectIcon.getVisibility() == View.VISIBLE) {
445 ejectIcon.setEnabled(!ejected);
446 }
Ben Lin5668b862016-06-28 17:12:52 -0700447 }
448 });
449 }
450
Garfield Tan7d75f7b2016-09-20 16:33:24 -0700451 private static class RootComparator implements Comparator<RootItem> {
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700452 @Override
Ben Kwa85c8cdd2015-10-09 07:48:00 -0700453 public int compare(RootItem lhs, RootItem rhs) {
Steve McKay55c00e72016-02-18 15:32:16 -0800454 return lhs.root.compareTo(rhs.root);
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700455 }
456 }
Jeff Sharkey76112212013-08-06 11:26:10 -0700457}