/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.email.activity; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import com.android.email.Clock; import com.android.email.Controller; import com.android.emailcommon.provider.EmailContent; import com.android.emailcommon.provider.EmailContent.MailboxColumns; import com.android.emailcommon.provider.Mailbox; import com.android.emailcommon.utility.EmailAsyncTask; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Maps; import java.util.ArrayList; import java.util.HashMap; /** * Manages recent data for mailboxes. */ public class RecentMailboxManager { @VisibleForTesting static Clock sClock = Clock.INSTANCE; @VisibleForTesting static RecentMailboxManager sInstance; public static String RECENT_MAILBOXES_SORT_ORDER = MailboxColumns.DISPLAY_NAME; /** The maximum number of results to retrieve */ private static final int LIMIT_RESULTS = 5; /** Query to find the top most recent mailboxes */ private static final String RECENT_SELECTION = MailboxColumns.ID + " IN " + "( SELECT " + MailboxColumns.ID + " FROM " + Mailbox.TABLE_NAME + " WHERE ( " + MailboxColumns.ACCOUNT_KEY + "=? " + " AND " + Mailbox.USER_VISIBLE_MAILBOX_SELECTION + " AND " + MailboxColumns.TYPE + "!=" + Mailbox.TYPE_INBOX + " AND " + MailboxColumns.LAST_TOUCHED_TIME + ">0 )" + " ORDER BY " + MailboxColumns.LAST_TOUCHED_TIME + " DESC" + " LIMIT ? )"; /** Similar query to {@link #RECENT_SELECTION}, except, exclude all but user mailboxes */ private static final String RECENT_SELECTION_WITH_EXCLUSIONS = MailboxColumns.ID + " IN " + "( SELECT " + MailboxColumns.ID + " FROM " + Mailbox.TABLE_NAME + " WHERE ( " + MailboxColumns.ACCOUNT_KEY + "=? " + " AND " + Mailbox.USER_VISIBLE_MAILBOX_SELECTION + " AND " + MailboxColumns.TYPE + "=" + Mailbox.TYPE_MAIL + " AND " + MailboxColumns.LAST_TOUCHED_TIME + ">0 )" + " ORDER BY " + MailboxColumns.LAST_TOUCHED_TIME + " DESC" + " LIMIT ? )"; /** Mailbox types for default "recent mailbox" entries if none exist */ @VisibleForTesting static final int[] DEFAULT_RECENT_TYPES = new int[] { Mailbox.TYPE_DRAFTS, Mailbox.TYPE_SENT, }; private final Context mContext; private final HashMap<Long, Boolean> mDefaultRecentsInitialized; public static synchronized RecentMailboxManager getInstance(Context context) { if (sInstance == null) { sInstance = new RecentMailboxManager(context); } return sInstance; } /** Hide constructor */ private RecentMailboxManager(Context context) { mContext = context; mDefaultRecentsInitialized = Maps.newHashMap(); } /** Updates the specified mailbox's touch time. Returns an async task for test only. */ public EmailAsyncTask<Void, Void, Void> touch(long accountId, long mailboxId) { return fireAndForget(accountId, mailboxId, sClock.getTime()); } /** * Gets the most recently touched mailboxes for the specified account. If there are no * recent mailboxes and withExclusions is {@code false}, default recent mailboxes will * be returned. * <p><em>WARNING</em>: This method blocks on the database. * @param accountId The ID of the account to load the recent list. * @param withExclusions If {@code false}, all mailboxes are eligible for the recent list. * Otherwise, only user defined mailboxes are eligible for the recent list. */ public ArrayList<Long> getMostRecent(long accountId, boolean withExclusions) { ensureDefaultsInitialized(accountId, sClock.getTime()); String selection = withExclusions ? RECENT_SELECTION_WITH_EXCLUSIONS : RECENT_SELECTION; ArrayList<Long> returnList = new ArrayList<Long>(); Cursor cursor = mContext.getContentResolver().query(Mailbox.CONTENT_URI, EmailContent.ID_PROJECTION, selection, new String[] { Long.toString(accountId), Integer.toString(LIMIT_RESULTS) }, RECENT_MAILBOXES_SORT_ORDER); try { while (cursor.moveToNext()) { returnList.add(cursor.getLong(EmailContent.ID_PROJECTION_COLUMN)); } } finally { cursor.close(); } return returnList; } /** Updates the last touched time for the mailbox in the background */ private EmailAsyncTask<Void, Void, Void> fireAndForget( final long accountId, final long mailboxId, final long time) { return EmailAsyncTask.runAsyncParallel(new Runnable() { @Override public void run() { ensureDefaultsInitialized(accountId, time); touchMailboxSynchronous(accountId, mailboxId, time); } }); } private void touchMailboxSynchronous(long accountId, long mailboxId, long time) { ContentValues values = new ContentValues(); values.put(MailboxColumns.LAST_TOUCHED_TIME, time); mContext.getContentResolver().update( ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId), values, null, null); } /** * Ensures the default recent mailboxes have been set for this account. */ private synchronized void ensureDefaultsInitialized(long accountId, long time) { if (Boolean.TRUE.equals(mDefaultRecentsInitialized.get(accountId))) { return; } String[] args = new String[] { Long.toString(accountId), Integer.toString(LIMIT_RESULTS) }; if (EmailContent.count(mContext, Mailbox.CONTENT_URI, RECENT_SELECTION, args) == 0) { // There are no recent mailboxes at all. Populate with default set. for (int type : DEFAULT_RECENT_TYPES) { long mailbox = Controller.getInstance(mContext).findOrCreateMailboxOfType( accountId, type); touchMailboxSynchronous(accountId, mailbox, time); } } mDefaultRecentsInitialized.put(accountId, true); } }