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}