RecentMailboxManager.java revision 4c49b4a2205da555c93e7d506af0d71218690039
1/* 2 * Copyright (C) 2011 The Android Open Source Project 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.email.activity; 18 19import android.content.ContentUris; 20import android.content.ContentValues; 21import android.content.Context; 22import android.database.Cursor; 23 24import com.android.email.Clock; 25import com.android.email.Controller; 26import com.android.emailcommon.provider.EmailContent; 27import com.android.emailcommon.provider.EmailContent.MailboxColumns; 28import com.android.emailcommon.provider.Mailbox; 29import com.android.emailcommon.utility.EmailAsyncTask; 30import com.google.common.annotations.VisibleForTesting; 31import com.google.common.collect.Maps; 32 33import java.util.ArrayList; 34import java.util.HashMap; 35 36/** 37 * Manages recent data for mailboxes. 38 */ 39public class RecentMailboxManager { 40 @VisibleForTesting 41 static Clock sClock = Clock.INSTANCE; 42 @VisibleForTesting 43 static RecentMailboxManager sInstance; 44 45 public static String RECENT_MAILBOXES_SORT_ORDER = 46 MailboxColumns.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; 47 48 /** The maximum number of results to retrieve */ 49 private static final int LIMIT_RESULTS = 5; 50 /** Query to find the top most recent mailboxes */ 51 private static final String RECENT_SELECTION = 52 MailboxColumns.ID + " IN " + 53 "( SELECT " + MailboxColumns.ID 54 + " FROM " + Mailbox.TABLE_NAME 55 + " WHERE ( " + MailboxColumns.ACCOUNT_KEY + "=? " 56 + " AND " + Mailbox.USER_VISIBLE_MAILBOX_SELECTION 57 + " AND " + MailboxColumns.TYPE + "!=" + Mailbox.TYPE_INBOX 58 + " AND " + MailboxColumns.LAST_TOUCHED_TIME + ">0 )" 59 + " ORDER BY " + MailboxColumns.LAST_TOUCHED_TIME + " DESC" 60 + " LIMIT ? )"; 61 /** Similar query to {@link #RECENT_SELECTION}, except, exclude all but user mailboxes */ 62 private static final String RECENT_SELECTION_WITH_EXCLUSIONS = 63 MailboxColumns.ID + " IN " + 64 "( SELECT " + MailboxColumns.ID 65 + " FROM " + Mailbox.TABLE_NAME 66 + " WHERE ( " + MailboxColumns.ACCOUNT_KEY + "=? " 67 + " AND " + Mailbox.USER_VISIBLE_MAILBOX_SELECTION 68 + " AND " + MailboxColumns.TYPE + "=" + Mailbox.TYPE_MAIL 69 + " AND " + MailboxColumns.LAST_TOUCHED_TIME + ">0 )" 70 + " ORDER BY " + MailboxColumns.LAST_TOUCHED_TIME + " DESC" 71 + " LIMIT ? )"; 72 73 /** Mailbox types for default "recent mailbox" entries if none exist */ 74 @VisibleForTesting 75 static final int[] DEFAULT_RECENT_TYPES = new int[] { 76 Mailbox.TYPE_DRAFTS, 77 Mailbox.TYPE_SENT, 78 }; 79 80 private final Context mContext; 81 private final HashMap<Long, Boolean> mDefaultRecentsInitialized; 82 83 public static synchronized RecentMailboxManager getInstance(Context context) { 84 if (sInstance == null) { 85 sInstance = new RecentMailboxManager(context); 86 } 87 return sInstance; 88 } 89 90 /** Hide constructor */ 91 private RecentMailboxManager(Context context) { 92 mContext = context; 93 mDefaultRecentsInitialized = Maps.newHashMap(); 94 } 95 96 /** Updates the specified mailbox's touch time. Returns an async task for test only. */ 97 public EmailAsyncTask<Void, Void, Void> touch(long accountId, long mailboxId) { 98 return fireAndForget(accountId, mailboxId, sClock.getTime()); 99 } 100 101 /** 102 * Gets the most recently touched mailboxes for the specified account. If there are no 103 * recent mailboxes and withExclusions is {@code false}, default recent mailboxes will 104 * be returned. 105 * <p><em>WARNING</em>: This method blocks on the database. 106 * @param accountId The ID of the account to load the recent list. 107 * @param withExclusions If {@code false}, all mailboxes are eligible for the recent list. 108 * Otherwise, only user defined mailboxes are eligible for the recent list. 109 */ 110 public ArrayList<Long> getMostRecent(long accountId, boolean withExclusions) { 111 ensureDefaultsInitialized(accountId, sClock.getTime()); 112 113 String selection = withExclusions ? RECENT_SELECTION_WITH_EXCLUSIONS : RECENT_SELECTION; 114 ArrayList<Long> returnList = new ArrayList<Long>(); 115 Cursor cursor = mContext.getContentResolver().query(Mailbox.CONTENT_URI, 116 EmailContent.ID_PROJECTION, 117 selection, 118 new String[] { Long.toString(accountId), Integer.toString(LIMIT_RESULTS) }, 119 RECENT_MAILBOXES_SORT_ORDER); 120 try { 121 while (cursor.moveToNext()) { 122 returnList.add(cursor.getLong(EmailContent.ID_PROJECTION_COLUMN)); 123 } 124 } finally { 125 cursor.close(); 126 } 127 return returnList; 128 } 129 130 /** Updates the last touched time for the mailbox in the background */ 131 private EmailAsyncTask<Void, Void, Void> fireAndForget( 132 final long accountId, final long mailboxId, final long time) { 133 return EmailAsyncTask.runAsyncParallel(new Runnable() { 134 @Override 135 public void run() { 136 ensureDefaultsInitialized(accountId, time); 137 touchMailboxSynchronous(accountId, mailboxId, time); 138 } 139 }); 140 } 141 142 private void touchMailboxSynchronous(long accountId, long mailboxId, long time) { 143 ContentValues values = new ContentValues(); 144 values.put(MailboxColumns.LAST_TOUCHED_TIME, time); 145 mContext.getContentResolver().update( 146 ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId), 147 values, null, null); 148 } 149 150 /** 151 * Ensures the default recent mailboxes have been set for this account. 152 */ 153 private synchronized void ensureDefaultsInitialized(long accountId, long time) { 154 if (Boolean.TRUE.equals(mDefaultRecentsInitialized.get(accountId))) { 155 return; 156 } 157 158 String[] args = new String[] { Long.toString(accountId), Integer.toString(LIMIT_RESULTS) }; 159 if (EmailContent.count(mContext, Mailbox.CONTENT_URI, RECENT_SELECTION, args) == 0) { 160 // There are no recent mailboxes at all. Populate with default set. 161 for (int type : DEFAULT_RECENT_TYPES) { 162 long mailbox = Controller.getInstance(mContext).findOrCreateMailboxOfType( 163 accountId, type); 164 touchMailboxSynchronous(accountId, mailbox, time); 165 } 166 } 167 168 mDefaultRecentsInitialized.put(accountId, true); 169 } 170} 171