FolderListFragment.java revision 11e3596e8c5978d07195ae2d7c8a96bb51aa75b3
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.Context; 24import android.content.CursorLoader; 25import android.content.Loader; 26import android.database.Cursor; 27import android.net.Uri; 28import android.os.Bundle; 29import android.view.LayoutInflater; 30import android.view.View; 31import android.view.View.OnClickListener; 32import android.view.ViewGroup; 33import android.widget.ArrayAdapter; 34import android.widget.ListView; 35import android.widget.SimpleCursorAdapter; 36import android.widget.TextView; 37 38import com.android.mail.R; 39import com.android.mail.providers.Folder; 40import com.android.mail.providers.UIProvider; 41import com.android.mail.utils.LogUtils; 42 43/** 44 * The folder list UI component. 45 */ 46public final class FolderListFragment extends ListFragment implements 47 LoaderManager.LoaderCallbacks<Cursor>, ViewMode.ModeChangeListener { 48 private static final String LOG_TAG = new LogUtils().getLogTag(); 49 50 private ControllableActivity mActivity; 51 52 // Control state. 53 private Cursor mFolderListCursor; 54 55 // The internal view objects. 56 private ListView mListView; 57 58 private Uri mFolderListUri; 59 60 private FolderListSelectionListener mListener; 61 62 private Folder mSelectedFolder; 63 64 private Folder mParentFolder; 65 66 private static final int FOLDER_LOADER_ID = 0; 67 public static final int MODE_DEFAULT = 0; 68 public static final int MODE_PICK = 1; 69 private static final String ARG_PARENT_FOLDER = "arg-parent-folder"; 70 private static final String ARG_FOLDER_URI = "arg-folder-list-uri"; 71 private FolderItemView.DropHandler mDropHandler; 72 73 /** 74 * Constructor needs to be public to handle orientation changes and activity lifecycle events. 75 * @param listener 76 */ 77 public FolderListFragment() { 78 super(); 79 } 80 81 /** 82 * Creates a new instance of {@link ConversationListFragment}, initialized 83 * to display conversation list context. 84 */ 85 public static FolderListFragment newInstance(Folder parentFolder, Uri folderUri) { 86 FolderListFragment fragment = new FolderListFragment(); 87 Bundle args = new Bundle(); 88 if (parentFolder != null) { 89 args.putParcelable(ARG_PARENT_FOLDER, parentFolder); 90 } 91 args.putString(ARG_FOLDER_URI, folderUri.toString()); 92 fragment.setArguments(args); 93 return fragment; 94 } 95 96 @Override 97 public void onActivityCreated(Bundle savedState) { 98 super.onActivityCreated(savedState); 99 // Strictly speaking, we get back an android.app.Activity from getActivity. However, the 100 // only activity creating a ConversationListContext is a MailActivity which is of type 101 // ControllableActivity, so this cast should be safe. If this cast fails, some other 102 // activity is creating ConversationListFragments. This activity must be of type 103 // ControllableActivity. 104 final Activity activity = getActivity(); 105 if (! (activity instanceof ControllableActivity)){ 106 LogUtils.wtf(LOG_TAG, "FolderListFragment expects only a ControllableActivity to" + 107 "create it. Cannot proceed."); 108 } 109 mActivity = (ControllableActivity) activity; 110 if (activity instanceof FolderItemView.DropHandler) { 111 mDropHandler = (FolderItemView.DropHandler) activity; 112 } 113 mListener = mActivity.getFolderListSelectionListener(); 114 if (mActivity.isFinishing()) { 115 // Activity is finishing, just bail. 116 return; 117 } 118 selectInitialFolder(mActivity.getCurrentFolder()); 119 getLoaderManager().initLoader(FOLDER_LOADER_ID, Bundle.EMPTY, this); 120 } 121 122 @Override 123 public void onCreate(Bundle savedState) { 124 super.onCreate(savedState); 125 } 126 127 @Override 128 public View onCreateView(LayoutInflater inflater, ViewGroup container, 129 Bundle savedInstanceState) { 130 final Bundle args = getArguments(); 131 mFolderListUri = Uri.parse(args.getString(ARG_FOLDER_URI)); 132 mParentFolder = (Folder) args.getParcelable(ARG_PARENT_FOLDER); 133 View rootView = inflater.inflate(R.layout.folder_list, null); 134 mListView = (ListView) rootView.findViewById(android.R.id.list); 135 mListView.setHeaderDividersEnabled(false); 136 mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 137 mListView.setEmptyView(null); 138 // Note - we manually save/restore the listview state. 139 mListView.setSaveEnabled(false); 140 141 if (mParentFolder != null) { 142 mSelectedFolder = mParentFolder; 143 } 144 return rootView; 145 } 146 147 @Override 148 public void onDestroyView() { 149 // Clear the adapter. 150 setListAdapter(null); 151 super.onDestroyView(); 152 } 153 154 @Override 155 public void onListItemClick(ListView l, View v, int position, long id) { 156 viewFolder(position); 157 } 158 159 public void viewFolder(int position) { 160 Object item = getListAdapter().getItem(position); 161 Folder folder; 162 if (item instanceof Folder) { 163 folder = (Folder) item; 164 } else { 165 folder = new Folder((Cursor) item); 166 } 167 // Since we may be looking at hierarchical views, if we can determine 168 // the parent of the folder we have tapped, set it here. 169 // If we are looking at the folder we are already viewing, don't update 170 // its parent! 171 folder.parent = folder.equals(mParentFolder) ? null : mParentFolder; 172 // Go to the conversation list for this folder. 173 mListener.onFolderSelected(folder, mParentFolder != null); 174 } 175 176 @Override 177 public void onPause() { 178 super.onPause(); 179 } 180 181 @Override 182 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 183 return new CursorLoader(mActivity.getActivityContext(), mFolderListUri, 184 UIProvider.FOLDERS_PROJECTION, null, null, null); 185 } 186 187 @Override 188 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 189 mFolderListCursor = data; 190 if (mParentFolder != null) { 191 setListAdapter(new HierarchicalFolderListAdapter(mActivity.getActivityContext(), 192 mFolderListCursor, mParentFolder)); 193 } else { 194 setListAdapter(new FolderListAdapter(mActivity.getActivityContext(), 195 R.layout.folder_item, mFolderListCursor, null, null)); 196 } 197 } 198 199 @Override 200 public void onLoaderReset(Loader<Cursor> loader) { 201 // Do nothing. 202 } 203 204 private class FolderListAdapter extends SimpleCursorAdapter { 205 206 public FolderListAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { 207 super(context, layout, c, new String[0], new int[0], 0); 208 } 209 210 @Override 211 public View getView(int position, View convertView, ViewGroup parent) { 212 FolderItemView folderItemView; 213 if (convertView != null) { 214 folderItemView = (FolderItemView) convertView; 215 } else { 216 folderItemView = (FolderItemView) LayoutInflater.from( 217 mActivity.getActivityContext()).inflate(R.layout.folder_item, null); 218 } 219 getCursor().moveToPosition(position); 220 Folder folder = new Folder(getCursor()); 221 folderItemView.bind(folder, mDropHandler, true); 222 if (mSelectedFolder != null && folder.uri.equals(mSelectedFolder.uri)) { 223 getListView().setItemChecked(position, true); 224 } 225 Folder.setFolderBlockColor(folder, folderItemView.findViewById(R.id.folder_box)); 226 return folderItemView; 227 } 228 } 229 230 private class HierarchicalFolderListAdapter extends ArrayAdapter<Folder> { 231 232 private static final int PARENT = 0; 233 private static final int CHILD = 1; 234 private final Uri mParentUri; 235 236 public HierarchicalFolderListAdapter(Context context, Cursor c, Folder parentFolder) { 237 super(context, R.layout.folder_item); 238 mParentUri = parentFolder.uri; 239 add(parentFolder); 240 if (c != null && c.getCount() > 0) { 241 c.moveToFirst(); 242 do { 243 add(new Folder(c)); 244 } while (c.moveToNext()); 245 } 246 } 247 248 @Override 249 public int getViewTypeCount() { 250 // Child and Parent 251 return 2; 252 } 253 254 public int getItemViewType(int position) { 255 Folder f = this.getItem(position); 256 return f.uri.equals(mParentUri) ? PARENT : CHILD; 257 } 258 259 @Override 260 public View getView(int position, View convertView, ViewGroup parent) { 261 FolderItemView folderItemView; 262 Folder folder = getItem(position); 263 boolean isParent = folder.uri.equals(mParentUri); 264 if (convertView != null) { 265 folderItemView = (FolderItemView) convertView; 266 } else { 267 int resId = isParent ? R.layout.folder_item : R.layout.child_folder_item; 268 folderItemView = (FolderItemView) LayoutInflater.from( 269 mActivity.getActivityContext()).inflate(resId, null); 270 } 271 folderItemView.bind(folder, mDropHandler, false); 272 if (mSelectedFolder != null && folder.uri.equals(mSelectedFolder.uri)) { 273 getListView().setItemChecked(position, true); 274 } 275 Folder.setFolderBlockColor(folder, folderItemView.findViewById(R.id.folder_box)); 276 return folderItemView; 277 } 278 } 279 280 @Override 281 public void onViewModeChanged(int newMode) { 282 // Listen on mode changes, when we move to Folder list mode, change accordingly. 283 } 284 285 public void selectInitialFolder(Folder folder) { 286 mSelectedFolder = folder; 287 } 288 289 public interface FolderListSelectionListener { 290 public void onFolderSelected(Folder folder, boolean viewingChildren); 291 } 292 293 /** 294 * Get whether the FolderListFragment is currently showing the hierarchy 295 * under a single parent. 296 */ 297 public boolean showingHierarchy() { 298 return mParentFolder != null; 299 } 300}