Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 1 | /* |
| 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 McKay | d080506 | 2016-09-15 14:30:38 -0700 | [diff] [blame] | 17 | package com.android.documentsui.sidebar; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 18 | |
Steve McKay | d9caa6a | 2016-09-15 16:36:45 -0700 | [diff] [blame] | 19 | import static com.android.documentsui.base.Shared.DEBUG; |
Steve McKay | cc460af | 2015-11-03 14:00:45 -0800 | [diff] [blame] | 20 | |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 21 | import android.annotation.Nullable; |
Steve McKay | 17f7e58 | 2016-04-04 15:26:48 -0700 | [diff] [blame] | 22 | import android.app.Activity; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 23 | import android.app.Fragment; |
| 24 | import android.app.FragmentManager; |
| 25 | import android.app.FragmentTransaction; |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 26 | import android.app.LoaderManager.LoaderCallbacks; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 27 | import android.content.Context; |
Jeff Sharkey | 1d890e0 | 2013-08-15 11:24:03 -0700 | [diff] [blame] | 28 | import android.content.Intent; |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 29 | import android.content.Loader; |
Jeff Sharkey | 1d890e0 | 2013-08-15 11:24:03 -0700 | [diff] [blame] | 30 | import android.content.pm.PackageManager; |
| 31 | import android.content.pm.ResolveInfo; |
Ben Lin | 8e91258 | 2016-09-23 14:25:14 -0700 | [diff] [blame^] | 32 | import android.os.AsyncTask; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 33 | import android.os.Bundle; |
Ben Lin | 8e91258 | 2016-09-23 14:25:14 -0700 | [diff] [blame^] | 34 | import android.os.Handler; |
Steve McKay | cc460af | 2015-11-03 14:00:45 -0800 | [diff] [blame] | 35 | import android.util.Log; |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 36 | import android.view.ContextMenu; |
Garfield, Tan | a5588b6 | 2016-07-13 09:23:04 -0700 | [diff] [blame] | 37 | import android.view.DragEvent; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 38 | import android.view.LayoutInflater; |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 39 | import android.view.MenuItem; |
| 40 | import android.view.MotionEvent; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 41 | import android.view.View; |
Garfield, Tan | 804133e | 2016-04-20 15:13:56 -0700 | [diff] [blame] | 42 | import android.view.View.OnDragListener; |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 43 | import android.view.View.OnGenericMotionListener; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 44 | import android.view.ViewGroup; |
| 45 | import android.widget.AdapterView; |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 46 | import android.widget.AdapterView.AdapterContextMenuInfo; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 47 | import android.widget.AdapterView.OnItemClickListener; |
Jeff Sharkey | 8f1edfe | 2013-10-23 16:05:56 -0700 | [diff] [blame] | 48 | import android.widget.AdapterView.OnItemLongClickListener; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 49 | import android.widget.ListView; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 50 | |
Steve McKay | 6d20d19 | 2016-09-21 17:57:10 -0700 | [diff] [blame] | 51 | import com.android.documentsui.ActionHandler; |
Steve McKay | d080506 | 2016-09-15 14:30:38 -0700 | [diff] [blame] | 52 | import com.android.documentsui.BaseActivity; |
| 53 | import com.android.documentsui.DocumentsApplication; |
Ben Lin | 8e91258 | 2016-09-23 14:25:14 -0700 | [diff] [blame^] | 54 | import com.android.documentsui.GetRootDocumentTask; |
Steve McKay | d080506 | 2016-09-15 14:30:38 -0700 | [diff] [blame] | 55 | import com.android.documentsui.ItemDragListener; |
Steve McKay | d080506 | 2016-09-15 14:30:38 -0700 | [diff] [blame] | 56 | import com.android.documentsui.R; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 57 | import com.android.documentsui.base.BooleanConsumer; |
Ben Lin | 8e91258 | 2016-09-23 14:25:14 -0700 | [diff] [blame^] | 58 | import com.android.documentsui.base.DocumentInfo; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 59 | import com.android.documentsui.base.DocumentStack; |
Steve McKay | d9caa6a | 2016-09-15 16:36:45 -0700 | [diff] [blame] | 60 | import com.android.documentsui.base.Events; |
Steve McKay | d080506 | 2016-09-15 14:30:38 -0700 | [diff] [blame] | 61 | import com.android.documentsui.base.RootInfo; |
Steve McKay | d9caa6a | 2016-09-15 16:36:45 -0700 | [diff] [blame] | 62 | import com.android.documentsui.base.Shared; |
| 63 | import com.android.documentsui.base.State; |
Steve McKay | d9caa6a | 2016-09-15 16:36:45 -0700 | [diff] [blame] | 64 | import com.android.documentsui.roots.RootsCache; |
| 65 | import com.android.documentsui.roots.RootsLoader; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 66 | |
Steve McKay | fefcd70 | 2015-08-20 16:19:38 +0000 | [diff] [blame] | 67 | import java.util.ArrayList; |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 68 | import java.util.Collection; |
Jeff Sharkey | 9dd0262 | 2013-09-27 16:44:11 -0700 | [diff] [blame] | 69 | import java.util.Collections; |
Jeff Sharkey | b6a7f2c | 2013-08-27 18:26:48 -0700 | [diff] [blame] | 70 | import java.util.Comparator; |
Jeff Sharkey | 1d890e0 | 2013-08-15 11:24:03 -0700 | [diff] [blame] | 71 | import java.util.List; |
Kenny Root | 16d8598 | 2013-12-13 12:00:26 -0800 | [diff] [blame] | 72 | import java.util.Objects; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 73 | |
| 74 | /** |
| 75 | * Display list of known storage backend roots. |
| 76 | */ |
Garfield, Tan | 804133e | 2016-04-20 15:13:56 -0700 | [diff] [blame] | 77 | public class RootsFragment extends Fragment implements ItemDragListener.DragHost { |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 78 | |
Steve McKay | cc460af | 2015-11-03 14:00:45 -0800 | [diff] [blame] | 79 | private static final String TAG = "RootsFragment"; |
| 80 | private static final String EXTRA_INCLUDE_APPS = "includeApps"; |
Ben Lin | 8e91258 | 2016-09-23 14:25:14 -0700 | [diff] [blame^] | 81 | private static final int CONTEXT_MENU_ITEM_TIMEOUT = 500; |
Steve McKay | cc460af | 2015-11-03 14:00:45 -0800 | [diff] [blame] | 82 | |
Garfield, Tan | a5588b6 | 2016-07-13 09:23:04 -0700 | [diff] [blame] | 83 | 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 Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 91 | return item.dropOn(event.getClipData()); |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 92 | } |
| 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 Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 109 | return item.showAppDetails(); |
Garfield, Tan | a5588b6 | 2016-07-13 09:23:04 -0700 | [diff] [blame] | 110 | } |
| 111 | }; |
| 112 | |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 113 | private ListView mList; |
Jeff Sharkey | 9dd0262 | 2013-09-27 16:44:11 -0700 | [diff] [blame] | 114 | private RootsAdapter mAdapter; |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 115 | private LoaderCallbacks<Collection<RootInfo>> mCallbacks; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 116 | private ActionHandler mActionHandler; |
Ben Lin | 821727a | 2016-07-08 10:49:53 -0700 | [diff] [blame] | 117 | |
| 118 | public static RootsFragment show(FragmentManager fm, Intent includeApps) { |
Jeff Sharkey | 1d890e0 | 2013-08-15 11:24:03 -0700 | [diff] [blame] | 119 | final Bundle args = new Bundle(); |
| 120 | args.putParcelable(EXTRA_INCLUDE_APPS, includeApps); |
| 121 | |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 122 | final RootsFragment fragment = new RootsFragment(); |
Jeff Sharkey | 1d890e0 | 2013-08-15 11:24:03 -0700 | [diff] [blame] | 123 | fragment.setArguments(args); |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 124 | |
| 125 | final FragmentTransaction ft = fm.beginTransaction(); |
| 126 | ft.replace(R.id.container_roots, fragment); |
| 127 | ft.commitAllowingStateLoss(); |
Ben Lin | 821727a | 2016-07-08 10:49:53 -0700 | [diff] [blame] | 128 | |
| 129 | return fragment; |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 130 | } |
| 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 Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 139 | |
| 140 | final View view = inflater.inflate(R.layout.fragment_roots, container, false); |
Ben Kwa | 74e5d41 | 2016-02-10 07:46:35 -0800 | [diff] [blame] | 141 | mList = (ListView) view.findViewById(R.id.roots_list); |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 142 | mList.setOnItemClickListener(mItemListener); |
Ben Lin | 821727a | 2016-07-08 10:49:53 -0700 | [diff] [blame] | 143 | // 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 Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 149 | mList.setOnGenericMotionListener( |
| 150 | new OnGenericMotionListener() { |
Ben Lin | 8e91258 | 2016-09-23 14:25:14 -0700 | [diff] [blame^] | 151 | @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 Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 163 | } |
| 164 | }); |
Jeff Sharkey | 42d2679 | 2013-09-06 13:22:09 -0700 | [diff] [blame] | 165 | mList.setChoiceMode(ListView.CHOICE_MODE_SINGLE); |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 166 | return view; |
| 167 | } |
| 168 | |
Ben Lin | 8e91258 | 2016-09-23 14:25:14 -0700 | [diff] [blame^] | 169 | 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 Sharkey | c6cbdf1 | 2013-08-07 16:22:02 -0700 | [diff] [blame] | 198 | @Override |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 199 | public void onActivityCreated(Bundle savedInstanceState) { |
| 200 | super.onActivityCreated(savedInstanceState); |
| 201 | |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 202 | final BaseActivity activity = getBaseActivity(); |
| 203 | final RootsCache roots = DocumentsApplication.getRootsCache(activity); |
| 204 | final State state = activity.getDisplayState(); |
| 205 | |
Steve McKay | 6d20d19 | 2016-09-21 17:57:10 -0700 | [diff] [blame] | 206 | mActionHandler = activity.getActionHandler(null, null); |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 207 | |
| 208 | mCallbacks = new LoaderCallbacks<Collection<RootInfo>>() { |
| 209 | @Override |
| 210 | public Loader<Collection<RootInfo>> onCreateLoader(int id, Bundle args) { |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 211 | return new RootsLoader(activity, roots, state); |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 212 | } |
| 213 | |
| 214 | @Override |
| 215 | public void onLoadFinished( |
| 216 | Loader<Collection<RootInfo>> loader, Collection<RootInfo> result) { |
Steve McKay | 323dffb | 2016-02-17 18:25:47 -0800 | [diff] [blame] | 217 | if (!isAdded()) { |
| 218 | return; |
| 219 | } |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 220 | |
Steve McKay | 323dffb | 2016-02-17 18:25:47 -0800 | [diff] [blame] | 221 | Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS); |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 222 | |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 223 | List<Item> sortedItems = sortLoadResult(result, handlerAppIntent); |
| 224 | mAdapter = new RootsAdapter(activity, sortedItems, mDragListener); |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 225 | 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 Sharkey | 1c903cc | 2013-09-02 17:19:40 -0700 | [diff] [blame] | 236 | } |
Jeff Sharkey | c6cbdf1 | 2013-08-07 16:22:02 -0700 | [diff] [blame] | 237 | |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 238 | /** |
| 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 Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 310 | @Override |
| 311 | public void onResume() { |
| 312 | super.onResume(); |
Jeff Sharkey | e8d13ea | 2014-08-08 15:10:03 -0700 | [diff] [blame] | 313 | onDisplayStateChanged(); |
| 314 | } |
Jeff Sharkey | 1c903cc | 2013-09-02 17:19:40 -0700 | [diff] [blame] | 315 | |
Jeff Sharkey | e8d13ea | 2014-08-08 15:10:03 -0700 | [diff] [blame] | 316 | public void onDisplayStateChanged() { |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 317 | final Context context = getActivity(); |
Steve McKay | d0a2a2c | 2015-03-25 14:35:33 -0700 | [diff] [blame] | 318 | final State state = ((BaseActivity) context).getDisplayState(); |
Jeff Sharkey | 1c903cc | 2013-09-02 17:19:40 -0700 | [diff] [blame] | 319 | |
Steve McKay | d0a2a2c | 2015-03-25 14:35:33 -0700 | [diff] [blame] | 320 | if (state.action == State.ACTION_GET_CONTENT) { |
Jeff Sharkey | 8f1edfe | 2013-10-23 16:05:56 -0700 | [diff] [blame] | 321 | mList.setOnItemLongClickListener(mItemLongClickListener); |
| 322 | } else { |
| 323 | mList.setOnItemLongClickListener(null); |
| 324 | mList.setLongClickable(false); |
| 325 | } |
| 326 | |
Jeff Sharkey | 8b99704 | 2013-09-19 15:25:56 -0700 | [diff] [blame] | 327 | getLoaderManager().restartLoader(2, null, mCallbacks); |
Jeff Sharkey | 42d2679 | 2013-09-06 13:22:09 -0700 | [diff] [blame] | 328 | } |
| 329 | |
| 330 | public void onCurrentRootChanged() { |
Steve McKay | 323dffb | 2016-02-17 18:25:47 -0800 | [diff] [blame] | 331 | if (mAdapter == null) { |
| 332 | return; |
| 333 | } |
Jeff Sharkey | 42d2679 | 2013-09-06 13:22:09 -0700 | [diff] [blame] | 334 | |
Steve McKay | d0a2a2c | 2015-03-25 14:35:33 -0700 | [diff] [blame] | 335 | final RootInfo root = ((BaseActivity) getActivity()).getCurrentRoot(); |
Jeff Sharkey | 42d2679 | 2013-09-06 13:22:09 -0700 | [diff] [blame] | 336 | for (int i = 0; i < mAdapter.getCount(); i++) { |
| 337 | final Object item = mAdapter.getItem(i); |
Jeff Sharkey | 25f10b3 | 2013-10-07 14:08:17 -0700 | [diff] [blame] | 338 | if (item instanceof RootItem) { |
| 339 | final RootInfo testRoot = ((RootItem) item).root; |
Kenny Root | 16d8598 | 2013-12-13 12:00:26 -0800 | [diff] [blame] | 340 | if (Objects.equals(testRoot, root)) { |
Jeff Sharkey | 25f10b3 | 2013-10-07 14:08:17 -0700 | [diff] [blame] | 341 | mList.setItemChecked(i, true); |
| 342 | return; |
| 343 | } |
Jeff Sharkey | 42d2679 | 2013-09-06 13:22:09 -0700 | [diff] [blame] | 344 | } |
| 345 | } |
Jeff Sharkey | c6cbdf1 | 2013-08-07 16:22:02 -0700 | [diff] [blame] | 346 | } |
| 347 | |
Ben Kwa | 74e5d41 | 2016-02-10 07:46:35 -0800 | [diff] [blame] | 348 | /** |
| 349 | * Attempts to shift focus back to the navigation drawer. |
| 350 | */ |
| 351 | public void requestFocus() { |
| 352 | mList.requestFocus(); |
| 353 | } |
| 354 | |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 355 | private BaseActivity getBaseActivity() { |
| 356 | return (BaseActivity) getActivity(); |
| 357 | } |
| 358 | |
Garfield, Tan | 804133e | 2016-04-20 15:13:56 -0700 | [diff] [blame] | 359 | @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, Tan | a5588b6 | 2016-07-13 09:23:04 -0700 | [diff] [blame] | 370 | public void onViewHovered(View v) { |
Garfield, Tan | 57facaf7 | 2016-05-27 15:02:35 -0700 | [diff] [blame] | 371 | // SpacerView doesn't have DragListener so this view is guaranteed to be a RootItemView. |
Garfield, Tan | a5588b6 | 2016-07-13 09:23:04 -0700 | [diff] [blame] | 372 | RootItemView itemView = (RootItemView) v; |
Garfield, Tan | 57facaf7 | 2016-05-27 15:02:35 -0700 | [diff] [blame] | 373 | itemView.drawRipple(); |
| 374 | |
Garfield, Tan | a5588b6 | 2016-07-13 09:23:04 -0700 | [diff] [blame] | 375 | final int position = (Integer) v.getTag(R.id.item_position_tag); |
Garfield, Tan | 7d66a86 | 2016-05-11 10:28:41 -0700 | [diff] [blame] | 376 | final Item item = mAdapter.getItem(position); |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 377 | item.open(); |
Garfield, Tan | 804133e | 2016-04-20 15:13:56 -0700 | [diff] [blame] | 378 | } |
| 379 | |
| 380 | @Override |
| 381 | public void setDropTargetHighlight(View v, boolean highlight) { |
Garfield, Tan | 57facaf7 | 2016-05-27 15:02:35 -0700 | [diff] [blame] | 382 | // SpacerView doesn't have DragListener so this view is guaranteed to be a RootItemView. |
| 383 | RootItemView itemView = (RootItemView) v; |
| 384 | itemView.setHighlight(highlight); |
Garfield, Tan | 804133e | 2016-04-20 15:13:56 -0700 | [diff] [blame] | 385 | } |
| 386 | |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 387 | @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 Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 393 | |
| 394 | BaseActivity activity = getBaseActivity(); |
| 395 | item.createContextMenu(menu, activity.getMenuInflater(), activity.getMenuManager()); |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 396 | } |
| 397 | |
| 398 | @Override |
| 399 | public boolean onContextItemSelected(MenuItem item) { |
| 400 | AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) item.getMenuInfo(); |
Ben Lin | 035f0c8 | 2016-07-14 10:40:00 -0700 | [diff] [blame] | 401 | // 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 Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 408 | final RootItem rootItem = (RootItem) mAdapter.getItem(adapterMenuInfo.position); |
Ben Lin | 035f0c8 | 2016-07-14 10:40:00 -0700 | [diff] [blame] | 409 | switch (item.getItemId()) { |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 410 | case R.id.menu_eject_root: |
Ben Lin | 821727a | 2016-07-08 10:49:53 -0700 | [diff] [blame] | 411 | final View ejectIcon = adapterMenuInfo.targetView.findViewById(R.id.eject_icon); |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 412 | ejectClicked(ejectIcon, rootItem.root, mActionHandler); |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 413 | return true; |
Garfield Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 414 | case R.id.menu_open_in_new_window: |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 415 | mActionHandler.openInNewWindow(new DocumentStack(rootItem.root)); |
Garfield Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 416 | return true; |
| 417 | case R.id.menu_paste_into_folder: |
| 418 | mActionHandler.pasteIntoFolder(rootItem.root); |
| 419 | return true; |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 420 | case R.id.menu_settings: |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 421 | mActionHandler.openSettings(rootItem.root); |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 422 | return true; |
| 423 | default: |
| 424 | if (DEBUG) Log.d(TAG, "Unhandled menu item selected: " + item); |
| 425 | return false; |
| 426 | } |
| 427 | } |
| 428 | |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 429 | static void ejectClicked(View ejectIcon, RootInfo root, ActionHandler actionHandler) { |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 430 | assert(ejectIcon != null); |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 431 | assert(!root.ejecting); |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 432 | ejectIcon.setEnabled(false); |
| 433 | root.ejecting = true; |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 434 | actionHandler.ejectRoot( |
| 435 | root, |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 436 | new BooleanConsumer() { |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 437 | @Override |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 438 | 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 Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 441 | root.ejecting = false; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 442 | |
| 443 | // If the view is still visible, we update its state. |
| 444 | if (ejectIcon.getVisibility() == View.VISIBLE) { |
| 445 | ejectIcon.setEnabled(!ejected); |
| 446 | } |
Ben Lin | 5668b86 | 2016-06-28 17:12:52 -0700 | [diff] [blame] | 447 | } |
| 448 | }); |
| 449 | } |
| 450 | |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 451 | private static class RootComparator implements Comparator<RootItem> { |
Jeff Sharkey | b6a7f2c | 2013-08-27 18:26:48 -0700 | [diff] [blame] | 452 | @Override |
Ben Kwa | 85c8cdd | 2015-10-09 07:48:00 -0700 | [diff] [blame] | 453 | public int compare(RootItem lhs, RootItem rhs) { |
Steve McKay | 55c00e7 | 2016-02-18 15:32:16 -0800 | [diff] [blame] | 454 | return lhs.root.compareTo(rhs.root); |
Jeff Sharkey | b6a7f2c | 2013-08-27 18:26:48 -0700 | [diff] [blame] | 455 | } |
| 456 | } |
Jeff Sharkey | 7611221 | 2013-08-06 11:26:10 -0700 | [diff] [blame] | 457 | } |