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