1e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira/*******************************************************************************
2e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *      Copyright (C) 2012 Google Inc.
3e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *      Licensed to The Android Open Source Project.
4e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *
5e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *      Licensed under the Apache License, Version 2.0 (the "License");
6e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *      you may not use this file except in compliance with the License.
7e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *      You may obtain a copy of the License at
8e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *
9e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *           http://www.apache.org/licenses/LICENSE-2.0
10e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *
11e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *      Unless required by applicable law or agreed to in writing, software
12e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *      distributed under the License is distributed on an "AS IS" BASIS,
13e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *      See the License for the specific language governing permissions and
15e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *      limitations under the License.
16e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira *******************************************************************************/
17e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
18e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereirapackage com.android.mail.ui;
19e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
20e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereiraimport com.android.mail.R;
21e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereiraimport com.android.mail.providers.Folder;
22a89424729fe63b635e64037a989958a49fb70cffPaul Westbrookimport com.android.mail.providers.UIProvider.FolderCapabilities;
239695e0046f79bd2d7166a411d6feff4cf0fb2539Jin Caoimport com.android.mail.utils.Utils;
24a7e154530add87e05d1fcee980e1a1fc34a456e7mindypimport com.google.common.base.Objects;
25e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereiraimport com.google.common.collect.Lists;
26e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
27e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereiraimport android.content.Context;
28e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereiraimport android.database.Cursor;
2947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantlerimport android.net.Uri;
30a7e154530add87e05d1fcee980e1a1fc34a456e7mindypimport android.text.TextUtils;
31e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereiraimport android.view.LayoutInflater;
32e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereiraimport android.view.View;
33e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereiraimport android.view.ViewGroup;
34e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereiraimport android.widget.BaseAdapter;
35590c6146482fd90b56afc547605c2193706fbf99Rohan Shahimport android.widget.CheckedTextView;
3641b1c8d641caf29a0699a8080f469b3984fe319eMindy Pereiraimport android.widget.CompoundButton;
37a716ddb162e23aab7c202460e5a475db97a71a8dMindy Pereiraimport android.widget.ImageView;
385a5859d34d44ed4cbe0a4658eec76ffe466f4bddMindy Pereiraimport android.widget.TextView;
39e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
4047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantlerimport java.util.ArrayDeque;
4133e84f666058dc902a89883f144be4d7baaf50f3Tony Mantlerimport java.util.ArrayList;
4247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantlerimport java.util.Deque;
4347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantlerimport java.util.HashMap;
44e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereiraimport java.util.List;
4547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantlerimport java.util.Map;
4647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantlerimport java.util.PriorityQueue;
47e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereiraimport java.util.Set;
48e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
49e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira/**
507a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal * An adapter for translating a cursor of {@link Folder} to a set of selectable views to be used for
5130fd47bf1947da5ad813cb957b6cbe569dce563aMindy Pereira * applying folders to one or more conversations.
52e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira */
53e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereirapublic class FolderSelectorAdapter extends BaseAdapter {
54e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
55e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    public static class FolderRow implements Comparable<FolderRow> {
56e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        private final Folder mFolder;
5774098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao        private boolean mIsSelected;
5847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        // Filled in during folderSort
5947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        public String mPathName;
60e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
6174098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao        public FolderRow(Folder folder, boolean isSelected) {
62e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira            mFolder = folder;
6374098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao            mIsSelected = isSelected;
64e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        }
65e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
66e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        public Folder getFolder() {
67e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira            return mFolder;
68e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        }
69e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
7074098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao        public boolean isSelected() {
7174098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao            return mIsSelected;
72e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        }
73e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
7474098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao        public void setIsSelected(boolean isSelected) {
7574098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao            mIsSelected = isSelected;
76e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        }
77e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
78e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        @Override
79e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        public int compareTo(FolderRow another) {
80a89424729fe63b635e64037a989958a49fb70cffPaul Westbrook            // TODO: this should sort the system folders in the appropriate order
81e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira            if (equals(another)) {
82e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira                return 0;
83e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira            } else {
84e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira                return mFolder.name.compareToIgnoreCase(another.mFolder.name);
85e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira            }
86e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        }
87e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
88e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    }
89e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
90eb86a56580c220e4442951e77e9e7c7658fbf09bVikram Aggarwal    protected final List<FolderRow> mFolderRows = Lists.newArrayList();
91eb86a56580c220e4442951e77e9e7c7658fbf09bVikram Aggarwal    private final LayoutInflater mInflater;
92eb86a56580c220e4442951e77e9e7c7658fbf09bVikram Aggarwal    private final int mLayout;
93a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp    private Folder mExcludedFolder;
94e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
95e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    public FolderSelectorAdapter(Context context, Cursor folders,
96106a12a1b6642e8c2f716aefa846d9664fbec158Andrew Sapperstein            Set<String> selected, int layout) {
97e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        mInflater = LayoutInflater.from(context);
98a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp        mLayout = layout;
9974098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao        createFolderRows(folders, selected);
100e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    }
101e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
102106a12a1b6642e8c2f716aefa846d9664fbec158Andrew Sapperstein    public FolderSelectorAdapter(Context context, Cursor folders,
103106a12a1b6642e8c2f716aefa846d9664fbec158Andrew Sapperstein            int layout, Folder excludedFolder) {
104a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp        mInflater = LayoutInflater.from(context);
105a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp        mLayout = layout;
106a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp        mExcludedFolder = excludedFolder;
107a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp        createFolderRows(folders, null);
108a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp    }
109a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp
11074098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao    protected void createFolderRows(Cursor folders, Set<String> selected) {
11161cea951d1e813941b80bba7438745541e5133eaMindy Pereira        if (folders == null) {
11261cea951d1e813941b80bba7438745541e5133eaMindy Pereira            return;
11361cea951d1e813941b80bba7438745541e5133eaMindy Pereira        }
11433e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler        final List<FolderRow> allFolders = new ArrayList<FolderRow>(folders.getCount());
11533e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler
116fc57e7c8ad3fba409e2eab312fa7a1da3ccc690cJin Cao        // Rows corresponding to user created, unchecked folders.
11774098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao        final List<FolderRow> userFolders = new ArrayList<FolderRow>();
118fc57e7c8ad3fba409e2eab312fa7a1da3ccc690cJin Cao        // Rows corresponding to system created, unchecked folders.
11974098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao        final List<FolderRow> systemFolders = new ArrayList<FolderRow>();
120fc57e7c8ad3fba409e2eab312fa7a1da3ccc690cJin Cao
121a6f1e1013f234123eb52bc3e36228a0cffb76092mindyp        if (folders.moveToFirst()) {
122a6f1e1013f234123eb52bc3e36228a0cffb76092mindyp            do {
123a6f1e1013f234123eb52bc3e36228a0cffb76092mindyp                final Folder folder = new Folder(folders);
12474098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao                final boolean isSelected = selected != null
12574098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao                        && selected.contains(
12633e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler                        folder.folderUri.getComparisonUri().toString());
12733e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler                final FolderRow row = new FolderRow(folder, isSelected);
12833e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler                allFolders.add(row);
129fc57e7c8ad3fba409e2eab312fa7a1da3ccc690cJin Cao
130fc57e7c8ad3fba409e2eab312fa7a1da3ccc690cJin Cao                // Add system folders here since we want the original unsorted order (for now..)
1318300fe7192c5511c2de7fca1e4abb198c06678b2Jin Cao                if (meetsRequirements(folder) && !Objects.equal(folder, mExcludedFolder) &&
1328300fe7192c5511c2de7fca1e4abb198c06678b2Jin Cao                        folder.isProviderFolder()) {
13374098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao                    systemFolders.add(row);
134fc57e7c8ad3fba409e2eab312fa7a1da3ccc690cJin Cao                }
135a6f1e1013f234123eb52bc3e36228a0cffb76092mindyp            } while (folders.moveToNext());
136a6f1e1013f234123eb52bc3e36228a0cffb76092mindyp        }
13733e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler        // Need to do the foldersort first with all folders present to avoid dropping orphans
13833e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler        folderSort(allFolders);
13933e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler
14033e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler        // Divert the folders to the appropriate sections
14133e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler        for (final FolderRow row : allFolders) {
14233e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler            final Folder folder = row.getFolder();
14374098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao            if (meetsRequirements(folder) && !Objects.equal(folder, mExcludedFolder) &&
14474098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao                    !folder.isProviderFolder()) {
14574098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao                userFolders.add(row);
14633e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler            }
14733e84f666058dc902a89883f144be4d7baaf50f3Tony Mantler        }
14874098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao        mFolderRows.addAll(systemFolders);
14974098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao        mFolderRows.addAll(userFolders);
150e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    }
151e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
1525650bb59fd9bb351ee94e44a32256e717048ed63Mindy Pereira    /**
15347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * Wrapper class to construct a hierarchy tree of FolderRow objects for sorting
15447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     */
15547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler    private static class TreeNode implements Comparable<TreeNode> {
15647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        public FolderRow mWrappedObject;
15747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        final public PriorityQueue<TreeNode> mChildren = new PriorityQueue<TreeNode>();
15847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        public boolean mAddedToList = false;
15947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler
16047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        TreeNode(FolderRow wrappedObject) {
16147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            mWrappedObject = wrappedObject;
16247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        }
16347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler
16447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        void addChild(final TreeNode child) {
16547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            mChildren.add(child);
16647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        }
16747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler
16847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        TreeNode pollChild() {
16947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            return mChildren.poll();
17047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        }
17147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler
17247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        @Override
17347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        public int compareTo(TreeNode o) {
17447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            // mWrappedObject is always non-null here because we set it before we add this object
17547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            // to a sorted collection, otherwise we wouldn't have known what collection to add it to
17647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            return mWrappedObject.compareTo(o.mWrappedObject);
17747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        }
17847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler    }
17947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler
18047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler    /**
18147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * Sorts the folder list according to hierarchy.
18247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * If no parent information exists this basically just turns into a heap sort
18347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     *
18447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * How this works:
18547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * When the first part of this algorithm completes, we want to have a tree of TreeNode objects
18647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * mirroring the hierarchy of mailboxes/folders in the user's account, but we don't have any
18747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * guarantee that we'll see the parents before their respective children.
18847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * First we check the nodeMap to see if we've already pre-created (see below) a TreeNode for
18947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * the current FolderRow, and if not then we create one now.
19047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * Then for each folder, we check to see if the parent TreeNode has already been created. We
19147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * special case the root node. If we don't find the parent node, then we pre-create one to fill
19247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * in later (see above) when we eventually find the parent's entry.
19347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * Whenever we create a new TreeNode we add it to the nodeMap keyed on the folder's provider
19447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * Uri, so that we can find it later either to add children or to retrieve a half-created node.
19547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * It should be noted that it is only valid to add a child node after the mWrappedObject
19647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * member variable has been set.
19747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * Finally we do a depth-first traversal of the constructed tree to re-fill the folderList in
19847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * hierarchical order.
19947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     * @param folderList List of {@link Folder} objects to sort
20047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler     */
20147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler    private void folderSort(final List<FolderRow> folderList) {
20247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        final TreeNode root = new TreeNode(null);
20347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        // Make double-sure we don't accidentally add the root node to the final list
20447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        root.mAddedToList = true;
20547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        // Map from folder Uri to TreeNode containing said folder
20647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        final Map<Uri, TreeNode> nodeMap = new HashMap<Uri, TreeNode>(folderList.size());
20747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        nodeMap.put(Uri.EMPTY, root);
20847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler
20947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        for (final FolderRow folderRow : folderList) {
21047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            final Folder folder = folderRow.mFolder;
21147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            // Find-and-complete or create the TreeNode wrapper
21247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            TreeNode node = nodeMap.get(folder.folderUri.getComparisonUri());
21347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            if (node == null) {
21447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                node = new TreeNode(folderRow);
21547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                nodeMap.put(folder.folderUri.getComparisonUri(), node);
21647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            } else {
21747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                node.mWrappedObject = folderRow;
21847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            }
21947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            // Special case the top level folders
2209695e0046f79bd2d7166a411d6feff4cf0fb2539Jin Cao            if (Utils.isEmpty(folderRow.mFolder.parent)) {
22147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                root.addChild(node);
22247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            } else {
22347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                // Find or half-create the parent TreeNode wrapper
22447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                TreeNode parentNode = nodeMap.get(folder.parent);
22547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                if (parentNode == null) {
22647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                    parentNode = new TreeNode(null);
22747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                    nodeMap.put(folder.parent, parentNode);
22847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                }
22947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                parentNode.addChild(node);
23047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            }
23147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        }
23247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler
23347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        folderList.clear();
23447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler
23547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        // Depth-first traversal of the constructed tree. Flattens the tree back into the
23647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        // folderList list and sets mPathName in the FolderRow objects
23747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        final Deque<TreeNode> stack = new ArrayDeque<TreeNode>(10);
23847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        stack.push(root);
23947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        TreeNode currentNode;
24047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        while ((currentNode = stack.poll()) != null) {
24147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            final TreeNode parentNode = stack.peek();
24247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            // If parentNode is null then currentNode is the root node (not a real folder)
24347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            // If mAddedToList is true it means we've seen this node before and just want to
24447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            // iterate the children.
24547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            if (parentNode != null && !currentNode.mAddedToList) {
24647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                final String pathName;
24747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                // If the wrapped object is null then the parent is the root
24847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                if (parentNode.mWrappedObject == null ||
24947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                        TextUtils.isEmpty(parentNode.mWrappedObject.mPathName)) {
25047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                    pathName = currentNode.mWrappedObject.mFolder.name;
25147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                } else {
25247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                    /**
25347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                     * This path name is re-split at / characters in
25447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                     * {@link HierarchicalFolderSelectorAdapter#truncateHierarchy}
25547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                     */
25647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                    pathName = parentNode.mWrappedObject.mPathName + "/"
25747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                            + currentNode.mWrappedObject.mFolder.name;
25847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                }
25947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                currentNode.mWrappedObject.mPathName = pathName;
26047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                folderList.add(currentNode.mWrappedObject);
26147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                // Mark this node as done so we don't re-add it
26247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                currentNode.mAddedToList = true;
26347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            }
26447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            final TreeNode childNode = currentNode.pollChild();
26547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            if (childNode != null) {
26647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                // If we have children to deal with, re-push the current node as the parent...
26747ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                stack.push(currentNode);
26847ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                // ... then add the child node and loop around to deal with it...
26947ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                stack.push(childNode);
27047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            }
27147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            // ... otherwise we're done with currentNode
27247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        }
27347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler    }
27447ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler
27547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler    /**
2765650bb59fd9bb351ee94e44a32256e717048ed63Mindy Pereira     * Return whether the supplied folder meets the requirements to be displayed
2775650bb59fd9bb351ee94e44a32256e717048ed63Mindy Pereira     * in the folder list.
2785650bb59fd9bb351ee94e44a32256e717048ed63Mindy Pereira     */
2795650bb59fd9bb351ee94e44a32256e717048ed63Mindy Pereira    protected boolean meetsRequirements(Folder folder) {
2805650bb59fd9bb351ee94e44a32256e717048ed63Mindy Pereira        // We only want to show the non-Trash folders that can accept moved messages
2815650bb59fd9bb351ee94e44a32256e717048ed63Mindy Pereira        return folder.supportsCapability(FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES) &&
2828c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy                !folder.isTrash() && !Objects.equal(folder, mExcludedFolder);
2835650bb59fd9bb351ee94e44a32256e717048ed63Mindy Pereira    }
2845650bb59fd9bb351ee94e44a32256e717048ed63Mindy Pereira
285e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    @Override
286e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    public int getCount() {
287106a12a1b6642e8c2f716aefa846d9664fbec158Andrew Sapperstein        return mFolderRows.size();
288e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    }
289e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
290e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    @Override
2915a5859d34d44ed4cbe0a4658eec76ffe466f4bddMindy Pereira    public Object getItem(int position) {
292106a12a1b6642e8c2f716aefa846d9664fbec158Andrew Sapperstein        return mFolderRows.get(position);
293e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    }
294e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
295e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    @Override
296e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    public long getItemId(int position) {
297e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        return position;
298e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    }
299e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira
300e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    @Override
3015a5859d34d44ed4cbe0a4658eec76ffe466f4bddMindy Pereira    public int getItemViewType(int position) {
302106a12a1b6642e8c2f716aefa846d9664fbec158Andrew Sapperstein        return SeparatedFolderListAdapter.TYPE_ITEM;
3035a5859d34d44ed4cbe0a4658eec76ffe466f4bddMindy Pereira    }
3045a5859d34d44ed4cbe0a4658eec76ffe466f4bddMindy Pereira
3055a5859d34d44ed4cbe0a4658eec76ffe466f4bddMindy Pereira    @Override
3065a5859d34d44ed4cbe0a4658eec76ffe466f4bddMindy Pereira    public int getViewTypeCount() {
307106a12a1b6642e8c2f716aefa846d9664fbec158Andrew Sapperstein        return 1;
308723ca34b4f74191a014055194dceb1a12a8127c5Vikram Aggarwal    }
309723ca34b4f74191a014055194dceb1a12a8127c5Vikram Aggarwal
3105a5859d34d44ed4cbe0a4658eec76ffe466f4bddMindy Pereira    @Override
311e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    public View getView(int position, View convertView, ViewGroup parent) {
31247ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        final View view;
31347ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        if (convertView == null) {
314a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp            view = mInflater.inflate(mLayout, parent, false);
31547ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        } else {
31647ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler            view = convertView;
317a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp        }
3187ac3d4ea760a5b2eef1a1f5476f25747027c2883Paul Westbrook        final FolderRow row = (FolderRow) getItem(position);
3197ac3d4ea760a5b2eef1a1f5476f25747027c2883Paul Westbrook        final Folder folder = row.getFolder();
32047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        final String folderDisplay = !TextUtils.isEmpty(row.mPathName) ?
32147ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler                row.mPathName : folder.name;
322590c6146482fd90b56afc547605c2193706fbf99Rohan Shah        final CheckedTextView checkBox = (CheckedTextView) view.findViewById(R.id.checkbox);
323a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp        if (checkBox != null) {
324a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp            // Suppress the checkbox selection, and handle the toggling of the
325a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp            // folder on the parent list item's click handler.
326a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp            checkBox.setClickable(false);
327a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp            checkBox.setText(folderDisplay);
32874098779f90c61487ff0b5bbb7a458f0a8818047Jin Cao            checkBox.setChecked(row.isSelected());
329a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp        }
33047ce8047c208f3268f31bd7ef7eb5392b670ea8aTony Mantler        final TextView display = (TextView) view.findViewById(R.id.folder_name);
331a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp        if (display != null) {
332a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp            display.setText(folderDisplay);
333a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp        }
334cc2e1ee5ea19d1b166a7542ea03f4fd4e5c807e3Rohan Shah
335cc2e1ee5ea19d1b166a7542ea03f4fd4e5c807e3Rohan Shah        final ImageView folderIcon = (ImageView) view.findViewById(R.id.folder_icon);
336590c6146482fd90b56afc547605c2193706fbf99Rohan Shah        Folder.setIcon(folder, folderIcon);
337e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira        return view;
338e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira    }
339e234cb1ef4285cb6061a18b95ec5dd49ed119f5cMindy Pereira}
340