RecentFolderList.java revision 6dde178687e85aaab9b3f8e9c124c3ab99b77d5a
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.ContentResolver; 20import android.content.ContentValues; 21import android.content.Context; 22import android.database.Cursor; 23import android.text.TextUtils; 24import android.os.AsyncTask; 25 26import com.android.mail.providers.Account; 27import com.android.mail.providers.Folder; 28import com.android.mail.providers.UIProvider; 29import com.android.mail.utils.LogUtils; 30import com.android.mail.utils.LruCache; 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 LOG_TAG = new LogUtils().getLogTag(); 48 /** The application context */ 49 private final Context mContext; 50 /** The current account */ 51 private Account mAccount = null; 52 /** 53 * Compare based on alphanumeric name of the folder, ignoring case. 54 */ 55 private static final Comparator<Folder> ALPHABET_IGNORECASE = new Comparator<Folder>() { 56 @Override 57 public int compare(Folder lhs, Folder rhs) { 58 return lhs.name.compareToIgnoreCase(rhs.name); 59 } 60 }; 61 private final LruCache<String, Folder> mFolderCache; 62 /** 63 * We want to show five recent folders, and one space for the current folder (not displayed 64 * to the user). 65 */ 66 private final static int NUM_FOLDERS = 5 + 1; 67 /** 68 * Class to store the recent folder list asynchronously. 69 */ 70 private class StoreRecent extends AsyncTask<ContentValues, Void, Void> { 71 final ContentResolver mResolver; 72 final Account mAccount; 73 74 public StoreRecent(Context context, Account account) { 75 mResolver = context.getContentResolver(); 76 mAccount = account; 77 } 78 79 @Override 80 protected Void doInBackground(ContentValues... valuesArray) { 81 mResolver.update(mAccount.recentFolderListUri, valuesArray[0], null, null); 82 return null; 83 } 84 } 85 86 /** 87 * Create a Recent Folder List from the given account. This will query the UIProvider to 88 * retrieve the RecentFolderList from persistent storage (if any). 89 * @param account 90 */ 91 public RecentFolderList(Context context) { 92 mFolderCache = new LruCache<String, Folder>(NUM_FOLDERS); 93 mContext = context; 94 } 95 96 /** 97 * Change the current account. This causes the recent label list to be written out to the 98 * provider. When a cursor over the recent folders for this account is available, the client 99 * <b>must</b> call {@link #loadFromUiProvider(Cursor)} with the updated cursor. Till then, 100 * the recent account list will be empty. 101 * @param account 102 */ 103 public void setCurrentAccount(Account account) { 104 saveToUiProvider(); 105 mAccount = account; 106 // At some point in the future, the load method will return and populate our cache with 107 // useful entries. But for now, the cache is invalid. 108 mFolderCache.clear(); 109 } 110 111 /** 112 * Load the account information from the UI provider given the cursor over the recent folders. 113 * @param data a cursor over the recent folders. 114 */ 115 public void loadFromUiProvider(Cursor data) { 116 if (mAccount == null || mAccount.recentFolderListUri == null || data == null 117 || data.getCount() <= 0) 118 return; 119 int i = 0; 120 while (data.moveToNext()) { 121 assert (data.getColumnCount() == UIProvider.FOLDERS_PROJECTION.length); 122 Folder folder = new Folder(data); 123 mFolderCache.putElement(folder.id, folder); 124 i++; 125 if (i >= NUM_FOLDERS) 126 break; 127 } 128 } 129 130 /** 131 * Marks the given folder as 'accessed' by the user interface, and its entry is updated in the 132 * recent folder list. 133 * @param folder the folder we have changed to. 134 */ 135 public void touchFolder(Folder folder) { 136 mFolderCache.putElement(folder.id, folder); 137 // Update the UiProvider with the current recent folder list. 138 // TODO(viki): Perhaps not do this on every touch. This is excessive. 139 saveToUiProvider(); 140 } 141 142 /** 143 * Requests the UIProvider to save this RecentFolderList to persistent storage. 144 */ 145 private void saveToUiProvider() { 146 if (mAccount == null || mFolderCache.isEmpty() || mAccount.recentFolderListUri == null) 147 return; 148 // TODO: Remove this test 149 if (TextUtils.equals("null", mAccount.recentFolderListUri.toString())) { 150 LogUtils.d(LOG_TAG, "recent folder list uri was null for account " + mAccount.name); 151 return; 152 } 153 // Write the current recent folder list into the account. 154 // Store the ID of the folder and the last touched timestamp. 155 ContentValues values = new ContentValues(); 156 // TODO(viki): Fix the timestamps here, and put real timestamps rather than garbage. 157 final long now = System.currentTimeMillis(); 158 for (String id : mFolderCache.keySet()) { 159 values.put(id, now); 160 } 161 // Store the values in the background. 162 new StoreRecent(mContext, mAccount).execute(values); 163 } 164 165 /** 166 * Generate a sorted array of recent folders, excluding the specified folders. 167 * @param exclude the folders to be excluded. 168 */ 169 public Folder[] getSortedArray(Folder exclude) { 170 final int spaceForCurrentFolder = 171 (exclude != null && mFolderCache.getElement(exclude.id) != null) 172 ? 1 : 0; 173 final int numRecents = mFolderCache.size() - spaceForCurrentFolder; 174 final Folder[] folders = new Folder[numRecents]; 175 int i = 0; 176 final List<Folder> recent = new ArrayList<Folder>(mFolderCache.values()); 177 Collections.sort(recent, ALPHABET_IGNORECASE); 178 for (Folder f : recent) { 179 if (exclude == null || !TextUtils.equals(f.id, exclude.id)) { 180 folders[i++] = f; 181 } 182 } 183 return folders; 184 } 185} 186