RefreshManager.java revision e357f5879187124c7af5c2ece5d7d3e4f60f07d2
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
1921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport com.android.email.mail.MessagingException;
2021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
2121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport android.content.Context;
2221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport android.os.Handler;
2321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport android.util.Log;
2421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
2521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport java.security.InvalidParameterException;
2621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport java.util.ArrayList;
2721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport java.util.Collection;
2821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukiimport java.util.HashMap;
2921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
3021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki/**
3121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * Class that handles "refresh" (and "send pending messages" for outboxes) related functionalities.
3221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *
3321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * <p>This class is responsible for two things:
3421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * <ul>
3521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *   <li>Taking refresh requests of mailbox-lists and message-lists and the "send outgoing
3621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *       messages" requests from UI, and calls appropriate methods of {@link Controller}.
3721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *       Note at this point the timer-based refresh
3821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *       (by {@link com.android.email.service.MailService}) uses {@link Controller} directly.
3921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *   <li>Keeping track of which mailbox list/message list is actually being refreshed.
4021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * </ul>
4121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * Refresh requests will be ignored if a request to the same target is already requested, or is
4221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * already being refreshed.
4321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki *
4421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki * <p>Conceptually it can be a part of {@link Controller}, but extracted for easy testing.
45715a19be28723908dbf332b9a9029993510dad0eMakoto Onuki *
46e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki * (All public methods must be called on the UI thread.  All callbacks will be called on the UI
47715a19be28723908dbf332b9a9029993510dad0eMakoto Onuki * thread.)
4821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki */
4921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onukipublic class RefreshManager {
5021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private static final boolean DEBUG_CALLBACK_LOG = true;
5121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private static final long MAILBOX_AUTO_REFRESH_INTERVAL = 5 * 60 * 1000; // in milliseconds
5221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
5321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private static RefreshManager sInstance;
5421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
5521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final Clock mClock;
5621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final Context mContext;
5721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final Controller mController;
5821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final Controller.Result mControllerResult;
5921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
6021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /** Last error message */
6121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private String mErrorMessage;
6221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
6321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public interface Listener {
6421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void onRefreshStatusChanged(long accountId, long mailboxId);
6521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void onMessagingError(long accountId, long mailboxId, String message);
6621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
6721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
6821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
6921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
7021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
7121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Status of a mailbox list/message list.
7221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
7321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /* package */ static class Status {
7421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
7521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * True if a refresh of the mailbox is requested, and not finished yet.
7621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
7721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private boolean mIsRefreshRequested;
7821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
7921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
8021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * True if the mailbox is being refreshed.
8121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *
8221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Set true when {@link #onRefreshRequested} is called, i.e. refresh is requested by UI.
8321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Note refresh can occur without a request from UI as well (e.g. timer based refresh).
8421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * In which case, {@link #mIsRefreshing} will be true with {@link #mIsRefreshRequested}
8521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * being false.
8621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
8721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private boolean mIsRefreshing;
8821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
8921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private long mLastRefreshTime;
9021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
9121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public boolean isRefreshing() {
9221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            return mIsRefreshRequested || mIsRefreshing;
9321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
9421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
9521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public boolean canRefresh() {
9621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            return !isRefreshing();
9721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
9821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
9921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void onRefreshRequested() {
10021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            mIsRefreshRequested = true;
10121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
10221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
10321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public long getLastRefreshTime() {
10421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            return mLastRefreshTime;
10521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
10621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
10721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void onCallback(MessagingException exception, int progress, Clock clock) {
10821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (exception == null && progress == 0) {
10921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                // Refresh started
11021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mIsRefreshing = true;
11121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            } else if (exception != null || progress == 100) {
11221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                // Refresh finished
11321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mIsRefreshing = false;
11421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mIsRefreshRequested = false;
11521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mLastRefreshTime = clock.getTime();
11621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
11721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
11821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
11921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
12021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
12121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Map of accounts/mailboxes to {@link Status}.
12221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
12321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private static class RefreshStatusMap {
12421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private final HashMap<Long, Status> mMap = new HashMap<Long, Status>();
12521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
12621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public Status get(long id) {
12721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            Status s = mMap.get(id);
12821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (s == null) {
12921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                s = new Status();
13021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mMap.put(id, s);
13121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
13221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            return s;
13321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
13421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
13521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public boolean isRefreshingAny() {
13621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            for (Status s : mMap.values()) {
13721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                if (s.isRefreshing()) {
13821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                    return true;
13921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                }
14021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
14121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            return false;
14221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
14321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
14421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
14521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final RefreshStatusMap mMailboxListStatus = new RefreshStatusMap();
14621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final RefreshStatusMap mMessageListStatus = new RefreshStatusMap();
14721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private final RefreshStatusMap mOutboxStatus = new RefreshStatusMap();
14821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
14921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
15021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * @return the singleton instance.
15121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
15221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public static synchronized RefreshManager getInstance(Context context) {
15321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        if (sInstance == null) {
154c184f36c2df16431693d7709e28ded593efc3da7Marc Blank            sInstance = new RefreshManager(context, Controller.getInstance(context),
15521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                    Clock.INSTANCE, new Handler());
15621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
15721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return sInstance;
15821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
15921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
160e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    protected RefreshManager(Context context, Controller controller, Clock clock,
16121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            Handler handler) {
16221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mClock = clock;
16321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mContext = context.getApplicationContext();
16421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mController = controller;
16521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mControllerResult = new ControllerResultUiThreadWrapper<ControllerResult>(
16621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                handler, new ControllerResult());
16721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mController.addResultCallback(mControllerResult);
16821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
16921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
170e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    /**
171e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki     * MUST be called for mock instances.  (The actual instance is a singleton, so no cleanup
172e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki     * is necessary.)
173e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki     */
174e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    public void cleanUpForTest() {
175e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki        mController.removeResultCallback(mControllerResult);
176e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    }
177e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki
17821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public void registerListener(Listener listener) {
17921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        if (listener == null) {
18021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            throw new InvalidParameterException();
18121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
18221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mListeners.add(listener);
18321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
18421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
18521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public void unregisterListener(Listener listener) {
18621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        if (listener == null) {
18721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            throw new InvalidParameterException();
18821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
18921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mListeners.remove(listener);
19021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
19121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
19221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
19321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Refresh the mailbox list of an account.
19421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
19521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean refreshMailboxList(long accountId) {
19621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        final Status status = mMailboxListStatus.get(accountId);
19721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        if (!status.canRefresh()) return false;
19821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
19921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        Log.i(Email.LOG_TAG, "refreshMailboxList " + accountId);
20021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        status.onRefreshRequested();
20121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        notifyRefreshStatusChanged(accountId, -1);
20221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mController.updateMailboxList(accountId);
20321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return true;
20421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
20521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
20621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean isMailboxStale(long mailboxId) {
20721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mClock.getTime() >= (mMessageListStatus.get(mailboxId).getLastRefreshTime()
20821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                + MAILBOX_AUTO_REFRESH_INTERVAL);
20921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
21021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
21121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
21221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Refresh messages in a mailbox.
21321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
21421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean refreshMessageList(long accountId, long mailboxId) {
21521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return refreshMessageList(accountId, mailboxId, false);
21621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
21721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
21821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
21921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * "load more messages" in a mailbox.
22021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
22121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean loadMoreMessages(long accountId, long mailboxId) {
22221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return refreshMessageList(accountId, mailboxId, true);
22321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
22421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
22521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private boolean refreshMessageList(long accountId, long mailboxId, boolean loadMoreMessages) {
22621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        final Status status = mMessageListStatus.get(mailboxId);
22721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        if (!status.canRefresh()) return false;
22821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
22921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        Log.i(Email.LOG_TAG, "refreshMessageList " + accountId + ", " + mailboxId + ", "
23021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                + loadMoreMessages);
23121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        status.onRefreshRequested();
23221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        notifyRefreshStatusChanged(accountId, mailboxId);
23321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mController.updateMailbox(accountId, mailboxId);
23421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return true;
23521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
23621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
23721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
23821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Send pending messages.
23921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
24021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean sendPendingMessages(long accountId) {
24121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        final Status status = mOutboxStatus.get(accountId);
24221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        if (!status.canRefresh()) return false;
24321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
24421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        Log.i(Email.LOG_TAG, "sendPendingMessages " + accountId);
24521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        status.onRefreshRequested();
24621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        notifyRefreshStatusChanged(accountId, -1);
24721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mController.sendPendingMessages(accountId);
24821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return true;
24921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
25021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
25121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /**
25221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     * Call {@link #sendPendingMessages} for all accounts.
25321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki     */
25421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public void sendPendingMessagesForAllAccounts() {
25521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        Log.i(Email.LOG_TAG, "sendPendingMessagesForAllAccounts");
256f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        new SendPendingMessagesForAllAccountsImpl().execute();
25721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
25821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
259f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki    private class SendPendingMessagesForAllAccountsImpl extends Utility.ForEachAccount {
260f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        public SendPendingMessagesForAllAccountsImpl() {
261f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            super(mContext);
262f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        }
263f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki
264f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        @Override
265f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        protected void performAction(long accountId) {
266f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            sendPendingMessages(accountId);
26721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
26821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
26921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
270e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    public long getLastMailboxListRefreshTime(long accountId) {
271e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki        return mMailboxListStatus.get(accountId).getLastRefreshTime();
272e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    }
273e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki
274e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    public long getLastMessageListRefreshTime(long mailboxId) {
275e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki        return mMessageListStatus.get(mailboxId).getLastRefreshTime();
276e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    }
277e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki
278e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    public long getLastSendMessageTime(long accountId) {
279e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki        return mOutboxStatus.get(accountId).getLastRefreshTime();
280e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki    }
281e357f5879187124c7af5c2ece5d7d3e4f60f07d2Makoto Onuki
28221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean isMailboxListRefreshing(long accountId) {
28321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMailboxListStatus.get(accountId).isRefreshing();
28421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
28521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
28621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean isMessageListRefreshing(long mailboxId) {
28721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMessageListStatus.get(mailboxId).isRefreshing();
28821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
28921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
29021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean isSendingMessage(long accountId) {
29121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mOutboxStatus.get(accountId).isRefreshing();
29221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
29321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
29421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean isRefreshingAnyMailboxList() {
29521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMailboxListStatus.isRefreshingAny();
29621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
29721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
29821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean isRefreshingAnyMessageList() {
29921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMessageListStatus.isRefreshingAny();
30021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
30121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
30221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean isSendingAnyMessage() {
30321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mOutboxStatus.isRefreshingAny();
30421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
30521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
30621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public boolean isRefreshingOrSendingAny() {
30721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return isRefreshingAnyMailboxList() || isRefreshingAnyMessageList()
30821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                || isSendingAnyMessage();
30921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
31021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
31121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    public String getErrorMessage() {
31221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mErrorMessage;
31321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
31421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
31521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private void notifyRefreshStatusChanged(long accountId, long mailboxId) {
31621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        for (Listener l : mListeners) {
31721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            l.onRefreshStatusChanged(accountId, mailboxId);
31821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
31921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
32021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
32121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private void reportError(long accountId, long mailboxId, String errorMessage) {
32221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        mErrorMessage = errorMessage;
32321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        for (Listener l : mListeners) {
32421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            l.onMessagingError(accountId, mailboxId, mErrorMessage);
32521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
32621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
32721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
32821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /* package */ Collection<Listener> getListenersForTest() {
32921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mListeners;
33021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
33121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
33221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /* package */ Status getMailboxListStatusForTest(long accountId) {
33321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMailboxListStatus.get(accountId);
33421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
33521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
33621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /* package */ Status getMessageListStatusForTest(long mailboxId) {
33721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mMessageListStatus.get(mailboxId);
33821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
33921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
34021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    /* package */ Status getOutboxStatusForTest(long acountId) {
34121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        return mOutboxStatus.get(acountId);
34221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
34321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
34421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    private class ControllerResult extends Controller.Result {
34521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private boolean mSendMailExceptionReported = false;
34621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
34721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private String exceptionToString(MessagingException exception) {
34821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (exception == null) {
34921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                return "(no exception)";
35021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            } else {
35121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                return exception.getUiErrorMessage(mContext);
35221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
35321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
35421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
35521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
35621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Callback for mailbox list refresh.
35721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
35821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        @Override
35921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void updateMailboxListCallback(MessagingException exception, long accountId,
36021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                int progress) {
36121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (Email.DEBUG && DEBUG_CALLBACK_LOG) {
36221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                Log.d(Email.LOG_TAG, "updateMailboxListCallback " + accountId + ", " + progress
36321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                        + ", " + exceptionToString(exception));
36421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
36521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            mMailboxListStatus.get(accountId).onCallback(exception, progress, mClock);
36621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (exception != null) {
36721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                reportError(accountId, -1, exception.getUiErrorMessage(mContext));
36821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
36921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            notifyRefreshStatusChanged(accountId, -1);
37021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
37121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
37221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
37321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Callback for explicit (user-driven) mailbox refresh.
37421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
37521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        @Override
37621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void updateMailboxCallback(MessagingException exception, long accountId,
37721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                long mailboxId, int progress, int dontUseNumNewMessages) {
37821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (Email.DEBUG && DEBUG_CALLBACK_LOG) {
37921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                Log.d(Email.LOG_TAG, "updateMailboxCallback " + accountId + ", "
38021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                        + mailboxId + ", " + progress + ", " + exceptionToString(exception));
38121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
38221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            updateMailboxCallbackInternal(exception, accountId, mailboxId, progress, 0);
38321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
38421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
38521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
38621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Callback for implicit (timer-based) mailbox refresh.
38721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *
38821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Do the same as {@link #updateMailboxCallback}.
38921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * TODO: Figure out if it's really okay to do the same as updateMailboxCallback.
39021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * If both the explicit refresh and the implicit refresh can run at the same time,
39121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * we need to keep track of their status separately.
39221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
39321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        @Override
39421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void serviceCheckMailCallback(
39521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                MessagingException exception, long accountId, long mailboxId, int progress,
39621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                long tag) {
39721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (Email.DEBUG && DEBUG_CALLBACK_LOG) {
39821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                Log.d(Email.LOG_TAG, "serviceCheckMailCallback " + accountId + ", "
39921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                        + mailboxId + ", " + progress + ", " + exceptionToString(exception));
40021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
40121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            updateMailboxCallbackInternal(exception, accountId, mailboxId, progress, 0);
40221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
40321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
40421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        private void updateMailboxCallbackInternal(MessagingException exception, long accountId,
40521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                long mailboxId, int progress, int dontUseNumNewMessages) {
40621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            // Don't use dontUseNumNewMessages.  serviceCheckMailCallback() don't set it.
40721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            mMessageListStatus.get(mailboxId).onCallback(exception, progress, mClock);
40821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (exception != null) {
40921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                reportError(accountId, mailboxId, exception.getUiErrorMessage(mContext));
41021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
41121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            notifyRefreshStatusChanged(accountId, mailboxId);
41221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
41321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
41421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki
41521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        /**
41621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Send message progress callback.
41721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *
41821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * This callback is overly overloaded:
41921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *
42021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * First, we get this.
42121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *  result == null, messageId == -1, progress == 0:     start batch send
42221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *
42321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Then we get these callbacks per message.
42421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * (Exchange backend may skip "start sending one message".)
42521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *  result == null, messageId == xx, progress == 0:     start sending one message
42621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *  result == xxxx, messageId == xx, progress == 0;     failed sending one message
42721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *
42821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * Finally we get this.
42921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *  result == null, messageId == -1, progres == 100;    finish sending batch
43021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         *
43121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         * So, let's just report the first exception we get, and ignore the rest.
43221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki         */
43321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        @Override
43421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        public void sendMailCallback(MessagingException exception, long accountId, long messageId,
43521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                int progress) {
43621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (Email.DEBUG && DEBUG_CALLBACK_LOG) {
43721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                Log.d(Email.LOG_TAG, "sendMailCallback " + accountId + ", "
43821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                        + messageId + ", " + progress + ", " + exceptionToString(exception));
43921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
44021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (progress == 0 && messageId == -1) {
44121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mSendMailExceptionReported = false;
44221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
44321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (messageId == -1) {
44421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                // Update the status only for the batch start/end.
44521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                // (i.e. don't report for each message.)
44621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mOutboxStatus.get(accountId).onCallback(exception, progress, mClock);
44721efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                notifyRefreshStatusChanged(accountId, -1);
44821efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
44921efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            if (exception != null && !mSendMailExceptionReported) {
45021efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                // Only the first error in a batch will be reported.
45121efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                mSendMailExceptionReported = true;
45221efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki                reportError(accountId, messageId, exception.getUiErrorMessage(mContext));
45321efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki            }
45421efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki        }
45521efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki    }
45621efedb67fdfff208cef3a18804771fd1d1fff30Makoto Onuki}
457