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