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