• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 
17 package com.android.email.activity;
18 
19 import com.android.email.Controller;
20 import com.android.email.ControllerResultUiThreadWrapper;
21 import com.android.email.Email;
22 import com.android.emailcommon.Logging;
23 import com.android.emailcommon.mail.MessagingException;
24 import com.android.emailcommon.provider.Account;
25 import com.android.emailcommon.provider.Mailbox;
26 import com.android.emailcommon.utility.EmailAsyncTask;
27 import com.android.emailcommon.utility.Utility;
28 
29 import android.content.Context;
30 import android.os.Handler;
31 import android.util.Log;
32 
33 /**
34  * A class that finds a mailbox ID by account ID and mailbox type.
35  *
36  * If an account doesn't have a mailbox of a specified type, it refreshes the mailbox list and
37  * try looking for again.
38  *
39  * This is a "one-shot" class.  You create an instance, call {@link #startLookup}, get a result
40  * or call {@link #cancel}, and that's it.  The instance can't be re-used.
41  */
42 public class MailboxFinder {
43     private final Context mContext;
44     private final Controller mController;
45 
46     // Actual Controller.Result that will wrapped by ControllerResultUiThreadWrapper.
47     // Unit tests directly use it to avoid asynchronicity caused by ControllerResultUiThreadWrapper.
48     private final ControllerResults mInnerControllerResults;
49     private Controller.Result mControllerResults; // Not final, we null it out when done.
50 
51     private final long mAccountId;
52     private final int mMailboxType;
53     private final Callback mCallback;
54 
55     private FindMailboxTask mTask;
56     private boolean mStarted;
57     private boolean mClosed;
58 
59     /**
60      * Callback for results.
61      */
62     public interface Callback {
onAccountNotFound()63         public void onAccountNotFound();
onMailboxNotFound(long accountId)64         public void onMailboxNotFound(long accountId);
onAccountSecurityHold(long accountId)65         public void onAccountSecurityHold(long accountId);
onMailboxFound(long accountId, long mailboxId)66         public void onMailboxFound(long accountId, long mailboxId);
67     }
68 
69     /**
70      * Creates an instance for {@code accountId} and {@code mailboxType}.  (But won't start yet)
71      *
72      * Must be called on the UI thread.
73      */
MailboxFinder(Context context, long accountId, int mailboxType, Callback callback)74     public MailboxFinder(Context context, long accountId, int mailboxType, Callback callback) {
75         if (accountId == -1) {
76             throw new UnsupportedOperationException();
77         }
78         mContext = context.getApplicationContext();
79         mController = Controller.getInstance(context);
80         mAccountId = accountId;
81         mMailboxType = mailboxType;
82         mCallback = callback;
83         mInnerControllerResults = new ControllerResults();
84         mControllerResults = new ControllerResultUiThreadWrapper<ControllerResults>(
85                 new Handler(), mInnerControllerResults);
86         mController.addResultCallback(mControllerResults);
87     }
88 
89     /**
90      * Start looking up.
91      *
92      * Must be called on the UI thread.
93      */
startLookup()94     public void startLookup() {
95         if (mStarted) {
96             throw new IllegalStateException(); // Can't start twice.
97         }
98         mStarted = true;
99         mTask = new FindMailboxTask(true);
100         mTask.executeParallel();
101     }
102 
103     /**
104      * Cancel the operation.  It's safe to call it multiple times, or even if the operation is
105      * already finished.
106      */
cancel()107     public void cancel() {
108         if (!mClosed) {
109             close();
110         }
111     }
112 
113     /**
114      * Stop the running task, if exists, and clean up internal resources.
115      */
close()116     private void close() {
117         mClosed = true;
118         if (mControllerResults != null) {
119             mController.removeResultCallback(mControllerResults);
120             mControllerResults = null;
121         }
122         Utility.cancelTaskInterrupt(mTask);
123         mTask = null;
124     }
125 
126     private class ControllerResults extends Controller.Result {
127         @Override
updateMailboxListCallback(MessagingException result, long accountId, int progress)128         public void updateMailboxListCallback(MessagingException result, long accountId,
129                 int progress) {
130             if (mClosed || (accountId != mAccountId)) {
131                 return; // Already closed, or non-target account.
132             }
133             Log.i(Logging.LOG_TAG, "MailboxFinder: updateMailboxListCallback");
134             if (result != null) {
135                 // Error while updating the mailbox list.  Notify the UI...
136                 try {
137                     mCallback.onMailboxNotFound(mAccountId);
138                 } finally {
139                     close();
140                 }
141             } else if (progress == 100) {
142                 // Mailbox list updated, look for mailbox again...
143                 mTask = new FindMailboxTask(false);
144                 mTask.executeParallel();
145             }
146         }
147     }
148 
149     /**
150      * Async task for finding a single mailbox by type.  If a mailbox of a type is not found,
151      * and {@code okToRecurse} is true, we update the mailbox list and try looking again.
152      */
153     private class FindMailboxTask extends EmailAsyncTask<Void, Void, Long> {
154         private final boolean mOkToRecurse;
155 
156         private static final int RESULT_MAILBOX_FOUND = 0;
157         private static final int RESULT_ACCOUNT_SECURITY_HOLD = 1;
158         private static final int RESULT_ACCOUNT_NOT_FOUND = 2;
159         private static final int RESULT_MAILBOX_NOT_FOUND = 3;
160         private static final int RESULT_START_NETWORK_LOOK_UP = 4;
161 
162         private int mResult = -1;
163 
164         /**
165          * Special constructor to cache some local info
166          */
FindMailboxTask(boolean okToRecurse)167         public FindMailboxTask(boolean okToRecurse) {
168             super(null);
169             mOkToRecurse = okToRecurse;
170         }
171 
172         @Override
doInBackground(Void... params)173         protected Long doInBackground(Void... params) {
174             // Quick check that account is not in security hold
175             if (Account.isSecurityHold(mContext, mAccountId)) {
176                 mResult = RESULT_ACCOUNT_SECURITY_HOLD;
177                 return Mailbox.NO_MAILBOX;
178             }
179 
180             // See if we can find the requested mailbox in the DB.
181             long mailboxId = Mailbox.findMailboxOfType(mContext, mAccountId, mMailboxType);
182             if (mailboxId != Mailbox.NO_MAILBOX) {
183                 mResult = RESULT_MAILBOX_FOUND;
184                 return mailboxId; // Found
185             }
186 
187             // Mailbox not found.  Does the account really exists?
188             final boolean accountExists = Account.isValidId(mContext, mAccountId);
189             if (accountExists) {
190                 if (mOkToRecurse) {
191                     // launch network lookup
192                     mResult = RESULT_START_NETWORK_LOOK_UP;
193                 } else {
194                     mResult = RESULT_MAILBOX_NOT_FOUND;
195                 }
196             } else {
197                 mResult = RESULT_ACCOUNT_NOT_FOUND;
198             }
199             return Mailbox.NO_MAILBOX;
200         }
201 
202         @Override
onSuccess(Long mailboxId)203         protected void onSuccess(Long mailboxId) {
204             switch (mResult) {
205                 case RESULT_ACCOUNT_SECURITY_HOLD:
206                     Log.w(Logging.LOG_TAG, "MailboxFinder: Account security hold.");
207                     try {
208                         mCallback.onAccountSecurityHold(mAccountId);
209                     } finally {
210                         close();
211                     }
212                     return;
213                 case RESULT_ACCOUNT_NOT_FOUND:
214                     Log.w(Logging.LOG_TAG, "MailboxFinder: Account not found.");
215                     try {
216                         mCallback.onAccountNotFound();
217                     } finally {
218                         close();
219                     }
220                     return;
221                 case RESULT_MAILBOX_NOT_FOUND:
222                     Log.w(Logging.LOG_TAG, "MailboxFinder: Mailbox not found.");
223                     try {
224                         mCallback.onMailboxNotFound(mAccountId);
225                     } finally {
226                         close();
227                     }
228                     return;
229                 case RESULT_MAILBOX_FOUND:
230                     if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
231                         Log.d(Logging.LOG_TAG, "MailboxFinder: mailbox found: id=" + mailboxId);
232                     }
233                     try {
234                         mCallback.onMailboxFound(mAccountId, mailboxId);
235                     } finally {
236                         close();
237                     }
238                     return;
239                 case RESULT_START_NETWORK_LOOK_UP:
240                     // Not found locally.  Let's sync the mailbox list...
241                     Log.i(Logging.LOG_TAG, "MailboxFinder: Starting network lookup.");
242                     mController.updateMailboxList(mAccountId);
243                     return;
244                 default:
245                     throw new RuntimeException();
246             }
247         }
248     }
249 
isStartedForTest()250     /* package */ boolean isStartedForTest() {
251         return mStarted;
252     }
253 
254     /**
255      * Called by unit test.  Return true if all the internal resources are really released.
256      */
isReallyClosedForTest()257     /* package */ boolean isReallyClosedForTest() {
258         return mClosed && (mTask == null) && (mControllerResults == null);
259     }
260 
getControllerResultsForTest()261     /* package */ Controller.Result getControllerResultsForTest() {
262         return mInnerControllerResults;
263     }
264 }
265