Welcome.java revision 95ad0f0e93d290b2c630a49a254e61f10b2783c8
1/* 2 * Copyright (C) 2008 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.activity; 18 19import com.android.email.Email; 20import com.android.email.ExchangeUtils; 21import com.android.email.R; 22import com.android.email.activity.setup.AccountSecurity; 23import com.android.email.activity.setup.AccountSetupBasics; 24import com.android.email.service.MailService; 25import com.android.emailcommon.Logging; 26import com.android.emailcommon.provider.Account; 27import com.android.emailcommon.provider.EmailContent; 28import com.android.emailcommon.provider.EmailContent.Message; 29import com.android.emailcommon.provider.Mailbox; 30import com.android.emailcommon.utility.EmailAsyncTask; 31import com.android.emailcommon.utility.Utility; 32import com.google.common.annotations.VisibleForTesting; 33 34import android.app.Activity; 35import android.content.Context; 36import android.content.Intent; 37import android.net.Uri; 38import android.os.Bundle; 39import android.text.TextUtils; 40import android.util.Log; 41 42/** 43 * The Welcome activity initializes the application and starts {@link EmailActivity}, or launch 44 * {@link AccountSetupBasics} if no accounts are configured. 45 * 46 * TOOD Show "your messages are on the way" message like gmail does during the inbox lookup. 47 */ 48public class Welcome extends Activity { 49 /* 50 * Commands for testing... 51 * Open 1 pane 52 adb shell am start -a android.intent.action.MAIN \ 53 -d '"content://ui.email.android.com/view/mailbox"' \ 54 -e DEBUG_PANE_MODE 1 55 56 * Open 2 pane 57 adb shell am start -a android.intent.action.MAIN \ 58 -d '"content://ui.email.android.com/view/mailbox"' \ 59 -e DEBUG_PANE_MODE 2 60 61 * Open an account (ID=1) in 2 pane 62 adb shell am start -a android.intent.action.MAIN \ 63 -d '"content://ui.email.android.com/view/mailbox?ACCOUNT_ID=1"' \ 64 -e DEBUG_PANE_MODE 2 65 66 * Open a message (account id=1, mailbox id=2, message id=3) 67 adb shell am start -a android.intent.action.MAIN \ 68 -d '"content://ui.email.android.com/view/mailbox?ACCOUNT_ID=1&MAILBOX_ID=2&MESSAGE_ID=3"' \ 69 -e DEBUG_PANE_MODE 2 70 71 * Open the combined starred on the combined view 72 adb shell am start -a android.intent.action.MAIN \ 73 -d '"content://ui.email.android.com/view/mailbox?ACCOUNT_ID=1152921504606846976&MAILBOX_ID=-4"' \ 74 -e DEBUG_PANE_MODE 2 75 */ 76 77 /** 78 * Extra for debugging. Set 1 to force one-pane. Set 2 to force two-pane. 79 */ 80 private static final String EXTRA_DEBUG_PANE_MODE = "DEBUG_PANE_MODE"; 81 82 private static final String VIEW_MAILBOX_INTENT_URL_PATH = "/view/mailbox"; 83 84 private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker(); 85 86 // Account reconciler is started from AccountResolver, which we may run multiple times, 87 // so remember if we did it already to prevent from running it twice. 88 private boolean mAccountsReconciled; 89 90 private long mAccountId; 91 private long mMailboxId; 92 private long mMessageId; 93 private String mAccountUuid; 94 95 private MailboxFinder mInboxFinder; 96 97 /** 98 * Launch this activity. Note: It's assumed that this activity is only called as a means to 99 * 'reset' the UI state; Because of this, it is always launched with FLAG_ACTIVITY_CLEAR_TOP, 100 * which will drop any other activities on the stack (e.g. AccountFolderList or MessageList). 101 */ 102 public static void actionStart(Activity fromActivity) { 103 Intent i = IntentUtilities.createRestartAppIntent(fromActivity, Welcome.class); 104 fromActivity.startActivity(i); 105 } 106 107 /** 108 * Create an Intent to open email activity. If <code>accountId</code> is not -1, the 109 * specified account will be automatically be opened when the activity starts. 110 */ 111 public static Intent createOpenAccountInboxIntent(Context context, long accountId) { 112 final Uri.Builder b = IntentUtilities.createActivityIntentUrlBuilder( 113 VIEW_MAILBOX_INTENT_URL_PATH); 114 IntentUtilities.setAccountId(b, accountId); 115 return IntentUtilities.createRestartAppIntent(b.build()); 116 } 117 118 /** 119 * Create an Intent to open a message. 120 */ 121 public static Intent createOpenMessageIntent(Context context, long accountId, 122 long mailboxId, long messageId) { 123 final Uri.Builder b = IntentUtilities.createActivityIntentUrlBuilder( 124 VIEW_MAILBOX_INTENT_URL_PATH); 125 IntentUtilities.setAccountId(b, accountId); 126 IntentUtilities.setMailboxId(b, mailboxId); 127 IntentUtilities.setMessageId(b, messageId); 128 return IntentUtilities.createRestartAppIntent(b.build()); 129 } 130 131 /** 132 * Open account's inbox. 133 */ 134 public static void actionOpenAccountInbox(Activity fromActivity, long accountId) { 135 fromActivity.startActivity(createOpenAccountInboxIntent(fromActivity, accountId)); 136 } 137 138 /** 139 * Create an {@link Intent} for account shortcuts. The returned intent stores the account's 140 * UUID rather than the account ID, which will be changed after account restore. 141 */ 142 public static Intent createAccountShortcutIntent(Context context, String uuid, long mailboxId) { 143 final Uri.Builder b = IntentUtilities.createActivityIntentUrlBuilder( 144 VIEW_MAILBOX_INTENT_URL_PATH); 145 IntentUtilities.setAccountUuid(b, uuid); 146 IntentUtilities.setMailboxId(b, mailboxId); 147 return IntentUtilities.createRestartAppIntent(b.build()); 148 } 149 150 /** 151 * If the {@link #EXTRA_DEBUG_PANE_MODE} extra is "1" or "2", return 1 or 2 respectively. 152 * Otherwise return 0. 153 * 154 * @see UiUtilities#setDebugPaneMode(int) 155 * @see UiUtilities#useTwoPane(Context) 156 */ 157 private static int getDebugPaneMode(Intent i) { 158 Bundle extras = i.getExtras(); 159 if (extras != null) { 160 String s = extras.getString(EXTRA_DEBUG_PANE_MODE); 161 if ("1".equals(s)) { 162 return 1; 163 } else if ("2".equals(s)) { 164 return 2; 165 } 166 } 167 return 0; 168 } 169 170 @Override 171 public void onCreate(Bundle icicle) { 172 super.onCreate(icicle); 173 ActivityHelper.debugSetWindowFlags(this); 174 175 // Reset the "accounts changed" notification, now that we're here 176 Email.setNotifyUiAccountsChanged(false); 177 178 // Because the app could be reloaded (for debugging, etc.), we need to make sure that 179 // ExchangeService gets a chance to start. There is no harm to starting it if it has 180 // already been started 181 // When the service starts, it reconciles EAS accounts. 182 // TODO More completely separate ExchangeService from Email app 183 ExchangeUtils.startExchangeService(this); 184 185 // Extract parameters from the intent. 186 final Intent intent = getIntent(); 187 mAccountId = IntentUtilities.getAccountIdFromIntent(intent); 188 mMailboxId = IntentUtilities.getMailboxIdFromIntent(intent); 189 mMessageId = IntentUtilities.getMessageIdFromIntent(intent); 190 mAccountUuid = IntentUtilities.getAccountUuidFromIntent(intent); 191 UiUtilities.setDebugPaneMode(getDebugPaneMode(intent)); 192 193 startAccountResolver(); 194 } 195 196 @Override 197 protected void onStop() { 198 // The activity no longer visible, which means the user opened some other app. 199 // Just close self and not launch EmailActivity. 200 stopInboxLookup(); 201 mTaskTracker.cancellAllInterrupt(); 202 super.onStop(); 203 finish(); 204 } 205 206 /** 207 * {@inheritDoc} 208 * 209 * When launching an activity from {@link Welcome}, we always want to set 210 * {@link Intent#FLAG_ACTIVITY_FORWARD_RESULT}. 211 */ 212 @Override 213 public void startActivity(Intent intent) { 214 intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 215 super.startActivity(intent); 216 } 217 218 private void startAccountResolver() { 219 new AccountResolver().executeParallel(); 220 } 221 222 /** 223 * Stop inbox lookup. This MSUT be called on the UI thread. 224 */ 225 private void stopInboxLookup() { 226 if (mInboxFinder != null) { 227 mInboxFinder.cancel(); 228 mInboxFinder = null; 229 } 230 } 231 232 /** 233 * Start inbox lookup. This MSUT be called on the UI thread. 234 */ 235 private void startInboxLookup() { 236 Log.i(Logging.LOG_TAG, "Inbox not found. Starting mailbox finder..."); 237 stopInboxLookup(); // Stop if already running -- it shouldn't be but just in case. 238 mInboxFinder = new MailboxFinder(this, mAccountId, Mailbox.TYPE_INBOX, 239 mMailboxFinderCallback); 240 mInboxFinder.startLookup(); 241 } 242 243 /** 244 * Determine which account to open with the given account ID and UUID. 245 * 246 * @return ID of the account to use. 247 */ 248 @VisibleForTesting 249 static long resolveAccountId(Context context, long inputAccountId, String inputUuid) { 250 final long accountId; 251 252 if (!TextUtils.isEmpty(inputUuid)) { 253 // If a UUID is specified, try to use it. 254 // If the UUID is invalid, accountId will be NO_ACCOUNT. 255 accountId = Account.getAccountIdFromUuid(context, inputUuid); 256 257 } else if (inputAccountId != Account.NO_ACCOUNT) { 258 // If a valid account ID is specified, just use it. 259 if (inputAccountId == Account.ACCOUNT_ID_COMBINED_VIEW 260 || Account.isValidId(context, inputAccountId)) { 261 accountId = inputAccountId; 262 } else { 263 accountId = Account.NO_ACCOUNT; 264 } 265 } else { 266 // Neither an accountID or a UUID is specified. 267 // Use the default, without showing the "account removed?" toast. 268 accountId = Account.getDefaultAccountId(context); 269 } 270 if (accountId != Account.NO_ACCOUNT) { 271 // Okay, the given account is valid. 272 return accountId; 273 } else { 274 // No, it's invalid. Show the warning toast and use the default. 275 Utility.showToast(context, R.string.toast_account_not_found); 276 return Account.getDefaultAccountId(context); 277 } 278 } 279 280 /** 281 * Determine which account to use according to the number of accounts already set up, 282 * {@link #mAccountId} and {@link #mAccountUuid}. 283 * 284 * <pre> 285 * 1. If there's no account configured, start account setup. 286 * 2. Otherwise detemine which account to open with {@link #resolveAccountId} and 287 * 2a. If the account doesn't have inbox yet, start inbox finder. 288 * 2b. Otherwise open the main activity. 289 * </pre> 290 */ 291 private class AccountResolver extends EmailAsyncTask<Void, Void, Void> { 292 private boolean mStartAccountSetup; 293 private boolean mStartInboxLookup; 294 295 public AccountResolver() { 296 super(mTaskTracker); 297 } 298 299 @Override 300 protected Void doInBackground(Void... params) { 301 final Activity activity = Welcome.this; 302 303 if (!mAccountsReconciled) { 304 mAccountsReconciled = true; 305 306 // Reconcile POP/IMAP accounts. EAS accounts are taken care of by ExchangeService. 307 // 308 // TODO Do we still really have to do it at startup? 309 // Now that we use the broadcast to detect system account changes, our database 310 // should always be in sync with the system accounts... 311 MailService.reconcilePopImapAccountsSync(activity); 312 } 313 314 final int numAccount = EmailContent.count(activity, Account.CONTENT_URI); 315 if (numAccount == 0) { 316 mStartAccountSetup = true; 317 } else { 318 mAccountId = resolveAccountId(activity, mAccountId, mAccountUuid); 319 if (Account.isNormalAccount(mAccountId) && 320 Mailbox.findMailboxOfType(activity, mAccountId, Mailbox.TYPE_INBOX) 321 == Mailbox.NO_MAILBOX) { 322 mStartInboxLookup = true; 323 } 324 } 325 return null; 326 } 327 328 @Override 329 protected void onPostExecute(Void noResult) { 330 final Activity activity = Welcome.this; 331 332 if (mStartAccountSetup) { 333 AccountSetupBasics.actionNewAccount(activity); 334 activity.finish(); 335 } else if (mStartInboxLookup) { 336 startInboxLookup(); 337 } else { 338 startEmailActivity(); 339 } 340 } 341 } 342 343 /** 344 * Start {@link EmailActivity} using {@link #mAccountId}, {@link #mMailboxId} and 345 * {@link #mMessageId}. 346 */ 347 private void startEmailActivity() { 348 final Intent i; 349 if (mMessageId != Message.NO_MESSAGE) { 350 i = EmailActivity.createOpenMessageIntent(this, mAccountId, mMailboxId, mMessageId); 351 } else if (mMailboxId != Mailbox.NO_MAILBOX) { 352 i = EmailActivity.createOpenMailboxIntent(this, mAccountId, mMailboxId); 353 } else { 354 i = EmailActivity.createOpenAccountIntent(this, mAccountId); 355 } 356 startActivity(i); 357 finish(); 358 } 359 360 private final MailboxFinder.Callback mMailboxFinderCallback = new MailboxFinder.Callback() { 361 // This MUST be called from callback methods. 362 private void cleanUp() { 363 mInboxFinder = null; 364 } 365 366 @Override 367 public void onAccountNotFound() { 368 cleanUp(); 369 // Account removed? Clear the IDs and restart the task. Which will result in either 370 // a) show account setup if there's really no accounts or b) open the default account. 371 372 mAccountId = Account.NO_ACCOUNT; 373 mMailboxId = Mailbox.NO_MAILBOX; 374 mMessageId = Message.NO_MESSAGE; 375 mAccountUuid = null; 376 377 // Restart the task. 378 startAccountResolver(); 379 } 380 381 @Override 382 public void onMailboxNotFound(long accountId) { 383 // Just do the same thing as "account not found". 384 onAccountNotFound(); 385 } 386 387 @Override 388 public void onAccountSecurityHold(long accountId) { 389 cleanUp(); 390 391 startActivity( 392 AccountSecurity.actionUpdateSecurityIntent(Welcome.this, accountId, true)); 393 finish(); 394 } 395 396 @Override 397 public void onMailboxFound(long accountId, long mailboxId) { 398 cleanUp(); 399 400 // Okay the account has Inbox now. Start the main activity. 401 startEmailActivity(); 402 } 403 }; 404} 405