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