Controller.java revision 7b0b463477ef7172bd4b9e19c6338634ebedd8ee
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email; 18 19import com.android.email.mail.MessagingException; 20import com.android.email.mail.Store; 21import com.android.email.provider.EmailContent; 22 23import android.content.Context; 24 25import java.util.HashSet; 26 27/** 28 * New central controller/dispatcher for Email activities that may require remote operations. 29 * Handles disambiguating between legacy MessagingController operations and newer provider/sync 30 * based code. 31 */ 32public class Controller { 33 34 static Controller sInstance; 35 private Context mContext; 36 private MessagingController mLegacyController; 37 private HashSet<Result> mListeners = new HashSet<Result>(); 38 39 protected Controller(Context _context) { 40 mContext = _context; 41 mLegacyController = MessagingController.getInstance(mContext); 42 } 43 44 /** 45 * Gets or creates the singleton instance of Controller. Application is used to 46 * provide a Context to classes that need it. 47 * @param _context 48 */ 49 public synchronized static Controller getInstance(Context _context) { 50 if (sInstance == null) { 51 sInstance = new Controller(_context); 52 } 53 return sInstance; 54 } 55 56 /** 57 * Any UI code that wishes for callback results (on async ops) should register their callback 58 * here (typically from onResume()). Unregistered callbacks will never be called, to prevent 59 * problems when the command completes and the activity has already paused or finished. 60 * @param listener The callback that may be used in action methods 61 */ 62 public void addResultCallback(Result listener) { 63 synchronized (mListeners) { 64 mListeners.add(listener); 65 } 66 } 67 68 /** 69 * Any UI code that no longer wishes for callback results (on async ops) should unregister 70 * their callback here (typically from onPause()). Unregistered callbacks will never be called, 71 * to prevent problems when the command completes and the activity has already paused or 72 * finished. 73 * @param listener The callback that may no longer be used 74 */ 75 public void removeResultCallback(Result listener) { 76 synchronized (mListeners) { 77 mListeners.remove(listener); 78 } 79 } 80 81 private boolean isActiveResultCallback(Result listener) { 82 synchronized (mListeners) { 83 return mListeners.contains(listener); 84 } 85 } 86 87 /** 88 * Request a remote update of mailboxes for an account. 89 * 90 * TODO: Implement (if any) for non-MessagingController 91 * TODO: Probably the right way is to create a fake "service" for MessagingController ops 92 */ 93 public void updateMailboxList(final EmailContent.Account account, final Result callback) { 94 95 // 1. determine if we can use MessagingController for this 96 boolean legacyController = isMessagingController(account); 97 98 // 2. if not...? 99 // TODO: for now, just pretend "it worked" 100 if (!legacyController) { 101 if (callback != null) { 102 callback.updateMailboxListCallback(null, account.mId); 103 } 104 return; 105 } 106 107 // 3. if so, make the call 108 new Thread() { 109 @Override 110 public void run() { 111 MessagingListener listener = new LegacyListener(callback); 112 mLegacyController.addListener(listener); 113 mLegacyController.listFolders(account, listener); 114 } 115 }.start(); 116 } 117 118 /** 119 * Request a remote update of a mailbox. 120 * 121 * The contract here should be to try and update the headers ASAP, in order to populate 122 * a simple message list. We should also at this point queue up a background task of 123 * downloading some/all of the messages in this mailbox, but that should be interruptable. 124 */ 125 public void updateMailbox(final EmailContent.Account account, 126 final EmailContent.Mailbox mailbox, final Result callback) { 127 128 // 1. determine if we can use MessagingController for this 129 boolean legacyController = isMessagingController(account); 130 131 // 2. if not...? 132 // TODO: for now, just pretend "it worked" 133 if (!legacyController) { 134 if (callback != null) { 135 callback.updateMailboxCallback(null, account.mId, mailbox.mId, -1, -1); 136 } 137 return; 138 } 139 140 // 3. if so, make the call 141 new Thread() { 142 @Override 143 public void run() { 144 MessagingListener listener = new LegacyListener(callback); 145 mLegacyController.addListener(listener); 146 mLegacyController.synchronizeMailbox(account, mailbox, listener); 147 } 148 }.start(); 149 } 150 151 /** 152 * Simple helper to determine if legacy MessagingController should be used 153 */ 154 private boolean isMessagingController(EmailContent.Account account) { 155 Store.StoreInfo info = 156 Store.StoreInfo.getStoreInfo(account.getStoreUri(mContext), mContext); 157 String scheme = info.mScheme; 158 159 return ("pop3".equals(scheme) || "imap".equals(scheme)); 160 } 161 162 /** 163 * Simple callback for synchronous commands. For many commands, this can be largely ignored 164 * and the result is observed via provider cursors. The callback will *not* necessarily be 165 * made from the UI thread, so you may need further handlers to safely make UI updates. 166 */ 167 public interface Result { 168 169 /** 170 * Callback for updateMailboxList 171 * 172 * @param result If null, the operation completed without error 173 * @param accountKey The account being operated on 174 */ 175 public void updateMailboxListCallback(MessagingException result, long accountKey); 176 177 /** 178 * Callback for updateMailbox 179 * 180 * @param result If null, the operation completed without error 181 * @param accountKey The account being operated on 182 * @param mailboxKey The mailbox being operated on 183 */ 184 public void updateMailboxCallback(MessagingException result, long accountKey, 185 long mailboxKey, int totalMessagesInMailbox, int numNewMessages); 186 } 187 188 /** 189 * Support for receiving callbacks from MessagingController and dealing with UI going 190 * out of scope. 191 */ 192 private class LegacyListener extends MessagingListener { 193 Result mResultCallback; 194 195 public LegacyListener(Result callback) { 196 mResultCallback = callback; 197 } 198 199 @Override 200 public void listFoldersFailed(EmailContent.Account account, String message) { 201 if (mResultCallback != null && isActiveResultCallback(mResultCallback)) { 202 mResultCallback.updateMailboxListCallback(new MessagingException(message), 203 account.mId); 204 } 205 mLegacyController.removeListener(this); 206 } 207 208 @Override 209 public void listFoldersFinished(EmailContent.Account account) { 210 if (mResultCallback != null && isActiveResultCallback(mResultCallback)) { 211 mResultCallback.updateMailboxListCallback(null, account.mId); 212 } 213 mLegacyController.removeListener(this); 214 } 215 216 @Override 217 public void synchronizeMailboxFinished(EmailContent.Account account, 218 EmailContent.Mailbox folder, int totalMessagesInMailbox, int numNewMessages) { 219 if (mResultCallback != null && isActiveResultCallback(mResultCallback)) { 220 mResultCallback.updateMailboxCallback(null, account.mId, folder.mId, 221 totalMessagesInMailbox, numNewMessages); 222 } 223 mLegacyController.removeListener(this); 224 } 225 226 @Override 227 public void synchronizeMailboxFailed(EmailContent.Account account, 228 EmailContent.Mailbox folder, Exception e) { 229 if (mResultCallback != null && isActiveResultCallback(mResultCallback)) { 230 MessagingException me; 231 if (e instanceof MessagingException) { 232 me = (MessagingException) e; 233 } else { 234 me = new MessagingException(e.toString()); 235 } 236 mResultCallback.updateMailboxCallback(me, account.mId, folder.mId, -1, -1); 237 } 238 mLegacyController.removeListener(this); 239 } 240 241 242 } 243 244 245} 246