1/**
2 * Copyright (c) 2012, Google Inc.
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.mail.compose;
18
19import android.content.ContentResolver;
20import android.content.Context;
21import android.content.Intent;
22import android.database.Cursor;
23import android.database.MatrixCursor;
24import android.net.Uri;
25import android.test.ActivityInstrumentationTestCase2;
26import android.test.suitebuilder.annotation.SmallTest;
27import android.text.Html;
28import android.text.TextUtils;
29import android.text.util.Rfc822Tokenizer;
30
31import com.android.mail.providers.Account;
32import com.android.mail.providers.Attachment;
33import com.android.mail.providers.MailAppProvider;
34import com.android.mail.providers.Message;
35import com.android.mail.providers.ReplyFromAccount;
36import com.android.mail.providers.UIProvider;
37import com.android.mail.utils.AccountUtils;
38import com.android.mail.utils.MatrixCursorWithCachedColumns;
39import com.android.mail.utils.Utils;
40
41import org.json.JSONArray;
42import org.json.JSONException;
43
44import java.io.UnsupportedEncodingException;
45import java.net.URLEncoder;
46import java.util.Date;
47import java.util.HashSet;
48
49@SmallTest
50public class ComposeActivityTest extends ActivityInstrumentationTestCase2<ComposeActivity> {
51    // TODO: Remove usages of FromAddressSpinner#initialize and ComposeActivity#initReplyRecipients.
52    // The internal state of the activity instance may have the wrong mReplyFromAccount as
53    // this is set when handling the initial intent.  Theses tests should
54    // instantiate the ComposeActivity with the correct reply all intent
55
56    // This varible shouldn't be used, as it may not match the state of the ComposeActivity
57    // TODO: remove usage of this variable
58    @Deprecated
59    private Account mAccount;
60
61    private static final Account[] EMPTY_ACCOUNT_LIST = new Account[0];
62
63    public ComposeActivityTest() {
64        super(ComposeActivity.class);
65    }
66
67    private Message getRefMessage(ContentResolver resolver, Uri folderListUri) {
68        Cursor foldersCursor = resolver.query(folderListUri,
69                UIProvider.FOLDERS_PROJECTION, null, null, null);
70        Uri convUri = null;
71        if (foldersCursor != null) {
72            foldersCursor.moveToFirst();
73            convUri = Uri.parse(foldersCursor
74                    .getString(UIProvider.FOLDER_CONVERSATION_LIST_URI_COLUMN));
75            foldersCursor.close();
76        }
77
78        Cursor convCursor = resolver.query(convUri,
79                UIProvider.CONVERSATION_PROJECTION, null, null, null);
80        Uri messagesUri = null;
81        if (convCursor != null) {
82            convCursor.moveToFirst();
83            messagesUri = Uri.parse(convCursor
84                    .getString(UIProvider.CONVERSATION_MESSAGE_LIST_URI_COLUMN));
85            convCursor.close();
86        }
87
88        Cursor msgCursor = resolver.query(messagesUri,
89                UIProvider.MESSAGE_PROJECTION, null, null, null);
90        if (msgCursor != null) {
91            msgCursor.moveToFirst();
92        }
93        return new Message(msgCursor);
94    }
95
96
97    private Message getRefMessage(ContentResolver resolver) {
98        final Account account = getActivity().getFromAccount();
99        return getRefMessage(resolver, account.folderListUri);
100    }
101
102    public void setAccount(ComposeActivity activity, String accountName) {
103        // Get a mock account.
104        final Account account = getAccountForName(activity, accountName);
105        if (account != null) {
106            mAccount = account;
107            activity.setAccount(mAccount);
108        }
109    }
110
111    private Account[] getAccounts(Context context) {
112        return AccountUtils.getSyncingAccounts(context);
113    }
114
115    private Account getAccountForName(Context context, String accountName) {
116        Account[] results = getAccounts(context);
117        for (Account account : results) {
118            if (account.getEmailAddress().equals(accountName)) {
119                return account;
120            }
121        }
122        return null;
123    }
124
125    /**
126     * Test the cases where: The user's reply-to is one of their custom from's
127     * and they are replying all to a message where their custom from was a
128     * recipient. TODO: verify web behavior
129     */
130    public void testRecipientsRefReplyAllCustomFromReplyTo() throws Throwable {
131        final ComposeActivity activity = getActivity();
132        setAccount(activity, "account3@mockuiprovider.com");
133        final Message refMessage = getRefMessage(activity.getContentResolver());
134        final String customFrom = "CUSTOMaccount3@mockuiprovider.com";
135        refMessage.setFrom("account3@mockuiprovider.com");
136        refMessage.setTo("someotheraccount1@mockuiprovider.com, "
137                + "someotheraccount2@mockuiprovider.com, someotheraccount3@mockuiprovider.com, "
138                + customFrom);
139        refMessage.setReplyTo(customFrom);
140
141        ReplyFromAccount a = new ReplyFromAccount(mAccount, mAccount.uri, customFrom,
142                customFrom, customFrom, true, true);
143        JSONArray array = new JSONArray();
144        array.put(a.serialize());
145        mAccount.accountFromAddresses = array.toString();
146        final ReplyFromAccount currentAccount = new ReplyFromAccount(mAccount, mAccount.uri,
147                mAccount.getEmailAddress(), mAccount.getEmailAddress(), customFrom, true, false);
148        runTestOnUiThread(new Runnable() {
149            @Override
150            public void run() {
151                activity.mFromSpinner = new FromAddressSpinner(activity);
152                activity.mFromSpinner.setCurrentAccount(currentAccount);
153                activity.mFromSpinner.initialize(ComposeActivity.REPLY_ALL,
154                        currentAccount.account, EMPTY_ACCOUNT_LIST, null);
155
156                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY_ALL);
157                String[] to = activity.getToAddresses();
158                String[] cc = activity.getCcAddresses();
159                String[] bcc = activity.getBccAddresses();
160                String toAsString = TextUtils.join(",", to);
161                assertEquals(3, to.length);
162                assertFalse(toAsString.contains(customFrom));
163                assertEquals(0, cc.length);
164                assertEquals(0, bcc.length);
165            }
166        });
167    }
168
169    /**
170     * Test the cases where: The user sent a message to one of
171     * their custom froms and just replied to that message
172     */
173    public void testRecipientsRefReplyAllOnlyAccount() throws Throwable {
174        final ComposeActivity activity = getActivity();
175        setAccount(activity, "account3@mockuiprovider.com");
176        final Message refMessage = getRefMessage(activity.getContentResolver());
177        refMessage.setFrom("account3@mockuiprovider.com");
178        refMessage.setTo("account3@mockuiprovider.com");
179        final Account account = mAccount;
180
181        final ReplyFromAccount currentAccount = new ReplyFromAccount(mAccount, mAccount.uri,
182                mAccount.getEmailAddress(), mAccount.getEmailAddress(), mAccount.getEmailAddress(),
183                true, false);
184
185        runTestOnUiThread(new Runnable() {
186            @Override
187            public void run() {
188                activity.mFromSpinner = new FromAddressSpinner(activity);
189                activity.mFromSpinner.setCurrentAccount(currentAccount);
190                activity.mFromSpinner.initialize(ComposeActivity.REPLY_ALL,
191                        currentAccount.account, EMPTY_ACCOUNT_LIST, null);
192
193                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY_ALL);
194                String[] to = activity.getToAddresses();
195                String[] cc = activity.getCcAddresses();
196                String[] bcc = activity.getBccAddresses();
197                String toAsString = TextUtils.join(",", to);
198                assertEquals(1, to.length);
199                assertTrue(toAsString.contains(account.getEmailAddress()));
200                assertEquals(0, cc.length);
201                assertEquals(0, bcc.length);
202            }
203        });
204    }
205
206    /**
207     * Test the cases where: The user sent a message to one of
208     * their custom froms and just replied to that message
209     */
210    public void testRecipientsRefReplyAllOnlyCustomFrom() throws Throwable {
211        final ComposeActivity activity = getActivity();
212        setAccount(activity, "account3@mockuiprovider.com");
213        final Message refMessage = getRefMessage(activity.getContentResolver());
214        final String customFrom = "CUSTOMaccount3@mockuiprovider.com";
215        refMessage.setFrom("account3@mockuiprovider.com");
216        refMessage.setTo(customFrom);
217
218        ReplyFromAccount a = new ReplyFromAccount(mAccount, mAccount.uri, customFrom,
219                customFrom, customFrom, true, true);
220        JSONArray array = new JSONArray();
221        array.put(a.serialize());
222        mAccount.accountFromAddresses = array.toString();
223        final ReplyFromAccount currentAccount = new ReplyFromAccount(mAccount, mAccount.uri,
224                mAccount.getEmailAddress(), mAccount.getEmailAddress(), customFrom, true, false);
225
226        runTestOnUiThread(new Runnable() {
227            @Override
228            public void run() {
229                activity.mFromSpinner = new FromAddressSpinner(activity);
230                activity.mFromSpinner.setCurrentAccount(currentAccount);
231                activity.mFromSpinner.initialize(ComposeActivity.REPLY_ALL,
232                        currentAccount.account, EMPTY_ACCOUNT_LIST, null);
233
234                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY_ALL);
235                String[] to = activity.getToAddresses();
236                String[] cc = activity.getCcAddresses();
237                String[] bcc = activity.getBccAddresses();
238                String toAsString = TextUtils.join(",", to);
239                assertEquals(1, to.length);
240                assertTrue(toAsString.contains(customFrom));
241                assertEquals(0, cc.length);
242                assertEquals(0, bcc.length);
243            }
244        });
245    }
246
247    public void testReply() throws Throwable {
248        final ComposeActivity activity = getActivity();
249        setAccount(activity, "account0@mockuiprovider.com");
250        final Message refMessage = getRefMessage(activity.getContentResolver());
251        final String refMessageFromAccount = refMessage.getFrom();
252
253        runTestOnUiThread(new Runnable() {
254            @Override
255            public void run() {
256                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY);
257                String[] to = activity.getToAddresses();
258                String[] cc = activity.getCcAddresses();
259                String[] bcc = activity.getBccAddresses();
260                assertTrue(to.length == 1);
261                assertEquals(refMessageFromAccount,
262                        Rfc822Tokenizer.tokenize(to[0])[0].getAddress());
263                assertTrue(cc.length == 0);
264                assertTrue(bcc.length == 0);
265            }
266        });
267    }
268
269    public void testReplyWithReplyTo() throws Throwable {
270        final ComposeActivity activity = getActivity();
271        setAccount(activity, "account1@mockuiprovider.com");
272        final Message refMessage = getRefMessage(activity.getContentResolver());
273        refMessage.setReplyTo("replytofromaccount1@mock.com");
274        final String refReplyToAccount = refMessage.getReplyTo();
275
276        runTestOnUiThread(new Runnable() {
277            @Override
278            public void run() {
279                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY);
280                String[] to = activity.getToAddresses();
281                String[] cc = activity.getCcAddresses();
282                String[] bcc = activity.getBccAddresses();
283                assertTrue(to.length == 1);
284                assertEquals(refReplyToAccount,
285                        Rfc822Tokenizer.tokenize(to[0])[0].getAddress());
286                assertTrue(cc.length == 0);
287                assertTrue(bcc.length == 0);
288            }
289        });
290    }
291
292    /**
293     * Reply to a message you sent yourself to some recipients in the to field.
294     */
295    public void testReplyToSelf() throws Throwable {
296        final ComposeActivity activity = getActivity();
297        setAccount(activity, "account1@mockuiprovider.com");
298        final Message refMessage = getRefMessage(activity.getContentResolver());
299        refMessage.setFrom("Account Test <account1@mockuiprovider.com>");
300        refMessage.setTo("test1@gmail.com");
301        runTestOnUiThread(new Runnable() {
302            @Override
303            public void run() {
304                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY);
305                String[] to = activity.getToAddresses();
306                String[] cc = activity.getCcAddresses();
307                String[] bcc = activity.getBccAddresses();
308                assertTrue(to.length == 1);
309                String toAsString = TextUtils.join(",", to);
310                assertTrue(toAsString.contains("test1@gmail.com"));
311                assertTrue(cc.length == 0);
312                assertTrue(bcc.length == 0);
313            }
314        });
315    }
316
317    /**
318     * Reply-all to a message you sent.
319     */
320    public void testReplyAllToSelf() throws Throwable {
321        final ComposeActivity activity = getActivity();
322        setAccount(activity, "account1@mockuiprovider.com");
323        final Message refMessage = getRefMessage(activity.getContentResolver());
324        refMessage.setFrom("Account Test <account1@mockuiprovider.com>");
325        refMessage.setTo("test1@gmail.com, test2@gmail.com");
326        runTestOnUiThread(new Runnable() {
327            @Override
328            public void run() {
329                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY_ALL);
330                String[] to = activity.getToAddresses();
331                String[] cc = activity.getCcAddresses();
332                String[] bcc = activity.getBccAddresses();
333                assertEquals(to.length, 2);
334                String toAsString = TextUtils.join(",", to);
335                assertTrue(toAsString.contains("test1@gmail.com"));
336                assertTrue(toAsString.contains("test2@gmail.com"));
337                assertTrue(cc.length == 0);
338                assertTrue(bcc.length == 0);
339            }
340        });
341    }
342
343    /**
344     * Reply-all to a message you sent with some to and some CC recips.
345     */
346    public void testReplyAllToSelfWithCc() throws Throwable {
347        final ComposeActivity activity = getActivity();
348        setAccount(activity, "account1@mockuiprovider.com");
349        final Message refMessage = getRefMessage(activity.getContentResolver());
350        refMessage.setFrom("Account Test <account1@mockuiprovider.com>");
351        refMessage.setTo("test1@gmail.com, test2@gmail.com");
352        refMessage.setCc("testcc@gmail.com");
353        runTestOnUiThread(new Runnable() {
354            @Override
355            public void run() {
356                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY_ALL);
357                String[] to = activity.getToAddresses();
358                String[] cc = activity.getCcAddresses();
359                String[] bcc = activity.getBccAddresses();
360                assertEquals(to.length, 2);
361                String toAsString = TextUtils.join(",", to);
362                assertTrue(toAsString.contains("test1@gmail.com"));
363                assertTrue(toAsString.contains("test2@gmail.com"));
364                String ccAsString = TextUtils.join(",", cc);
365                assertTrue(ccAsString.contains("testcc@gmail.com"));
366                assertTrue(cc.length == 1);
367                assertTrue(bcc.length == 0);
368            }
369        });
370    }
371
372    public void testReplyAll() throws Throwable {
373        final ComposeActivity activity = getActivity();
374        setAccount(activity, "account0@mockuiprovider.com");
375        final Message refMessage = getRefMessage(activity.getContentResolver());
376        final String[] refMessageTo = TextUtils.split(refMessage.getTo(), ",");
377        final String refMessageFromAccount = refMessage.getFrom();
378
379        runTestOnUiThread(new Runnable() {
380            @Override
381            public void run() {
382                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY_ALL);
383                String[] to = activity.getToAddresses();
384                String[] cc = activity.getCcAddresses();
385                String[] bcc = activity.getBccAddresses();
386                assertTrue(to.length == 1);
387                assertEquals(refMessageFromAccount,
388                        Rfc822Tokenizer.tokenize(to[0])[0].getAddress());
389                assertEquals(cc.length, refMessageTo.length);
390                assertTrue(bcc.length == 0);
391            }
392        });
393    }
394
395    public void testReplyAllWithReplyTo() throws Throwable {
396        final ComposeActivity activity = getActivity();
397        setAccount(activity, "account1@mockuiprovider.com");
398        final Message refMessage = getRefMessage(activity.getContentResolver());
399        refMessage.setReplyTo("replytofromaccount1@mock.com");
400        final String[] refMessageTo = TextUtils.split(refMessage.getTo(), ",");
401        final String refReplyToAccount = refMessage.getReplyTo();
402
403        runTestOnUiThread(new Runnable() {
404            @Override
405            public void run() {
406                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY_ALL);
407                String[] to = activity.getToAddresses();
408                String[] cc = activity.getCcAddresses();
409                String[] bcc = activity.getBccAddresses();
410                assertTrue(to.length == 1);
411                assertEquals(refReplyToAccount, Rfc822Tokenizer.tokenize(to[0])[0].getAddress());
412                assertEquals(cc.length, refMessageTo.length);
413                assertTrue(bcc.length == 0);
414            }
415        });
416    }
417
418    private static Message getRefMessageWithCc(long messageId, boolean hasAttachments) {
419        MatrixCursor cursor = new MatrixCursorWithCachedColumns(UIProvider.MESSAGE_PROJECTION);
420        final String messageUri = "content://xxx/message/" + messageId;
421        Object[] messageValues = new Object[UIProvider.MESSAGE_PROJECTION.length];
422        messageValues[UIProvider.MESSAGE_ID_COLUMN] = Long.valueOf(messageId);
423        messageValues[UIProvider.MESSAGE_URI_COLUMN] = messageUri;
424        messageValues[UIProvider.MESSAGE_SUBJECT_COLUMN] = "Message subject";
425        messageValues[UIProvider.MESSAGE_SNIPPET_COLUMN] = "SNIPPET";
426        String html = "<html><body><b><i>This is some html!!!</i></b></body></html>";
427        messageValues[UIProvider.MESSAGE_BODY_HTML_COLUMN] = html;
428        messageValues[UIProvider.MESSAGE_BODY_TEXT_COLUMN] = Html.fromHtml(html);
429        messageValues[UIProvider.MESSAGE_HAS_ATTACHMENTS_COLUMN] = hasAttachments ? 1 : 0;
430        messageValues[UIProvider.MESSAGE_DATE_RECEIVED_MS_COLUMN] = new Date().getTime();
431        messageValues[UIProvider.MESSAGE_ATTACHMENT_LIST_URI_COLUMN] = messageUri
432                + "/getAttachments";
433        messageValues[UIProvider.MESSAGE_TO_COLUMN] = "account1@mock.com, account2@mock.com";
434        messageValues[UIProvider.MESSAGE_FROM_COLUMN] = "fromaccount1@mock.com";
435        messageValues[UIProvider.MESSAGE_CC_COLUMN] = "accountcc1@mock.com, accountcc2@mock.com";
436        cursor.addRow(messageValues);
437        cursor.moveToFirst();
438        return new Message(cursor);
439    }
440
441    public void testReplyAllWithCc() throws Throwable {
442        final ComposeActivity activity = getActivity();
443        setAccount(activity, "account1@mockuiprovider.com");
444        final Message refMessage = getRefMessageWithCc(0, false);
445        final String[] refMessageTo = TextUtils.split(refMessage.getTo(), ",");
446        final String[] refMessageCc = TextUtils.split(refMessage.getCc(), ",");
447        final String refMessageFromAccount = refMessage.getFrom();
448
449        runTestOnUiThread(new Runnable() {
450            @Override
451            public void run() {
452                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY_ALL);
453                String[] to = activity.getToAddresses();
454                String[] cc = activity.getCcAddresses();
455                String[] bcc = activity.getBccAddresses();
456                assertTrue(to.length == 1);
457                assertEquals(refMessageFromAccount,
458                        Rfc822Tokenizer.tokenize(to[0])[0].getAddress());
459                assertEquals(cc.length, refMessageTo.length + refMessageCc.length);
460                HashSet<String> ccMap = new HashSet<String>();
461                for (String recip : cc) {
462                    ccMap.add(Rfc822Tokenizer.tokenize(recip.trim())[0].getAddress());
463                }
464                for (String toRecip : refMessageTo) {
465                    assertTrue(ccMap.contains(toRecip.trim()));
466                }
467                for (String ccRecip : refMessageCc) {
468                    assertTrue(ccMap.contains(ccRecip.trim()));
469                }
470                assertTrue(bcc.length == 0);
471            }
472        });
473    }
474
475    public void testForward() throws Throwable {
476        final ComposeActivity activity = getActivity();
477        setAccount(activity, "account0@mockuiprovider.com");
478        final Message refMessage = getRefMessage(activity.getContentResolver());
479
480        runTestOnUiThread(new Runnable() {
481            @Override
482            public void run() {
483                activity.initReplyRecipients(refMessage, ComposeActivity.FORWARD);
484                String[] to = activity.getToAddresses();
485                String[] cc = activity.getCcAddresses();
486                String[] bcc = activity.getBccAddresses();
487                assertEquals(to.length, 0);
488                assertEquals(cc.length, 0);
489                assertEquals(bcc.length, 0);
490            }
491        });
492    }
493
494    public void testCompose() throws Throwable {
495        final ComposeActivity activity = getActivity();
496        setAccount(activity, "account0@mockuiprovider.com");
497        final Message refMessage = getRefMessage(activity.getContentResolver());
498
499        runTestOnUiThread(new Runnable() {
500            @Override
501            public void run() {
502                activity.initReplyRecipients(refMessage, ComposeActivity.COMPOSE);
503                String[] to = activity.getToAddresses();
504                String[] cc = activity.getCcAddresses();
505                String[] bcc = activity.getBccAddresses();
506                assertEquals(to.length, 0);
507                assertEquals(cc.length, 0);
508                assertEquals(bcc.length, 0);
509            }
510        });
511    }
512
513    /**
514     * Test the cases where: The user is replying to a message they sent
515     */
516    public void testRecipientsRefMessageReplyToSelf() throws Throwable {
517        final ComposeActivity activity = getActivity();
518        setAccount(activity, "account0@mockuiprovider.com");
519        final Message refMessage = getRefMessage(activity.getContentResolver());
520        refMessage.setFrom("account0@mockuiprovider.com");
521        refMessage.setTo("someotheraccount@mockuiprovider.com");
522
523        runTestOnUiThread(new Runnable() {
524            @Override
525            public void run() {
526                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY);
527                String[] to = activity.getToAddresses();
528                String[] cc = activity.getCcAddresses();
529                String[] bcc = activity.getBccAddresses();
530                assertEquals(to.length, 1);
531                assertTrue(to[0].contains(refMessage.getTo()));
532                assertEquals(cc.length, 0);
533                assertEquals(bcc.length, 0);
534            }
535        });
536    }
537
538    /**
539     * Test the cases where:
540     * The user is replying to a message sent from one of their custom froms
541     */
542    public void brokentestRecipientsRefMessageReplyToCustomFrom() throws Throwable {
543        final ComposeActivity activity = getActivity();
544        setAccount(activity, "account1@mockuiprovider.com");
545        final Message refMessage = getRefMessage(activity.getContentResolver());
546        refMessage.setFrom("CUSTOMaccount1@mockuiprovider.com");
547        refMessage.setTo("someotheraccount@mockuiprovider.com");
548
549        ReplyFromAccount a = new ReplyFromAccount(mAccount, mAccount.uri, refMessage.getFrom(),
550                refMessage.getFrom(), refMessage.getFrom(), true, true);
551        JSONArray array = new JSONArray();
552        array.put(a.serialize());
553        mAccount.accountFromAddresses = array.toString();
554        final ReplyFromAccount currentAccount = new ReplyFromAccount(mAccount, mAccount.uri,
555                mAccount.getEmailAddress(), mAccount.getEmailAddress(), mAccount.getEmailAddress(),
556                true, false);
557
558        runTestOnUiThread(new Runnable() {
559            @Override
560            public void run() {
561                activity.mFromSpinner = new FromAddressSpinner(activity);
562                activity.mFromSpinner.setCurrentAccount(currentAccount);
563                activity.mFromSpinner.initialize(ComposeActivity.REPLY, currentAccount.account,
564                        EMPTY_ACCOUNT_LIST, null);
565
566                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY);
567                String[] to = activity.getToAddresses();
568                String[] cc = activity.getCcAddresses();
569                String[] bcc = activity.getBccAddresses();
570                assertEquals(to.length, 1);
571                assertTrue(to[0].contains(refMessage.getTo()));
572                assertEquals(cc.length, 0);
573                assertEquals(bcc.length, 0);
574            }
575        });
576    }
577
578    /**
579     * Test the cases where:
580     * The user is replying to a message sent from one of their custom froms
581     */
582    public void brokentestRecipientsRefMessageReplyAllCustomFrom() throws Throwable {
583        final ComposeActivity activity = getActivity();
584        setAccount(activity, "account1@mockuiprovider.com");
585        final Message refMessage = getRefMessage(activity.getContentResolver());
586        final String customFrom = "CUSTOMaccount1@mockuiprovider.com";
587        refMessage.setFrom("senderaccount@mockuiprovider.com");
588        refMessage.setTo("someotheraccount@mockuiprovider.com, "
589                + "someotheraccount2@mockuiprovider.com, someotheraccount4@mockuiprovider.com, "
590                + customFrom);
591
592        ReplyFromAccount a = new ReplyFromAccount(mAccount, mAccount.uri, customFrom,
593                customFrom, customFrom, true, true);
594        JSONArray array = new JSONArray();
595        array.put(a.serialize());
596        mAccount.accountFromAddresses = array.toString();
597        final ReplyFromAccount currentAccount = new ReplyFromAccount(mAccount, mAccount.uri,
598                mAccount.getEmailAddress(), mAccount.getEmailAddress(), mAccount.getEmailAddress(),
599                true, false);
600
601        runTestOnUiThread(new Runnable() {
602            @Override
603            public void run() {
604                activity.mFromSpinner = new FromAddressSpinner(activity);
605                activity.mFromSpinner.setCurrentAccount(currentAccount);
606                activity.mFromSpinner.initialize(ComposeActivity.REPLY_ALL,
607                        currentAccount.account, EMPTY_ACCOUNT_LIST, null);
608
609                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY_ALL);
610                String[] to = activity.getToAddresses();
611                String[] cc = activity.getCcAddresses();
612                String[] bcc = activity.getBccAddresses();
613                String toAsString = TextUtils.join(",", to);
614                String ccAsString = TextUtils.join(",", cc);
615                String bccAsString = TextUtils.join(",", bcc);
616                assertEquals(to.length, 1);
617                assertFalse(toAsString.contains(customFrom));
618                assertFalse(ccAsString.contains(customFrom));
619                assertFalse(bccAsString.contains(customFrom));
620            }
621        });
622    }
623
624    /**
625     * Test the cases where:
626     * The user is replying to a message sent from one of their custom froms
627     */
628    public void brokentestRecipientsRefMessageReplyAllCustomFromThisAccount() throws Throwable {
629        final ComposeActivity activity = getActivity();
630        setAccount(activity, "account1@mockuiprovider.com");
631        final Message refMessage = getRefMessage(activity.getContentResolver());
632        final String customFrom = "CUSTOMaccount1@mockuiprovider.com";
633        refMessage.setFrom("account1@mockuiprovider.com");
634        refMessage.setTo("someotheraccount@mockuiprovider.com, "
635                + "someotheraccount2@mockuiprovider.com, someotheraccount4@mockuiprovider.com, "
636                + customFrom);
637
638        ReplyFromAccount a = new ReplyFromAccount(mAccount, mAccount.uri, customFrom,
639                customFrom, customFrom, true, true);
640        JSONArray array = new JSONArray();
641        array.put(a.serialize());
642        mAccount.accountFromAddresses = array.toString();
643        final ReplyFromAccount currentAccount = new ReplyFromAccount(mAccount, mAccount.uri,
644                mAccount.getEmailAddress(), mAccount.getEmailAddress(), mAccount.getEmailAddress(),
645                true, false);
646
647        runTestOnUiThread(new Runnable() {
648            @Override
649            public void run() {
650                activity.mFromSpinner = new FromAddressSpinner(activity);
651                activity.mFromSpinner.setCurrentAccount(currentAccount);
652                activity.mFromSpinner.initialize(ComposeActivity.REPLY_ALL,
653                        currentAccount.account, EMPTY_ACCOUNT_LIST, null);
654
655                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY_ALL);
656                String[] to = activity.getToAddresses();
657                String[] cc = activity.getCcAddresses();
658                String[] bcc = activity.getBccAddresses();
659                String toAsString = TextUtils.join(",", to);
660                String ccAsString = TextUtils.join(",", cc);
661                String bccAsString = TextUtils.join(",", bcc);
662                // Should have the same count as the original message.
663                assertEquals(to.length, 3);
664                assertFalse(toAsString.contains(customFrom));
665                assertFalse(ccAsString.contains(customFrom));
666                assertFalse(bccAsString.contains(customFrom));
667            }
668        });
669    }
670
671    // Test replying to a message in the first account in the list, and verify that
672    // the Compose Activity's from account for the reply is correct
673    public void testReplySendingAccount0() throws Throwable {
674        final Context context = getInstrumentation().getContext();
675        // Get the test account
676        final Account currentAccount = getAccountForName(context, "account0@mockuiprovider.com");
677
678        // Get the message to be replied to
679        final Message refMessage =
680                getRefMessage(context.getContentResolver(), currentAccount.folderListUri);
681
682        // Create the reply intent
683        final Intent replyIntent =
684                ComposeActivity.updateActionIntent(currentAccount, refMessage.uri,
685                        ComposeActivity.REPLY, new Intent());
686
687        setActivityIntent(replyIntent);
688
689        final ComposeActivity activity = getActivity();
690        final String refMessageFromAccount = refMessage.getFrom();
691
692        runTestOnUiThread(new Runnable() {
693            @Override
694            public void run() {
695                String[] to = activity.getToAddresses();
696                String[] cc = activity.getCcAddresses();
697                String[] bcc = activity.getBccAddresses();
698                Account fromAccount = activity.getFromAccount();
699                assertEquals(1, to.length);
700                assertEquals(refMessageFromAccount,
701                        Rfc822Tokenizer.tokenize(to[0])[0].getAddress());
702                assertEquals(0, cc.length);
703                assertEquals(0, bcc.length);
704                assertEquals("account0@mockuiprovider.com", fromAccount.getEmailAddress());
705            }
706        });
707    }
708
709    // Test replying to a message in the third account in the list, and verify that
710    // the Compose Activity's from account for the reply is correct
711    public void testReplySendingAccount1() throws Throwable {
712        final Context context = getInstrumentation().getContext();
713        // Get the test account
714        final Account currentAccount = getAccountForName(context, "account2@mockuiprovider.com");
715
716        // Get the message to be replied to
717        final Message refMessage =
718                getRefMessage(context.getContentResolver(), currentAccount.folderListUri);
719
720        // Create the reply intent
721        final Intent replyIntent =
722                ComposeActivity.updateActionIntent(currentAccount, refMessage.uri,
723                        ComposeActivity.REPLY, new Intent());
724
725        setActivityIntent(replyIntent);
726
727        final ComposeActivity activity = getActivity();
728        Account fromAccount = activity.getFromAccount();
729
730        final String refMessageFromAccount = refMessage.getFrom();
731
732        runTestOnUiThread(new Runnable() {
733            @Override
734            public void run() {
735                String[] to = activity.getToAddresses();
736                String[] cc = activity.getCcAddresses();
737                String[] bcc = activity.getBccAddresses();
738                Account fromAccount = activity.getFromAccount();
739                assertEquals(1, to.length);
740                assertEquals(refMessageFromAccount,
741                        Rfc822Tokenizer.tokenize(to[0])[0].getAddress());
742                assertEquals(0, cc.length);
743                assertEquals(0, bcc.length);
744                assertEquals("account2@mockuiprovider.com", fromAccount.getEmailAddress());
745            }
746        });
747    }
748
749    private static String encodeMailtoParam(String s) throws UnsupportedEncodingException {
750        return URLEncoder.encode(s, "UTF-8").replace("+", "%20");
751    }
752
753    public void testMailto() throws Throwable {
754        final String to = "foo@bar.com";
755        final String cc = "baz@baf.com";
756        final String subject = "hello world";
757        final String body = "Dear foo,\nGoodbye.\n--me";
758
759        final String mailto = String.format("mailto:%s?cc=%s&subject=%s&body=%s",
760                encodeMailtoParam(to), encodeMailtoParam(cc), encodeMailtoParam(subject),
761                encodeMailtoParam(body));
762
763        final Intent mailtoIntent = new Intent(Intent.ACTION_VIEW,
764                Uri.parse(mailto));
765        setActivityIntent(mailtoIntent);
766
767        final ComposeActivity activity = getActivity();
768
769        runTestOnUiThread(new Runnable() {
770            @Override
771            public void run() {
772                final String resultTo[] = activity.getToAddresses();
773                assertEquals(1, resultTo.length);
774                assertEquals(to, Rfc822Tokenizer.tokenize(resultTo[0])[0].getAddress());
775
776                final String resultCc[] = activity.getCcAddresses();
777                assertEquals(1, resultCc.length);
778                assertEquals(cc, Rfc822Tokenizer.tokenize(resultCc[0])[0].getAddress());
779
780                assertEquals(subject, activity.getSubject());
781// the result is HTML-wrapped in a way that's not trivial to test, so disabled for now
782//                assertEquals(body, activity.getBodyHtml());
783            }
784        });
785    }
786
787    // Test a mailto VIEW Intent, with an account specified in JSON format
788    public void testMailToAccountJSON() throws Throwable {
789        final Context context = getInstrumentation().getContext();
790        // Get the test account
791        final Account currentAccount = getAccountForName(context, "account2@mockuiprovider.com");
792
793        // Create the mailto intent
794        final Intent mailtoIntent =
795                new Intent(Intent.ACTION_VIEW, Uri.parse("mailto:test@localhost.com"));
796        mailtoIntent.putExtra(Utils.EXTRA_ACCOUNT, currentAccount.serialize());
797
798        setActivityIntent(mailtoIntent);
799
800        final ComposeActivity activity = getActivity();
801        Account fromAccount = activity.getFromAccount();
802
803        runTestOnUiThread(new Runnable() {
804            @Override
805            public void run() {
806                String[] to = activity.getToAddresses();
807                String[] cc = activity.getCcAddresses();
808                String[] bcc = activity.getBccAddresses();
809                Account fromAccount = activity.getFromAccount();
810                assertEquals( 1, to.length);
811                assertEquals("test@localhost.com",
812                        Rfc822Tokenizer.tokenize(to[0])[0].getAddress());
813                assertEquals(0, cc.length);
814                assertEquals(0, bcc.length);
815                assertEquals("account2@mockuiprovider.com", fromAccount.getEmailAddress());
816            }
817        });
818    }
819
820    // Test a COMPOSE Intent, with an account specified in parcel format
821    public void testMailToAccount() throws Throwable {
822        final Context context = getInstrumentation().getContext();
823        // Get the test account
824        final Account currentAccount = getAccountForName(context, "account2@mockuiprovider.com");
825
826        // Create the mailto intent
827        Intent intent = new Intent(context, ComposeActivity.class);
828        intent.putExtra(ComposeActivity.EXTRA_FROM_EMAIL_TASK, true);
829        intent.putExtra(ComposeActivity.EXTRA_ACTION, ComposeActivity.COMPOSE);
830        intent.putExtra(Utils.EXTRA_ACCOUNT, currentAccount);
831        intent.putExtra(ComposeActivity.EXTRA_TO, "test@localhost.com");
832
833        setActivityIntent(intent);
834
835        final ComposeActivity activity = getActivity();
836        Account fromAccount = activity.getFromAccount();
837
838        runTestOnUiThread(new Runnable() {
839            @Override
840            public void run() {
841                String[] to = activity.getToAddresses();
842                String[] cc = activity.getCcAddresses();
843                String[] bcc = activity.getBccAddresses();
844                Account fromAccount = activity.getFromAccount();
845                assertEquals( 1, to.length);
846                assertEquals("test@localhost.com",
847                        Rfc822Tokenizer.tokenize(to[0])[0].getAddress());
848                assertEquals(0, cc.length);
849                assertEquals(0, bcc.length);
850                assertEquals("account2@mockuiprovider.com", fromAccount.getEmailAddress());
851            }
852        });
853    }
854
855    // Test a mailto VIEW Intent, with no account specified.  The fromAccount should default to the
856    // last sent account.
857    public void testMailToAccountWithLastSentAccount() throws Throwable {
858        final Context context = getInstrumentation().getContext();
859
860        // Set the last sent account to account0
861        final Account lastSentAccount = getAccountForName(context, "account1@mockuiprovider.com");
862        MailAppProvider appProvider = MailAppProvider.getInstance();
863        appProvider.setLastSentFromAccount(lastSentAccount.uri.toString());
864
865        // Create the mailto intent
866        final Intent mailtoIntent =
867                new Intent(Intent.ACTION_VIEW, Uri.parse("mailto:test@localhost.com"));
868
869        setActivityIntent(mailtoIntent);
870
871        final ComposeActivity activity = getActivity();
872        Account fromAccount = activity.getFromAccount();
873
874        runTestOnUiThread(new Runnable() {
875            @Override
876            public void run() {
877                String[] to = activity.getToAddresses();
878                String[] cc = activity.getCcAddresses();
879                String[] bcc = activity.getBccAddresses();
880                Account fromAccount = activity.getFromAccount();
881                assertEquals( 1, to.length);
882                assertEquals("test@localhost.com",
883                        Rfc822Tokenizer.tokenize(to[0])[0].getAddress());
884                assertEquals(0, cc.length);
885                assertEquals(0, bcc.length);
886                assertEquals("account1@mockuiprovider.com", fromAccount.getEmailAddress());
887            }
888        });
889    }
890
891    private static String createAttachmentsJson() {
892        Attachment attachment1 = new Attachment();
893        attachment1.contentUri = Uri.parse("www.google.com");
894        attachment1.setContentType("img/jpeg");
895        attachment1.setName("attachment1");
896        Attachment attachment2 = new Attachment();
897        attachment2.contentUri = Uri.parse("www.google.com");
898        attachment2.setContentType("img/jpeg");
899        attachment2.setName("attachment2");
900        JSONArray attachments = new JSONArray();
901        try {
902            attachments.put(attachment1.toJSON());
903            attachments.put(attachment2.toJSON());
904        } catch (JSONException e) {
905            assertTrue(false);
906        }
907        return attachments.toString();
908    }
909
910    // First test: switch reply to reply all to fwd, 1 to recipient, 1 cc recipient.
911    public void testChangeModes0() throws Throwable {
912        final ComposeActivity activity = getActivity();
913        setAccount(activity, "account0@mockuiprovider.com");
914        final Message refMessage = getRefMessage(activity.getContentResolver());
915        refMessage.setFrom("fromaccount@mockuiprovider.com");
916        refMessage.setTo("account0@mockuiprovider.com");
917        refMessage.setCc("ccaccount@mockuiprovider.com");
918        runTestOnUiThread(new Runnable() {
919            @Override
920            public void run() {
921                activity.mRefMessage = refMessage;
922                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY);
923                String[] to = activity.getToAddresses();
924                String[] cc = activity.getCcAddresses();
925                String[] bcc = activity.getBccAddresses();
926                assertEquals(1, to.length);
927                assertTrue(to[0].contains(refMessage.getFrom()));
928                assertEquals(cc.length, 0);
929                assertEquals(bcc.length, 0);
930                activity.onNavigationItemSelected(1, ComposeActivity.REPLY_ALL);
931                assertEquals(activity.getToAddresses().length, 1);
932                assertTrue(activity.getToAddresses()[0].contains(refMessage.getFrom()));
933                assertEquals(activity.getCcAddresses().length, 1);
934                assertTrue(activity.getCcAddresses()[0].contains(refMessage.getCc()));
935                assertEquals(activity.getBccAddresses().length, 0);
936                activity.onNavigationItemSelected(2, ComposeActivity.FORWARD);
937                assertEquals(activity.getToAddresses().length, 0);
938                assertEquals(activity.getCcAddresses().length, 0);
939                assertEquals(activity.getBccAddresses().length, 0);
940            }
941        });
942    }
943
944    // Switch reply to reply all to fwd, 2 to recipients, 1 cc recipient.
945    public void testChangeModes1() throws Throwable {
946        final ComposeActivity activity = getActivity();
947        setAccount(activity, "account0@mockuiprovider.com");
948        final Message refMessage = getRefMessage(activity.getContentResolver());
949        refMessage.setFrom("fromaccount@mockuiprovider.com");
950        refMessage.setTo("account0@mockuiprovider.com, toaccount0@mockuiprovider.com");
951        refMessage.setCc("ccaccount@mockuiprovider.com");
952        runTestOnUiThread(new Runnable() {
953            @Override
954            public void run() {
955                activity.mRefMessage = refMessage;
956                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY);
957                String[] to = activity.getToAddresses();
958                String[] cc = activity.getCcAddresses();
959                String[] bcc = activity.getBccAddresses();
960                assertEquals(to.length, 1);
961                assertTrue(to[0].contains(refMessage.getFrom()));
962                assertEquals(cc.length, 0);
963                assertEquals(bcc.length, 0);
964                activity.onNavigationItemSelected(1, ComposeActivity.REPLY_ALL);
965                assertEquals(activity.getToAddresses().length, 1);
966                assertTrue(activity.getToAddresses()[0].contains(refMessage.getFrom()));
967                assertEquals(activity.getCcAddresses().length, 2);
968                assertTrue(activity.getCcAddresses()[0].contains(refMessage.getCc())
969                        || activity.getCcAddresses()[1].contains(refMessage.getCc()));
970                assertTrue(activity.getCcAddresses()[0].contains("toaccount0@mockuiprovider.com")
971                        || activity.getCcAddresses()[1]
972                        .contains("toaccount0@mockuiprovider.com"));
973                assertEquals(activity.getBccAddresses().length, 0);
974                activity.onNavigationItemSelected(2, ComposeActivity.FORWARD);
975                assertEquals(activity.getToAddresses().length, 0);
976                assertEquals(activity.getCcAddresses().length, 0);
977                assertEquals(activity.getBccAddresses().length, 0);
978            }
979        });
980    }
981
982    // Switch reply to reply all to fwd, 2 to recipients, 2 cc recipients.
983    public void testChangeModes2() throws Throwable {
984        final ComposeActivity activity = getActivity();
985        setAccount(activity, "account0@mockuiprovider.com");
986        final Message refMessage = getRefMessage(activity.getContentResolver());
987        refMessage.setFrom("fromaccount@mockuiprovider.com");
988        refMessage.setTo("account0@mockuiprovider.com, toaccount0@mockuiprovider.com");
989        refMessage.setCc("ccaccount@mockuiprovider.com, ccaccount2@mockuiprovider.com");
990        runTestOnUiThread(new Runnable() {
991            @Override
992            public void run() {
993                activity.mRefMessage = refMessage;
994                activity.initReplyRecipients(refMessage, ComposeActivity.REPLY);
995                String[] to = activity.getToAddresses();
996                String[] cc = activity.getCcAddresses();
997                String[] bcc = activity.getBccAddresses();
998                assertEquals(to.length, 1);
999                assertTrue(to[0].contains(refMessage.getFrom()));
1000                assertEquals(cc.length, 0);
1001                assertEquals(bcc.length, 0);
1002                activity.onNavigationItemSelected(1, ComposeActivity.REPLY_ALL);
1003                assertEquals(activity.getToAddresses().length, 1);
1004                assertTrue(activity.getToAddresses()[0].contains(refMessage.getFrom()));
1005                assertEquals(activity.getCcAddresses().length, 3);
1006                assertTrue(activity.getCcAddresses()[0].contains("ccaccount@mockuiprovider.com")
1007                        || activity.getCcAddresses()[1].contains("ccaccount@mockuiprovider.com")
1008                        || activity.getCcAddresses()[2].contains("ccaccount@mockuiprovider.com"));
1009                assertTrue(activity.getCcAddresses()[0].contains("ccaccount2@mockuiprovider.com")
1010                        || activity.getCcAddresses()[1].contains("ccaccount2@mockuiprovider.com")
1011                        || activity.getCcAddresses()[2].contains("ccaccount2@mockuiprovider.com"));
1012                assertTrue(activity.getCcAddresses()[0].contains("toaccount0@mockuiprovider.com")
1013                        || activity.getCcAddresses()[1].contains("toaccount0@mockuiprovider.com")
1014                        || activity.getCcAddresses()[2].contains("toaccount0@mockuiprovider.com"));
1015                assertTrue(activity.getCcAddresses()[0].contains("toaccount0@mockuiprovider.com")
1016                        || activity.getCcAddresses()[1]
1017                        .contains("toaccount0@mockuiprovider.com")
1018                        || activity.getCcAddresses()[2]
1019                        .contains("toaccount0@mockuiprovider.com"));
1020                assertEquals(activity.getBccAddresses().length, 0);
1021                activity.onNavigationItemSelected(2, ComposeActivity.FORWARD);
1022                assertEquals(activity.getToAddresses().length, 0);
1023                assertEquals(activity.getCcAddresses().length, 0);
1024                assertEquals(activity.getBccAddresses().length, 0);
1025            }
1026        });
1027    }
1028
1029    // Switch reply to reply all to fwd, 2 attachments.
1030    public void testChangeModes3() throws Throwable {
1031        final ComposeActivity activity = getActivity();
1032        setAccount(activity, "account0@mockuiprovider.com");
1033        final Message refMessage = getRefMessage(activity.getContentResolver());
1034        refMessage.hasAttachments = true;
1035        refMessage.attachmentsJson = createAttachmentsJson();
1036        runTestOnUiThread(new Runnable() {
1037            @Override
1038            public void run() {
1039                activity.mRefMessage = refMessage;
1040                activity.initAttachments(refMessage);
1041                assertEquals(activity.getAttachments().size(), 2);
1042                activity.onNavigationItemSelected(1, ComposeActivity.REPLY);
1043                assertEquals(activity.getAttachments().size(), 0);
1044                activity.onNavigationItemSelected(1, ComposeActivity.REPLY_ALL);
1045                assertEquals(activity.getAttachments().size(), 0);
1046                activity.onNavigationItemSelected(2, ComposeActivity.FORWARD);
1047                assertEquals(activity.getAttachments().size(), 2);
1048            }
1049        });
1050    }
1051
1052    // Test existence of % signs and basic functionality (to, cc, bcc, subject)
1053    public void testInitFromMailTo0() throws Throwable {
1054        final ComposeActivity activity = getActivity();
1055        final String input = "mailto:Test1@Test1.com?cc=Test2@Test2.com" +
1056                "&bcc=Test3@Test3.com&subject=Hello&body=Bye%25Bye";
1057
1058        runTestOnUiThread(new Runnable() {
1059            @Override
1060            public void run() {
1061                activity.initFromMailTo(input);
1062                assertEquals(1, activity.getToAddresses().length);
1063                assertTrue(activity.getToAddresses()[0].contains("Test1@Test1.com"));
1064                assertEquals(1, activity.getCcAddresses().length);
1065                assertTrue(activity.getCcAddresses()[0].contains("Test2@Test2.com"));
1066                assertEquals(1, activity.getBccAddresses().length);
1067                assertTrue(activity.getBccAddresses()[0].contains("Test3@Test3.com"));
1068                assertEquals("Hello", activity.getSubject());
1069                assertEquals("%25 should be decoded into %",
1070                        "Bye%Bye", activity.getBody().getText().toString());
1071            }
1072        });
1073    }
1074
1075    // Test existence of + and space in addition to %
1076    public void testInitFromMailTo1() throws Throwable {
1077        final ComposeActivity activity = getActivity();
1078        final String query = "Bye+Bye %";
1079        final Uri uri = Uri.parse("mailto:test@test.com?body=" + encodeMailtoParam(query));
1080
1081        runTestOnUiThread(new Runnable() {
1082            @Override
1083            public void run() {
1084                activity.initFromMailTo(uri.toString());
1085                assertEquals(query, activity.getBody().getText().toString());
1086            }
1087        });
1088    }
1089
1090    // Test existence of random set of url encoded characters
1091    public void testInitFromMailTo2() throws Throwable {
1092        final ComposeActivity activity = getActivity();
1093        final String query = "I'm TESTING @#$%^&*\"";
1094        final Uri uri = Uri.parse("mailto:test@test.com?body=" + encodeMailtoParam(query));
1095
1096        runTestOnUiThread(new Runnable() {
1097            @Override
1098            public void run() {
1099                activity.initFromMailTo(uri.toString());
1100                assertEquals(query, activity.getBody().getText().toString());
1101            }
1102        });
1103    }
1104}
1105