FolderListFragment.java revision fa50e6f9f3ab9105d7a3f67e2be85f5a1fea9d85
1/* 2 * Copyright (C) 2012 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package com.android.mail.ui; 19 20import android.app.Activity; 21import android.app.ListFragment; 22import android.app.LoaderManager; 23import android.content.CursorLoader; 24import android.content.Loader; 25import android.database.Cursor; 26import android.database.DataSetObserver; 27import android.net.Uri; 28import android.os.Bundle; 29import android.view.LayoutInflater; 30import android.view.View; 31import android.view.ViewGroup; 32import android.widget.ArrayAdapter; 33import android.widget.BaseAdapter; 34import android.widget.ImageView; 35import android.widget.ListAdapter; 36import android.widget.ListView; 37import android.widget.TextView; 38 39import com.android.mail.R; 40import com.android.mail.providers.Folder; 41import com.android.mail.providers.RecentFolderObserver; 42import com.android.mail.providers.UIProvider; 43import com.android.mail.providers.UIProvider.FolderType; 44import com.android.mail.utils.LogTag; 45import com.android.mail.utils.LogUtils; 46import com.android.mail.utils.Utils; 47 48import java.util.ArrayList; 49import java.util.Iterator; 50import java.util.List; 51 52/** 53 * The folder list UI component. 54 */ 55public final class FolderListFragment extends ListFragment implements 56 LoaderManager.LoaderCallbacks<Cursor> { 57 private static final String LOG_TAG = LogTag.getLogTag(); 58 /** The parent activity */ 59 private ControllableActivity mActivity; 60 /** The underlying list view */ 61 private ListView mListView; 62 /** URI that points to the list of folders for the current account. */ 63 private Uri mFolderListUri; 64 /** True if you want a sectioned FolderList, false otherwise. */ 65 private boolean mIsSectioned; 66 /** An {@link ArrayList} of {@link FolderType}s to exclude from displaying. */ 67 private ArrayList<Integer> mExcludedFolderTypes; 68 /** Callback into the parent */ 69 private FolderListSelectionListener mListener; 70 71 /** The currently selected folder (the folder being viewed). This is never null. */ 72 private Uri mSelectedFolderUri = Uri.EMPTY; 73 /** Parent of the current folder, or null if the current folder is not a child. */ 74 private Folder mParentFolder; 75 76 private static final int FOLDER_LOADER_ID = 0; 77 public static final int MODE_DEFAULT = 0; 78 public static final int MODE_PICK = 1; 79 /** Key to store {@link #mParentFolder}. */ 80 private static final String ARG_PARENT_FOLDER = "arg-parent-folder"; 81 /** Key to store {@link #mFolderListUri}. */ 82 private static final String ARG_FOLDER_URI = "arg-folder-list-uri"; 83 /** Key to store {@link #mIsSectioned} */ 84 private static final String ARG_IS_SECTIONED = "arg-is-sectioned"; 85 /** Key to store {@link #mExcludedFolderTypes} */ 86 private static final String ARG_EXCLUDED_FOLDER_TYPES = "arg-excluded-folder-types"; 87 88 private static final String BUNDLE_LIST_STATE = "flf-list-state"; 89 private static final String BUNDLE_SELECTED_FOLDER = "flf-selected-folder"; 90 private static final String BUNDLE_SELECTED_TYPE = "flf-selected-type"; 91 92 private FolderListFragmentCursorAdapter mCursorAdapter; 93 /** View that we show while we are waiting for the folder list to load */ 94 private View mEmptyView; 95 /** Observer to wait for changes to the current folder so we can change the selected folder */ 96 private FolderObserver mFolderObserver = null; 97 /** 98 * Type of currently selected folder: {@link FolderListAdapter.Item#FOLDER_SYSTEM}, 99 * {@link FolderListAdapter.Item#FOLDER_RECENT} or {@link FolderListAdapter.Item#FOLDER_USER}. 100 */ 101 // Setting to NOT_A_FOLDER = leaving uninitialized. 102 private int mSelectedFolderType = FolderListAdapter.Item.NOT_A_FOLDER; 103 104 /** 105 * Listens to folder changes from the controller and updates state accordingly. 106 */ 107 private final class FolderObserver extends DataSetObserver { 108 @Override 109 public void onChanged() { 110 if (mActivity == null) { 111 return; 112 } 113 final FolderController controller = mActivity.getFolderController(); 114 if (controller == null) { 115 return; 116 } 117 setSelectedFolder(controller.getFolder()); 118 } 119 } 120 121 /** 122 * Constructor needs to be public to handle orientation changes and activity lifecycle events. 123 */ 124 public FolderListFragment() { 125 super(); 126 } 127 128 @Override 129 public void onResume() { 130 Utils.dumpLayoutRequests("FLF(" + this + ").onResume()", getView()); 131 132 super.onResume(); 133 // Hacky workaround for http://b/6946182 134 Utils.fixSubTreeLayoutIfOrphaned(getView(), "FolderListFragment"); 135 } 136 /** 137 * Creates a new instance of {@link ConversationListFragment}, initialized 138 * to display conversation list context. 139 * @param isSectioned TODO(viki): 140 */ 141 public static FolderListFragment newInstance(Folder parentFolder, Uri folderUri, 142 boolean isSectioned) { 143 return newInstance(parentFolder, folderUri, isSectioned, null); 144 } 145 146 /** 147 * Creates a new instance of {@link ConversationListFragment}, initialized 148 * to display conversation list context. 149 * @param isSectioned TODO(viki): 150 * @param excludedFolderTypes A list of {@link FolderType}s to exclude from displaying 151 */ 152 public static FolderListFragment newInstance(Folder parentFolder, Uri folderUri, 153 boolean isSectioned, final ArrayList<Integer> excludedFolderTypes) { 154 final FolderListFragment fragment = new FolderListFragment(); 155 final Bundle args = new Bundle(); 156 if (parentFolder != null) { 157 args.putParcelable(ARG_PARENT_FOLDER, parentFolder); 158 } 159 args.putString(ARG_FOLDER_URI, folderUri.toString()); 160 args.putBoolean(ARG_IS_SECTIONED, isSectioned); 161 if (excludedFolderTypes != null) { 162 args.putIntegerArrayList(ARG_EXCLUDED_FOLDER_TYPES, excludedFolderTypes); 163 } 164 fragment.setArguments(args); 165 return fragment; 166 } 167 168 @Override 169 public void onActivityCreated(Bundle savedState) { 170 super.onActivityCreated(savedState); 171 // Strictly speaking, we get back an android.app.Activity from getActivity. However, the 172 // only activity creating a ConversationListContext is a MailActivity which is of type 173 // ControllableActivity, so this cast should be safe. If this cast fails, some other 174 // activity is creating ConversationListFragments. This activity must be of type 175 // ControllableActivity. 176 final Activity activity = getActivity(); 177 if (! (activity instanceof ControllableActivity)){ 178 LogUtils.wtf(LOG_TAG, "FolderListFragment expects only a ControllableActivity to" + 179 "create it. Cannot proceed."); 180 } 181 mActivity = (ControllableActivity) activity; 182 final FolderController controller = mActivity.getFolderController(); 183 // Listen to folder changes in the future 184 mFolderObserver = new FolderObserver(); 185 if (controller != null) { 186 // Only register for selected folder updates if we have a controller. 187 controller.registerFolderObserver(mFolderObserver); 188 } 189 190 mListener = mActivity.getFolderListSelectionListener(); 191 if (mActivity.isFinishing()) { 192 // Activity is finishing, just bail. 193 return; 194 } 195 196 final Folder selectedFolder; 197 if (mParentFolder != null) { 198 mCursorAdapter = new HierarchicalFolderListAdapter(null, mParentFolder); 199 selectedFolder = mActivity.getHierarchyFolder(); 200 } else { 201 mCursorAdapter = new FolderListAdapter(R.layout.folder_item, mIsSectioned); 202 selectedFolder = controller == null ? null : controller.getFolder(); 203 } 204 // Is the selected folder fresher than the one we have restored from a bundle? 205 if (selectedFolder != null && !selectedFolder.uri.equals(mSelectedFolderUri)) { 206 setSelectedFolder(selectedFolder); 207 } 208 setListAdapter(mCursorAdapter); 209 // Set the region which gets highlighted since it might not have been set till now. 210 getLoaderManager().initLoader(FOLDER_LOADER_ID, Bundle.EMPTY, this); 211 } 212 213 @Override 214 public View onCreateView(LayoutInflater inflater, ViewGroup container, 215 Bundle savedState) { 216 final Bundle args = getArguments(); 217 mFolderListUri = Uri.parse(args.getString(ARG_FOLDER_URI)); 218 mParentFolder = (Folder) args.getParcelable(ARG_PARENT_FOLDER); 219 mIsSectioned = args.getBoolean(ARG_IS_SECTIONED); 220 mExcludedFolderTypes = args.getIntegerArrayList(ARG_EXCLUDED_FOLDER_TYPES); 221 final View rootView = inflater.inflate(R.layout.folder_list, null); 222 mListView = (ListView) rootView.findViewById(android.R.id.list); 223 mListView.setHeaderDividersEnabled(false); 224 mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 225 mListView.setEmptyView(null); 226 if (savedState != null && savedState.containsKey(BUNDLE_LIST_STATE)) { 227 mListView.onRestoreInstanceState(savedState.getParcelable(BUNDLE_LIST_STATE)); 228 } 229 mEmptyView = rootView.findViewById(R.id.empty_view); 230 if (savedState != null && savedState.containsKey(BUNDLE_SELECTED_FOLDER)) { 231 mSelectedFolderUri = Uri.parse(savedState.getString(BUNDLE_SELECTED_FOLDER)); 232 mSelectedFolderType = savedState.getInt(BUNDLE_SELECTED_TYPE); 233 } else if (mParentFolder != null) { 234 mSelectedFolderUri = mParentFolder.uri; 235 // No selected folder type required for hierarchical lists. 236 } 237 Utils.dumpLayoutRequests("FLF(" + this + ").onCreateView()", rootView); 238 239 return rootView; 240 } 241 242 @Override 243 public void onStart() { 244 Utils.dumpLayoutRequests("FLF(" + this + ").onStart()", getView()); 245 super.onStart(); 246 } 247 248 @Override 249 public void onStop() { 250 Utils.dumpLayoutRequests("FLF(" + this + ").onStop()", getView()); 251 super.onStop(); 252 } 253 254 @Override 255 public void onPause() { 256 Utils.dumpLayoutRequests("FLF(" + this + ").onPause()", getView()); 257 super.onPause(); 258 } 259 260 @Override 261 public void onSaveInstanceState(Bundle outState) { 262 super.onSaveInstanceState(outState); 263 if (mListView != null) { 264 outState.putParcelable(BUNDLE_LIST_STATE, mListView.onSaveInstanceState()); 265 } 266 if (mSelectedFolderUri != null) { 267 outState.putString(BUNDLE_SELECTED_FOLDER, mSelectedFolderUri.toString()); 268 } 269 outState.putInt(BUNDLE_SELECTED_TYPE, mSelectedFolderType); 270 } 271 272 @Override 273 public void onDestroyView() { 274 Utils.dumpLayoutRequests("FLF(" + this + ").onDestoryView()", getView()); 275 if (mCursorAdapter != null) { 276 mCursorAdapter.destroy(); 277 } 278 // Clear the adapter. 279 setListAdapter(null); 280 if (mFolderObserver != null) { 281 FolderController controller = mActivity.getFolderController(); 282 if (controller != null) { 283 controller.unregisterFolderObserver(mFolderObserver); 284 mFolderObserver = null; 285 } 286 } 287 super.onDestroyView(); 288 } 289 290 @Override 291 public void onListItemClick(ListView l, View v, int position, long id) { 292 viewFolder(position); 293 } 294 295 /** 296 * Display the conversation list from the folder at the position given. 297 * @param position 298 */ 299 private void viewFolder(int position) { 300 final Object item = getListAdapter().getItem(position); 301 final Folder folder; 302 if (item instanceof FolderListAdapter.Item) { 303 final FolderListAdapter.Item folderItem = (FolderListAdapter.Item) item; 304 folder = folderItem.mFolder; 305 mSelectedFolderType = folderItem.mFolderType; 306 } else if (item instanceof Folder) { 307 folder = (Folder) item; 308 } else { 309 folder = new Folder((Cursor) item); 310 } 311 if (folder != null) { 312 // Since we may be looking at hierarchical views, if we can 313 // determine the parent of the folder we have tapped, set it here. 314 // If we are looking at the folder we are already viewing, don't 315 // update its parent! 316 folder.parent = folder.equals(mParentFolder) ? null : mParentFolder; 317 // Go to the conversation list for this folder. 318 mListener.onFolderSelected(folder); 319 } 320 } 321 322 @Override 323 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 324 mListView.setEmptyView(null); 325 mEmptyView.setVisibility(View.GONE); 326 return new CursorLoader(mActivity.getActivityContext(), mFolderListUri, 327 UIProvider.FOLDERS_PROJECTION, null, null, null); 328 } 329 330 @Override 331 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 332 mCursorAdapter.setCursor(data); 333 if (data == null || data.getCount() == 0) { 334 mEmptyView.setVisibility(View.VISIBLE); 335 mListView.setEmptyView(mEmptyView); 336 } 337 } 338 339 @Override 340 public void onLoaderReset(Loader<Cursor> loader) { 341 mCursorAdapter.setCursor(null); 342 } 343 344 /** 345 * Interface for all cursor adapters that allow setting a cursor and being destroyed. 346 */ 347 private interface FolderListFragmentCursorAdapter extends ListAdapter { 348 /** Update the folder list cursor with the cursor given here. */ 349 void setCursor(Cursor cursor); 350 /** Remove all observers and destroy the object. */ 351 void destroy(); 352 /** Notifies the adapter that the data has changed. */ 353 void notifyDataSetChanged(); 354 } 355 356 /** 357 * An adapter for flat folder lists. 358 */ 359 private class FolderListAdapter extends BaseAdapter implements FolderListFragmentCursorAdapter { 360 361 private final RecentFolderObserver mRecentFolderObserver = new RecentFolderObserver() { 362 @Override 363 public void onChanged() { 364 recalculateList(); 365 } 366 }; 367 368 private final RecentFolderList mRecentFolders; 369 /** True if the list is sectioned, false otherwise */ 370 private final boolean mIsSectioned; 371 private final LayoutInflater mInflater; 372 /** All the items */ 373 private final List<Item> mItemList = new ArrayList<Item>(); 374 /** Cursor into the folder list. This might be null. */ 375 private Cursor mCursor = null; 376 377 /** A union of either a folder or a resource string */ 378 private class Item { 379 public final Folder mFolder; 380 public final int mResource; 381 /** Either {@link #VIEW_FOLDER} or {@link #VIEW_HEADER} */ 382 public final int mType; 383 /** A normal folder, also a child, if a parent is specified. */ 384 private static final int VIEW_FOLDER = 0; 385 /** A text-label which serves as a header in sectioned lists. */ 386 private static final int VIEW_HEADER = 1; 387 388 /** 389 * Either {@link #FOLDER_SYSTEM}, {@link #FOLDER_RECENT} or {@link #FOLDER_USER} when 390 * {@link #mType} is {@link #VIEW_FOLDER}, and {@link #NOT_A_FOLDER} otherwise. 391 */ 392 public final int mFolderType; 393 private static final int NOT_A_FOLDER = 0; 394 private static final int FOLDER_SYSTEM = 1; 395 private static final int FOLDER_RECENT = 2; 396 private static final int FOLDER_USER = 3; 397 398 /** 399 * Create a folder item with the given type. 400 * @param folder 401 * @param folderType one of {@link #FOLDER_SYSTEM}, {@link #FOLDER_RECENT} or 402 * {@link #FOLDER_USER} 403 */ 404 private Item(Folder folder, int folderType) { 405 mFolder = folder; 406 mResource = -1; 407 mType = VIEW_FOLDER; 408 mFolderType = folderType; 409 } 410 /** 411 * Create a header item with a string resource. 412 * @param resource the string resource: R.string.all_folders_heading 413 */ 414 private Item(int resource) { 415 mFolder = null; 416 mResource = resource; 417 mType = VIEW_HEADER; 418 mFolderType = NOT_A_FOLDER; 419 } 420 421 private final View getView(int position, View convertView, ViewGroup parent) { 422 if (mType == VIEW_FOLDER) { 423 return getFolderView(position, convertView, parent); 424 } else { 425 return getHeaderView(position, convertView, parent); 426 } 427 } 428 429 /** 430 * Returns a text divider between sections. 431 * @param convertView 432 * @param parent 433 * @return a text header at the given position. 434 */ 435 private final View getHeaderView(int position, View convertView, ViewGroup parent) { 436 final TextView headerView; 437 if (convertView != null) { 438 headerView = (TextView) convertView; 439 } else { 440 headerView = (TextView) mInflater.inflate( 441 R.layout.folder_list_header, parent, false); 442 } 443 headerView.setText(mResource); 444 return headerView; 445 } 446 447 /** 448 * Return a folder: either a parent folder or a normal (child or flat) 449 * folder. 450 * @param position 451 * @param convertView 452 * @param parent 453 * @return a view showing a folder at the given position. 454 */ 455 private final View getFolderView(int position, View convertView, ViewGroup parent) { 456 final FolderItemView folderItemView; 457 if (convertView != null) { 458 folderItemView = (FolderItemView) convertView; 459 } else { 460 folderItemView = 461 (FolderItemView) mInflater.inflate(R.layout.folder_item, null, false); 462 } 463 folderItemView.bind(mFolder, mActivity); 464 if (mListView != null) { 465 final boolean isSelected = (mFolderType == mSelectedFolderType) 466 && mFolder.uri.equals(mSelectedFolderUri); 467 mListView.setItemChecked(position, isSelected); 468 } 469 Folder.setFolderBlockColor(mFolder, folderItemView.findViewById(R.id.color_block)); 470 Folder.setIcon(mFolder, (ImageView) folderItemView.findViewById(R.id.folder_box)); 471 return folderItemView; 472 } 473 } 474 475 /** 476 * Creates a {@link FolderListAdapter}.This is a flat folder list of all the folders for the 477 * given account. 478 * @param layout 479 * @param isSectioned TODO(viki): 480 */ 481 public FolderListAdapter(int layout, boolean isSectioned) { 482 super(); 483 mInflater = LayoutInflater.from(mActivity.getActivityContext()); 484 mIsSectioned = isSectioned; 485 final RecentFolderController controller = mActivity.getRecentFolderController(); 486 if (controller != null && mIsSectioned) { 487 mRecentFolders = mRecentFolderObserver.initialize(controller); 488 } else { 489 mRecentFolders = null; 490 } 491 } 492 493 @Override 494 public View getView(int position, View convertView, ViewGroup parent) { 495 return ((Item) getItem(position)).getView(position, convertView, parent); 496 } 497 498 @Override 499 public int getViewTypeCount() { 500 // Headers and folders 501 return 2; 502 } 503 504 @Override 505 public int getItemViewType(int position) { 506 return ((Item) getItem(position)).mType; 507 } 508 509 @Override 510 public int getCount() { 511 return mItemList.size(); 512 } 513 514 @Override 515 public boolean isEnabled(int position) { 516 // We disallow taps on headers 517 return ((Item) getItem(position)).mType != Item.VIEW_HEADER; 518 } 519 520 @Override 521 public boolean areAllItemsEnabled() { 522 // The headers are not enabled. 523 return false; 524 } 525 526 /** 527 * Returns all the recent folders from the list given here. Safe to call with a null list. 528 * @param recentList 529 * @return a valid list of folders, which are all recent folders. 530 */ 531 private final List<Folder> getRecentFolders(RecentFolderList recentList) { 532 final List<Folder> folderList = new ArrayList<Folder>(); 533 if (recentList == null) { 534 return folderList; 535 } 536 // Get all recent folders, after removing system folders. 537 for (final Folder f : recentList.getRecentFolderList(null)) { 538 if (!f.isProviderFolder()) { 539 folderList.add(f); 540 } 541 } 542 return folderList; 543 } 544 545 /** 546 * Recalculates the system, recent and user label lists. Notifies that the data has changed. 547 * This method modifies all the three lists on every single invocation. 548 */ 549 private void recalculateList() { 550 if (mCursor == null || mCursor.getCount() <= 0 || !mCursor.moveToFirst()) { 551 return; 552 } 553 mItemList.clear(); 554 if (!mIsSectioned) { 555 // Adapter for a flat list. Everything is a FOLDER_USER, and there are no headers. 556 do { 557 final Folder f = new Folder(mCursor); 558 if (mExcludedFolderTypes == null || !mExcludedFolderTypes.contains(f.type)) { 559 mItemList.add(new Item(f, Item.FOLDER_USER)); 560 } 561 } while (mCursor.moveToNext()); 562 // Ask the list to invalidate its views. 563 notifyDataSetChanged(); 564 return; 565 } 566 567 // Otherwise, this is an adapter for a sectioned list. 568 // First add all the system folders. 569 final List<Folder> userFolderList = new ArrayList<Folder>(); 570 do { 571 final Folder f = new Folder(mCursor); 572 if (mExcludedFolderTypes == null || !mExcludedFolderTypes.contains(f.type)) { 573 if (f.isProviderFolder()) { 574 mItemList.add(new Item(f, Item.FOLDER_SYSTEM)); 575 } else { 576 userFolderList.add(f); 577 } 578 } 579 } while (mCursor.moveToNext()); 580 // If there are recent folders, add them and a header. 581 final List<Folder> recentFolderList = getRecentFolders(mRecentFolders); 582 583 // Remove any excluded folder types 584 if (mExcludedFolderTypes != null) { 585 final Iterator<Folder> iterator = recentFolderList.iterator(); 586 while (iterator.hasNext()) { 587 if (mExcludedFolderTypes.contains(iterator.next().type)) { 588 iterator.remove(); 589 } 590 } 591 } 592 593 if (recentFolderList.size() > 0) { 594 mItemList.add(new Item(R.string.recent_folders_heading)); 595 for (Folder f : recentFolderList) { 596 mItemList.add(new Item(f, Item.FOLDER_RECENT)); 597 } 598 } 599 // If there are user folders, add them and a header. 600 if (userFolderList.size() > 0) { 601 mItemList.add(new Item(R.string.all_folders_heading)); 602 for (final Folder f : userFolderList) { 603 mItemList.add(new Item(f, Item.FOLDER_USER)); 604 } 605 } 606 // Ask the list to invalidate its views. 607 notifyDataSetChanged(); 608 } 609 610 @Override 611 public void setCursor(Cursor cursor) { 612 mCursor = cursor; 613 recalculateList(); 614 } 615 616 @Override 617 public Object getItem(int position) { 618 return mItemList.get(position); 619 } 620 621 @Override 622 public long getItemId(int position) { 623 return getItem(position).hashCode(); 624 } 625 626 @Override 627 public final void destroy() { 628 mRecentFolderObserver.unregisterAndDestroy(); 629 } 630 } 631 632 private class HierarchicalFolderListAdapter extends ArrayAdapter<Folder> 633 implements FolderListFragmentCursorAdapter{ 634 635 private static final int PARENT = 0; 636 private static final int CHILD = 1; 637 private final Uri mParentUri; 638 private final Folder mParent; 639 private final FolderItemView.DropHandler mDropHandler; 640 641 public HierarchicalFolderListAdapter(Cursor c, Folder parentFolder) { 642 super(mActivity.getActivityContext(), R.layout.folder_item); 643 mDropHandler = mActivity; 644 mParent = parentFolder; 645 mParentUri = parentFolder.uri; 646 setCursor(c); 647 } 648 649 @Override 650 public int getViewTypeCount() { 651 // Child and Parent 652 return 2; 653 } 654 655 @Override 656 public int getItemViewType(int position) { 657 Folder f = getItem(position); 658 return f.uri.equals(mParentUri) ? PARENT : CHILD; 659 } 660 661 @Override 662 public View getView(int position, View convertView, ViewGroup parent) { 663 FolderItemView folderItemView; 664 Folder folder = getItem(position); 665 boolean isParent = folder.uri.equals(mParentUri); 666 if (convertView != null) { 667 folderItemView = (FolderItemView) convertView; 668 } else { 669 int resId = isParent ? R.layout.folder_item : R.layout.child_folder_item; 670 folderItemView = (FolderItemView) LayoutInflater.from( 671 mActivity.getActivityContext()).inflate(resId, null); 672 } 673 folderItemView.bind(folder, mDropHandler); 674 if (folder.uri.equals(mSelectedFolderUri)) { 675 getListView().setItemChecked(position, true); 676 } 677 Folder.setFolderBlockColor(folder, folderItemView.findViewById(R.id.folder_box)); 678 return folderItemView; 679 } 680 681 @Override 682 public void setCursor(Cursor cursor) { 683 clear(); 684 if (mParent != null) { 685 add(mParent); 686 } 687 if (cursor != null && cursor.getCount() > 0) { 688 cursor.moveToFirst(); 689 do { 690 Folder f = new Folder(cursor); 691 f.parent = mParent; 692 add(f); 693 } while (cursor.moveToNext()); 694 } 695 } 696 697 @Override 698 public void destroy() { 699 // Do nothing. 700 } 701 } 702 703 /** 704 * Sets the currently selected folder safely. 705 * @param folder 706 */ 707 private void setSelectedFolder(Folder folder) { 708 if (folder == null) { 709 mSelectedFolderUri = Uri.EMPTY; 710 return; 711 } 712 mSelectedFolderUri = folder.uri; 713 setSelectedFolderType(folder); 714 if (mCursorAdapter != null) { 715 mCursorAdapter.notifyDataSetChanged(); 716 } 717 } 718 719 /** 720 * Sets the selected folder type safely. 721 * @param folder 722 */ 723 private void setSelectedFolderType(Folder folder) { 724 // If it is set already, assume it is correct. 725 if (mSelectedFolderType != FolderListAdapter.Item.NOT_A_FOLDER) { 726 return; 727 } 728 mSelectedFolderType = folder.isProviderFolder() ? FolderListAdapter.Item.FOLDER_SYSTEM 729 : FolderListAdapter.Item.FOLDER_USER; 730 } 731 732 public interface FolderListSelectionListener { 733 public void onFolderSelected(Folder folder); 734 } 735 736 /** 737 * Get whether the FolderListFragment is currently showing the hierarchy 738 * under a single parent. 739 */ 740 public boolean showingHierarchy() { 741 return mParentFolder != null; 742 } 743} 744