RecentFolderList.java revision 025eba8bfd4d0b5e248b6de0bda6f1129170fb41
1/**
2 * Copyright (c) 2011, Google Inc.
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
17package com.android.mail.ui;
18
19import android.content.ContentValues;
20import android.content.Context;
21import android.database.Cursor;
22import android.net.Uri;
23import android.os.AsyncTask;
24
25import com.android.mail.providers.Account;
26import com.android.mail.providers.Folder;
27import com.android.mail.providers.Settings;
28import com.android.mail.utils.LogUtils;
29import com.android.mail.utils.LruCache;
30import com.android.mail.utils.Utils;
31
32import java.util.ArrayList;
33import java.util.Collections;
34import java.util.Comparator;
35import java.util.List;
36
37/**
38 * A self-updating list of folder canonical names for the N most recently touched folders, ordered
39 * from least-recently-touched to most-recently-touched. N is a fixed size determined upon
40 * creation.
41 *
42 * RecentFoldersCache returns lists of this type, and will keep them updated when observers are
43 * registered on them.
44 *
45 */
46public final class RecentFolderList {
47    private static final String TAG = "RecentFolderList";
48    /** The application context */
49    private final Context mContext;
50    /** The current account */
51    private Account mAccount = null;
52
53    private final LruCache<String, Folder> mFolderCache;
54    /**
55     *  We want to show at most five recent folders
56     */
57    private final static int MAX_RECENT_FOLDERS = 5;
58    /**
59     *  We exclude the default inbox for the account and the current folder; these might be the
60     *  same, but we'll allow for both
61     */
62    private final static int MAX_EXCLUDED_FOLDERS = 2;
63
64    /**
65     * Compare based on alphanumeric name of the folder, ignoring case.
66     */
67    private static final Comparator<Folder> ALPHABET_IGNORECASE = new Comparator<Folder>() {
68        @Override
69        public int compare(Folder lhs, Folder rhs) {
70            return lhs.name.compareToIgnoreCase(rhs.name);
71        }
72    };
73    /**
74     * Class to store the recent folder list asynchronously.
75     */
76    private class StoreRecent extends AsyncTask<Void, Void, Void> {
77        final Account mAccount;
78        final Folder mFolder;
79
80        /**
81         * Create a new asynchronous task to store the recent folder list. Both the account
82         * and the folder should be non-null.
83         * @param account
84         * @param folder
85         */
86        public StoreRecent(Account account, Folder folder) {
87            assert (account != null && folder != null);
88            mAccount = account;
89            mFolder = folder;
90        }
91
92        @Override
93        protected Void doInBackground(Void... v) {
94            Uri uri = mAccount.recentFolderListUri;
95            if (!Utils.isEmpty(uri)) {
96                ContentValues values = new ContentValues();
97                values.put(mFolder.uri.toString(), System.currentTimeMillis());
98                // TODO: Remove when well tested
99                LogUtils.i(TAG, "Save: %s", mFolder.name);
100                mContext.getContentResolver().update(uri, values, null, null);
101            }
102            return null;
103        }
104    }
105
106    /**
107     * Create a Recent Folder List from the given account. This will query the UIProvider to
108     * retrieve the RecentFolderList from persistent storage (if any).
109     * @param account
110     */
111    public RecentFolderList(Context context) {
112        mFolderCache = new LruCache<String, Folder>(MAX_RECENT_FOLDERS);
113        mContext = context;
114    }
115
116    /**
117     * Change the current account. When a cursor over the recent folders for this account is
118     * available, the client <b>must</b> call {@link #loadFromUiProvider(Cursor)} with the updated
119     * cursor. Till then, the recent account list will be empty.
120     * @param account the new current account
121     */
122    public void setCurrentAccount(Account account) {
123        mAccount = account;
124        mFolderCache.clear();
125    }
126
127    /**
128     * Load the account information from the UI provider given the cursor over the recent folders.
129     * @param c a cursor over the recent folders.
130     */
131    public void loadFromUiProvider(Cursor c) {
132        if (mAccount == null || c == null) {
133            return;
134        }
135        int i = 0;
136        while (c.moveToNext()) {
137            Folder folder = new Folder(c);
138            mFolderCache.putElement(folder.uri.toString(), folder);
139            // TODO: Remove when well tested
140            LogUtils.i(TAG, "Account %s, Recent: %s", mAccount.name, folder.name);
141            if (++i == (MAX_RECENT_FOLDERS + MAX_EXCLUDED_FOLDERS))
142                break;
143        }
144    }
145
146    /**
147     * Marks the given folder as 'accessed' by the user interface, its entry is updated in the
148     * recent folder list, and the current time is written to the provider. This should never
149     * be called with a null folder.
150     * @param folder the folder we touched
151     */
152    public void touchFolder(Folder folder, Account account) {
153        // We haven't got a valid account yet, cannot proceed.
154        if (mAccount == null || !mAccount.equals(account)) {
155            if (account != null) {
156                setCurrentAccount(account);
157            } else {
158                LogUtils.w(TAG, "No account set for setting recent folders?");
159                return;
160            }
161        }
162        assert (folder != null);
163        mFolderCache.putElement(folder.uri.toString(), folder);
164        new StoreRecent(mAccount, folder).execute();
165    }
166
167    /**
168     * Generate a sorted list of recent folders, excluding the passed in folder (if any) and
169     * the current account's default inbox. This must be called <em>after</em>
170     * {@link #setCurrentAccount(Account)} has been called.
171     * @param excludedFolder the folder to be excluded (typically the current folder)
172     */
173    public ArrayList<Folder> getRecentFolderList(Folder excludedFolder) {
174        final ArrayList<Uri> excludedUris = new ArrayList<Uri>();
175        if (excludedFolder != null) {
176            excludedUris.add(excludedFolder.uri);
177        }
178        final Uri defaultInbox = Settings.getDefaultInboxUri(mAccount.settings);
179        if (!defaultInbox.equals(Uri.EMPTY)) {
180            // This could already be in the list, but that's ok
181            excludedUris.add(defaultInbox);
182        }
183        final List<Folder> recent = new ArrayList<Folder>(mFolderCache.values());
184        Collections.sort(recent, ALPHABET_IGNORECASE);
185        final ArrayList<Folder> recentFolders = new ArrayList<Folder>();
186        for (Folder f : recent) {
187            if (!excludedUris.contains(f.uri)) {
188                recentFolders.add(f);
189            }
190            if (recentFolders.size() == MAX_RECENT_FOLDERS) break;
191        }
192        return recentFolders;
193    }
194}
195