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