121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki/*
221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * Copyright (C) 2010 The Android Open Source Project
321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *
421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * Licensed under the Apache License, Version 2.0 (the "License");
521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * you may not use this file except in compliance with the License.
621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * You may obtain a copy of the License at
721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *
821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *      http://www.apache.org/licenses/LICENSE-2.0
921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *
1021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * Unless required by applicable law or agreed to in writing, software
1121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * distributed under the License is distributed on an "AS IS" BASIS,
1221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * See the License for the specific language governing permissions and
1421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * limitations under the License.
1521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki */
1621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
1721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukipackage com.android.email;
1821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
1931d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blankimport com.android.emailcommon.Logging;
202193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.MessagingException;
2131d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blankimport com.android.emailcommon.utility.Utility;
2221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
2321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport android.content.Context;
24bc2eaadde987044027b57d241e635de014bdb8baMakoto Onukiimport android.os.AsyncTask;
2521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport android.os.Handler;
2621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport android.util.Log;
2721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
2821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport java.util.ArrayList;
2921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport java.util.Collection;
3021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport java.util.HashMap;
3121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
3221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki/**
3321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * Class that handles "refresh" (and "send pending messages" for outboxes) related functionalities.
3421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *
3521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * <p>This class is responsible for two things:
3621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * <ul>
3721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *   <li>Taking refresh requests of mailbox-lists and message-lists and the "send outgoing
3821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *       messages" requests from UI, and calls appropriate methods of {@link Controller}.
3921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *       Note at this point the timer-based refresh
4021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *       (by {@link com.android.email.service.MailService}) uses {@link Controller} directly.
4121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *   <li>Keeping track of which mailbox list/message list is actually being refreshed.
4221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * </ul>
4321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * Refresh requests will be ignored if a request to the same target is already requested, or is
4421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * already being refreshed.
4521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *
4621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * <p>Conceptually it can be a part of {@link Controller}, but extracted for easy testing.
47715a19be28723908dbf332b9a9029993510dad0eMakoto Onuki *
48e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki * (All public methods must be called on the UI thread.  All callbacks will be called on the UI
49715a19be28723908dbf332b9a9029993510dad0eMakoto Onuki * thread.)
5021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki */
5121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukipublic class RefreshManager {
52295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki    private static final boolean LOG_ENABLED = false; // DONT SUBMIT WITH TRUE
5321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private static final long MAILBOX_AUTO_REFRESH_INTERVAL = 5 * 60 * 1000; // in milliseconds
546993130a7cb089da642ee689603a2f8487b3fd39Makoto Onuki    private static final long MAILBOX_LIST_AUTO_REFRESH_INTERVAL = 5 * 60 * 1000; // in milliseconds
5521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
5621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private static RefreshManager sInstance;
5721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
5821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final Clock mClock;
5921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final Context mContext;
6021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final Controller mController;
6121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final Controller.Result mControllerResult;
6221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
6321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /** Last error message */
6421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private String mErrorMessage;
6521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
6621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public interface Listener {
67be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki        /**
68be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         * Refresh status of a mailbox list or a message list has changed.
69be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         *
70be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         * @param accountId ID of the account.
71be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         * @param mailboxId -1 if it's about the mailbox list, or the ID of the mailbox list in
72be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         * question.
73be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         */
7421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void onRefreshStatusChanged(long accountId, long mailboxId);
75be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki
76be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki        /**
77be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         * Error callback.
78be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         *
79be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         * @param accountId ID of the account, or -1 if unknown.
80be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         * @param mailboxId ID of the mailbox, or -1 if unknown.
81be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         * @param message error message which can be shown to the user.
82be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         */
8321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void onMessagingError(long accountId, long mailboxId, String message);
8421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
8521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
8621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
8721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
8821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
8921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Status of a mailbox list/message list.
9021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
9121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /* package */ static class Status {
9221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
9321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * True if a refresh of the mailbox is requested, and not finished yet.
9421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
9521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private boolean mIsRefreshRequested;
9621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
9721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
9821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * True if the mailbox is being refreshed.
9921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *
10021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Set true when {@link #onRefreshRequested} is called, i.e. refresh is requested by UI.
10121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Note refresh can occur without a request from UI as well (e.g. timer based refresh).
10221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * In which case, {@link #mIsRefreshing} will be true with {@link #mIsRefreshRequested}
10321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * being false.
10421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
10521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private boolean mIsRefreshing;
10621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
10721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private long mLastRefreshTime;
10821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
10921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public boolean isRefreshing() {
11021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            return mIsRefreshRequested || mIsRefreshing;
11121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
11221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
11321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public boolean canRefresh() {
11421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            return !isRefreshing();
11521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
11621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
11721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void onRefreshRequested() {
11821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            mIsRefreshRequested = true;
11921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
12021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
12121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public long getLastRefreshTime() {
12221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            return mLastRefreshTime;
12321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
12421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
12521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void onCallback(MessagingException exception, int progress, Clock clock) {
12621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (exception == null && progress == 0) {
12721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                // Refresh started
12821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mIsRefreshing = true;
12921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            } else if (exception != null || progress == 100) {
13021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                // Refresh finished
13121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mIsRefreshing = false;
13221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mIsRefreshRequested = false;
13321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mLastRefreshTime = clock.getTime();
13421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
13521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
13621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
13721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
13821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
13921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Map of accounts/mailboxes to {@link Status}.
14021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
14121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private static class RefreshStatusMap {
14221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private final HashMap<Long, Status> mMap = new HashMap<Long, Status>();
14321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
14421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public Status get(long id) {
14521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            Status s = mMap.get(id);
14621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (s == null) {
14721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                s = new Status();
14821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mMap.put(id, s);
14921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
15021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            return s;
15121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
15221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
15321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public boolean isRefreshingAny() {
15421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            for (Status s : mMap.values()) {
15521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                if (s.isRefreshing()) {
15621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                    return true;
15721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                }
15821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
15921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            return false;
16021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
16121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
16221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
16321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final RefreshStatusMap mMailboxListStatus = new RefreshStatusMap();
16421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final RefreshStatusMap mMessageListStatus = new RefreshStatusMap();
16521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
16621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
16721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * @return the singleton instance.
16821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
16921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public static synchronized RefreshManager getInstance(Context context) {
17021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        if (sInstance == null) {
171c184f36c2df16431693d7709e28ded593efc3da7Marc Blank            sInstance = new RefreshManager(context, Controller.getInstance(context),
17221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                    Clock.INSTANCE, new Handler());
17321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
17421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return sInstance;
17521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
17621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
177e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    protected RefreshManager(Context context, Controller controller, Clock clock,
17821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            Handler handler) {
17921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mClock = clock;
18021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mContext = context.getApplicationContext();
18121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mController = controller;
18221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mControllerResult = new ControllerResultUiThreadWrapper<ControllerResult>(
18321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                handler, new ControllerResult());
18421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mController.addResultCallback(mControllerResult);
18521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
18621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
187e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    /**
188e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki     * MUST be called for mock instances.  (The actual instance is a singleton, so no cleanup
189e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki     * is necessary.)
190e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki     */
191e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    public void cleanUpForTest() {
192e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki        mController.removeResultCallback(mControllerResult);
193e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    }
194e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki
19521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public void registerListener(Listener listener) {
19621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        if (listener == null) {
19754c91f00d7f967690a80b992062e75c40182d088Makoto Onuki            throw new IllegalArgumentException();
19821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
19921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mListeners.add(listener);
20021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
20121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
20221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public void unregisterListener(Listener listener) {
20321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        if (listener == null) {
20454c91f00d7f967690a80b992062e75c40182d088Makoto Onuki            throw new IllegalArgumentException();
20521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
20621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mListeners.remove(listener);
20721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
20821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
20921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
21021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Refresh the mailbox list of an account.
21121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
21221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean refreshMailboxList(long accountId) {
21321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        final Status status = mMailboxListStatus.get(accountId);
21421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        if (!status.canRefresh()) return false;
21521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
216295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki        if (LOG_ENABLED) {
21731d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank            Log.d(Logging.LOG_TAG, "refreshMailboxList " + accountId);
218295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki        }
21921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        status.onRefreshRequested();
22021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        notifyRefreshStatusChanged(accountId, -1);
22121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mController.updateMailboxList(accountId);
22221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return true;
22321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
22421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
22521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean isMailboxStale(long mailboxId) {
22621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mClock.getTime() >= (mMessageListStatus.get(mailboxId).getLastRefreshTime()
22721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                + MAILBOX_AUTO_REFRESH_INTERVAL);
22821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
22921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
2306993130a7cb089da642ee689603a2f8487b3fd39Makoto Onuki    public boolean isMailboxListStale(long accountId) {
2316993130a7cb089da642ee689603a2f8487b3fd39Makoto Onuki        return mClock.getTime() >= (mMailboxListStatus.get(accountId).getLastRefreshTime()
2326993130a7cb089da642ee689603a2f8487b3fd39Makoto Onuki                + MAILBOX_LIST_AUTO_REFRESH_INTERVAL);
2336993130a7cb089da642ee689603a2f8487b3fd39Makoto Onuki    }
2346993130a7cb089da642ee689603a2f8487b3fd39Makoto Onuki
23521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
23621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Refresh messages in a mailbox.
23721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
238cbdd9f78b2605e87e45e4f6761b0a8c444a8cd4cMarc Blank    public boolean refreshMessageList(long accountId, long mailboxId, boolean userRequest) {
239cbdd9f78b2605e87e45e4f6761b0a8c444a8cd4cMarc Blank        return refreshMessageList(accountId, mailboxId, false, userRequest);
24021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
24121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
24221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
24321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * "load more messages" in a mailbox.
24421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
24521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean loadMoreMessages(long accountId, long mailboxId) {
246a796fbab0237c094aa943de68f85d201dd22503bMakoto Onuki        return refreshMessageList(accountId, mailboxId, true, true);
24721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
24821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
249cbdd9f78b2605e87e45e4f6761b0a8c444a8cd4cMarc Blank    private boolean refreshMessageList(long accountId, long mailboxId, boolean loadMoreMessages,
250cbdd9f78b2605e87e45e4f6761b0a8c444a8cd4cMarc Blank            boolean userRequest) {
25121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        final Status status = mMessageListStatus.get(mailboxId);
25221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        if (!status.canRefresh()) return false;
25321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
254295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki        if (LOG_ENABLED) {
25531d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank            Log.d(Logging.LOG_TAG, "refreshMessageList " + accountId + ", " + mailboxId + ", "
256295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki                    + loadMoreMessages);
257295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki        }
25821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        status.onRefreshRequested();
25921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        notifyRefreshStatusChanged(accountId, mailboxId);
2600d21aa395ae21857fb5ab74db7c284eda49729d4Makoto Onuki        if (loadMoreMessages) {
2610d21aa395ae21857fb5ab74db7c284eda49729d4Makoto Onuki            mController.loadMoreMessages(mailboxId);
2620d21aa395ae21857fb5ab74db7c284eda49729d4Makoto Onuki        } else {
263cbdd9f78b2605e87e45e4f6761b0a8c444a8cd4cMarc Blank            mController.updateMailbox(accountId, mailboxId, userRequest);
2640d21aa395ae21857fb5ab74db7c284eda49729d4Makoto Onuki        }
26521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return true;
26621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
26721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
26821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
26921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Send pending messages.
27021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
27121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean sendPendingMessages(long accountId) {
272295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki        if (LOG_ENABLED) {
27331d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank            Log.d(Logging.LOG_TAG, "sendPendingMessages " + accountId);
274295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki        }
27521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        notifyRefreshStatusChanged(accountId, -1);
27621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mController.sendPendingMessages(accountId);
27721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return true;
27821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
27921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
28021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
28121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Call {@link #sendPendingMessages} for all accounts.
28221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
28321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public void sendPendingMessagesForAllAccounts() {
284295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki        if (LOG_ENABLED) {
28531d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank            Log.d(Logging.LOG_TAG, "sendPendingMessagesForAllAccounts");
286295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki        }
287bc2eaadde987044027b57d241e635de014bdb8baMakoto Onuki        new SendPendingMessagesForAllAccountsImpl()
288bc2eaadde987044027b57d241e635de014bdb8baMakoto Onuki                .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
28921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
29021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
291f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki    private class SendPendingMessagesForAllAccountsImpl extends Utility.ForEachAccount {
292f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        public SendPendingMessagesForAllAccountsImpl() {
293f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            super(mContext);
294f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        }
295f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki
296f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        @Override
297f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        protected void performAction(long accountId) {
298f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            sendPendingMessages(accountId);
29921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
30021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
30121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
302e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    public long getLastMailboxListRefreshTime(long accountId) {
303e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki        return mMailboxListStatus.get(accountId).getLastRefreshTime();
304e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    }
305e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki
306e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    public long getLastMessageListRefreshTime(long mailboxId) {
307e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki        return mMessageListStatus.get(mailboxId).getLastRefreshTime();
308e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    }
309e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki
31021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean isMailboxListRefreshing(long accountId) {
31121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMailboxListStatus.get(accountId).isRefreshing();
31221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
31321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
31421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean isMessageListRefreshing(long mailboxId) {
31521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMessageListStatus.get(mailboxId).isRefreshing();
31621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
31721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
318c1e8c95d053464e0821d45347704bd6c27e7e546Andy Stadler    public boolean isRefreshingAnyMailboxListForTest() {
31921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMailboxListStatus.isRefreshingAny();
32021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
32121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
322c1e8c95d053464e0821d45347704bd6c27e7e546Andy Stadler    public boolean isRefreshingAnyMessageListForTest() {
32321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMessageListStatus.isRefreshingAny();
32421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
32521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
32621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public String getErrorMessage() {
32721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mErrorMessage;
32821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
32921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
33021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private void notifyRefreshStatusChanged(long accountId, long mailboxId) {
33121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        for (Listener l : mListeners) {
33221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            l.onRefreshStatusChanged(accountId, mailboxId);
33321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
33421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
33521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
33621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private void reportError(long accountId, long mailboxId, String errorMessage) {
33721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mErrorMessage = errorMessage;
33821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        for (Listener l : mListeners) {
33921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            l.onMessagingError(accountId, mailboxId, mErrorMessage);
34021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
34121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
34221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
34321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /* package */ Collection<Listener> getListenersForTest() {
34421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mListeners;
34521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
34621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
34721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /* package */ Status getMailboxListStatusForTest(long accountId) {
34821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMailboxListStatus.get(accountId);
34921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
35021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
35121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /* package */ Status getMessageListStatusForTest(long mailboxId) {
35221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMessageListStatus.get(mailboxId);
35321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
35421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
35521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private class ControllerResult extends Controller.Result {
35621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private boolean mSendMailExceptionReported = false;
35721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
35821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private String exceptionToString(MessagingException exception) {
35921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (exception == null) {
36021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                return "(no exception)";
36121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            } else {
36231d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                return MessagingExceptionStrings.getErrorString(mContext, exception);
36321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
36421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
36521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
36621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
36721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Callback for mailbox list refresh.
36821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
36921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        @Override
37021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void updateMailboxListCallback(MessagingException exception, long accountId,
37121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                int progress) {
372295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki            if (LOG_ENABLED) {
37331d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                Log.d(Logging.LOG_TAG, "updateMailboxListCallback " + accountId + ", " + progress
37421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                        + ", " + exceptionToString(exception));
37521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
37621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            mMailboxListStatus.get(accountId).onCallback(exception, progress, mClock);
37721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (exception != null) {
37831d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                reportError(accountId, -1,
37931d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                        MessagingExceptionStrings.getErrorString(mContext, exception));
38021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
38121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            notifyRefreshStatusChanged(accountId, -1);
38221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
38321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
38421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
38521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Callback for explicit (user-driven) mailbox refresh.
38621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
38721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        @Override
38821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void updateMailboxCallback(MessagingException exception, long accountId,
389c4cdb11d24c19428dd39f986b00c1a29e75e1505Todd Kennedy                long mailboxId, int progress, int dontUseNumNewMessages,
390c4cdb11d24c19428dd39f986b00c1a29e75e1505Todd Kennedy                ArrayList<Long> addedMessages) {
391295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki            if (LOG_ENABLED) {
39231d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                Log.d(Logging.LOG_TAG, "updateMailboxCallback " + accountId + ", "
39321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                        + mailboxId + ", " + progress + ", " + exceptionToString(exception));
39421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
39521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            updateMailboxCallbackInternal(exception, accountId, mailboxId, progress, 0);
39621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
39721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
39821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
39921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Callback for implicit (timer-based) mailbox refresh.
40021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *
40121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Do the same as {@link #updateMailboxCallback}.
40221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * TODO: Figure out if it's really okay to do the same as updateMailboxCallback.
40321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * If both the explicit refresh and the implicit refresh can run at the same time,
40421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * we need to keep track of their status separately.
40521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
40621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        @Override
40721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void serviceCheckMailCallback(
40821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                MessagingException exception, long accountId, long mailboxId, int progress,
40921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                long tag) {
410295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki            if (LOG_ENABLED) {
41131d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                Log.d(Logging.LOG_TAG, "serviceCheckMailCallback " + accountId + ", "
41221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                        + mailboxId + ", " + progress + ", " + exceptionToString(exception));
41321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
41421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            updateMailboxCallbackInternal(exception, accountId, mailboxId, progress, 0);
41521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
41621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
41721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private void updateMailboxCallbackInternal(MessagingException exception, long accountId,
41821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                long mailboxId, int progress, int dontUseNumNewMessages) {
41921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            // Don't use dontUseNumNewMessages.  serviceCheckMailCallback() don't set it.
42021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            mMessageListStatus.get(mailboxId).onCallback(exception, progress, mClock);
42121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (exception != null) {
42231d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                reportError(accountId, mailboxId,
42331d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                        MessagingExceptionStrings.getErrorString(mContext, exception));
42421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
42521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            notifyRefreshStatusChanged(accountId, mailboxId);
42621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
42721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
42821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
42921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
43021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Send message progress callback.
43121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *
432be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         * We don't keep track of the status of outboxes, but we monitor this to catch
433be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki         * errors.
43421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
43521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        @Override
43621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void sendMailCallback(MessagingException exception, long accountId, long messageId,
43721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                int progress) {
438295297581c8af9b6ee0a41487b7fded08739890cMakoto Onuki            if (LOG_ENABLED) {
43931d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                Log.d(Logging.LOG_TAG, "sendMailCallback " + accountId + ", "
44021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                        + messageId + ", " + progress + ", " + exceptionToString(exception));
44121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
44221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (progress == 0 && messageId == -1) {
44321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mSendMailExceptionReported = false;
44421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
44521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (exception != null && !mSendMailExceptionReported) {
44621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                // Only the first error in a batch will be reported.
44721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mSendMailExceptionReported = true;
44831d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                reportError(accountId, messageId,
44931d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                        MessagingExceptionStrings.getErrorString(mContext, exception));
45021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
451be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki            if (progress == 100) {
452be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki                mSendMailExceptionReported = false;
453be1aa37dc516a9c3dd4af65b11f92a5951f5c5c3Makoto Onuki            }
45421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
45521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
45621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki}
457