2010-07-21 21:29:49 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.android.email.activity;
|
|
|
|
|
|
|
|
import android.app.Activity;
|
2011-04-19 21:06:03 +00:00
|
|
|
import android.app.Fragment;
|
2010-08-04 22:38:25 +00:00
|
|
|
import android.content.Intent;
|
2012-04-25 17:26:46 +00:00
|
|
|
import android.content.res.Configuration;
|
2010-07-21 21:29:49 +00:00
|
|
|
import android.os.Bundle;
|
2010-12-07 23:17:52 +00:00
|
|
|
import android.os.Handler;
|
2011-01-25 19:07:42 +00:00
|
|
|
import android.text.TextUtils;
|
2010-07-21 21:29:49 +00:00
|
|
|
import android.util.Log;
|
2010-08-04 23:54:08 +00:00
|
|
|
import android.view.Menu;
|
|
|
|
import android.view.MenuItem;
|
2010-11-29 19:42:13 +00:00
|
|
|
import android.view.View;
|
|
|
|
import android.widget.TextView;
|
2010-07-21 21:29:49 +00:00
|
|
|
|
2011-06-13 21:57:17 +00:00
|
|
|
import com.android.email.Controller;
|
|
|
|
import com.android.email.ControllerResultUiThreadWrapper;
|
|
|
|
import com.android.email.Email;
|
2011-06-20 20:50:24 +00:00
|
|
|
import com.android.email.MessageListContext;
|
2011-06-13 21:57:17 +00:00
|
|
|
import com.android.email.MessagingExceptionStrings;
|
|
|
|
import com.android.email.R;
|
|
|
|
import com.android.emailcommon.Logging;
|
|
|
|
import com.android.emailcommon.mail.MessagingException;
|
|
|
|
import com.android.emailcommon.provider.Account;
|
2011-06-17 18:55:35 +00:00
|
|
|
import com.android.emailcommon.provider.EmailContent.Message;
|
2011-06-13 21:57:17 +00:00
|
|
|
import com.android.emailcommon.provider.Mailbox;
|
|
|
|
import com.android.emailcommon.utility.EmailAsyncTask;
|
2011-08-08 21:39:19 +00:00
|
|
|
import com.android.emailcommon.utility.IntentUtilities;
|
2011-06-20 20:50:24 +00:00
|
|
|
import com.google.common.base.Preconditions;
|
2011-06-13 21:57:17 +00:00
|
|
|
|
2011-05-03 21:42:26 +00:00
|
|
|
import java.util.ArrayList;
|
2010-08-25 02:09:34 +00:00
|
|
|
|
2010-07-21 21:29:49 +00:00
|
|
|
/**
|
2011-04-26 17:28:18 +00:00
|
|
|
* The main Email activity, which is used on both the tablet and the phone.
|
2011-04-19 21:06:03 +00:00
|
|
|
*
|
2011-04-26 17:28:18 +00:00
|
|
|
* Because this activity is device agnostic, so most of the UI aren't owned by this, but by
|
|
|
|
* the UIController.
|
2010-07-21 21:29:49 +00:00
|
|
|
*/
|
2011-06-02 23:44:13 +00:00
|
|
|
public class EmailActivity extends Activity implements View.OnClickListener, FragmentInstallable {
|
2011-06-20 20:43:02 +00:00
|
|
|
public static final String EXTRA_ACCOUNT_ID = "ACCOUNT_ID";
|
|
|
|
public static final String EXTRA_MAILBOX_ID = "MAILBOX_ID";
|
|
|
|
public static final String EXTRA_MESSAGE_ID = "MESSAGE_ID";
|
|
|
|
public static final String EXTRA_QUERY_STRING = "QUERY_STRING";
|
2011-04-28 00:55:13 +00:00
|
|
|
|
|
|
|
/** Loader IDs starting with this is safe to use from UIControllers. */
|
|
|
|
static final int UI_CONTROLLER_LOADER_ID_BASE = 100;
|
2010-07-21 21:29:49 +00:00
|
|
|
|
2011-05-05 21:22:33 +00:00
|
|
|
/** Loader IDs starting with this is safe to use from ActionBarController. */
|
|
|
|
static final int ACTION_BAR_CONTROLLER_LOADER_ID_BASE = 200;
|
|
|
|
|
2011-10-13 21:16:20 +00:00
|
|
|
private static float sLastFontScale = -1;
|
|
|
|
|
2010-12-07 23:17:52 +00:00
|
|
|
private Controller mController;
|
|
|
|
private Controller.Result mControllerResult;
|
2010-07-21 21:29:49 +00:00
|
|
|
|
2011-05-09 21:31:11 +00:00
|
|
|
private UIControllerBase mUIController;
|
2010-07-21 21:29:49 +00:00
|
|
|
|
2011-03-21 21:08:57 +00:00
|
|
|
private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
|
2010-08-27 17:25:03 +00:00
|
|
|
|
2011-04-01 18:34:03 +00:00
|
|
|
/** Banner to display errors */
|
|
|
|
private BannerController mErrorBanner;
|
|
|
|
/** Id of the account that had a messaging exception most recently. */
|
2010-12-07 23:17:52 +00:00
|
|
|
private long mLastErrorAccountId;
|
2010-11-29 19:42:13 +00:00
|
|
|
|
2010-08-09 23:17:53 +00:00
|
|
|
/**
|
2011-05-09 21:31:11 +00:00
|
|
|
* Create an intent to launch and open account's inbox.
|
2010-08-25 02:09:34 +00:00
|
|
|
*
|
|
|
|
* @param accountId If -1, default account will be used.
|
2010-08-09 23:17:53 +00:00
|
|
|
*/
|
2011-05-09 21:31:11 +00:00
|
|
|
public static Intent createOpenAccountIntent(Activity fromActivity, long accountId) {
|
2011-04-28 00:55:13 +00:00
|
|
|
Intent i = IntentUtilities.createRestartAppIntent(fromActivity, EmailActivity.class);
|
2010-08-09 23:17:53 +00:00
|
|
|
if (accountId != -1) {
|
|
|
|
i.putExtra(EXTRA_ACCOUNT_ID, accountId);
|
|
|
|
}
|
2011-05-09 21:31:11 +00:00
|
|
|
return i;
|
2010-08-25 02:09:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-05-09 21:31:11 +00:00
|
|
|
* Create an intent to launch and open a mailbox.
|
2010-08-25 02:09:34 +00:00
|
|
|
*
|
|
|
|
* @param accountId must not be -1.
|
2010-11-03 00:38:29 +00:00
|
|
|
* @param mailboxId must not be -1. Magic mailboxes IDs (such as
|
|
|
|
* {@link Mailbox#QUERY_ALL_INBOXES}) don't work.
|
2010-08-25 02:09:34 +00:00
|
|
|
*/
|
2011-05-09 21:31:11 +00:00
|
|
|
public static Intent createOpenMailboxIntent(Activity fromActivity, long accountId,
|
|
|
|
long mailboxId) {
|
2010-08-25 02:09:34 +00:00
|
|
|
if (accountId == -1 || mailboxId == -1) {
|
2011-05-17 00:29:13 +00:00
|
|
|
throw new IllegalArgumentException();
|
2010-08-25 02:09:34 +00:00
|
|
|
}
|
2011-04-28 00:55:13 +00:00
|
|
|
Intent i = IntentUtilities.createRestartAppIntent(fromActivity, EmailActivity.class);
|
2010-08-25 02:09:34 +00:00
|
|
|
i.putExtra(EXTRA_ACCOUNT_ID, accountId);
|
2010-11-03 00:38:29 +00:00
|
|
|
i.putExtra(EXTRA_MAILBOX_ID, mailboxId);
|
2011-05-09 21:31:11 +00:00
|
|
|
return i;
|
2010-08-04 22:38:25 +00:00
|
|
|
}
|
|
|
|
|
2010-12-04 00:28:25 +00:00
|
|
|
/**
|
2011-05-09 21:31:11 +00:00
|
|
|
* Create an intent to launch and open a message.
|
2010-12-04 00:28:25 +00:00
|
|
|
*
|
|
|
|
* @param accountId must not be -1.
|
|
|
|
* @param mailboxId must not be -1. Magic mailboxes IDs (such as
|
|
|
|
* {@link Mailbox#QUERY_ALL_INBOXES}) don't work.
|
|
|
|
* @param messageId must not be -1.
|
|
|
|
*/
|
2011-05-09 21:31:11 +00:00
|
|
|
public static Intent createOpenMessageIntent(Activity fromActivity, long accountId,
|
|
|
|
long mailboxId, long messageId) {
|
2010-12-04 00:28:25 +00:00
|
|
|
if (accountId == -1 || mailboxId == -1 || messageId == -1) {
|
2011-05-17 00:29:13 +00:00
|
|
|
throw new IllegalArgumentException();
|
2010-12-04 00:28:25 +00:00
|
|
|
}
|
2011-04-28 00:55:13 +00:00
|
|
|
Intent i = IntentUtilities.createRestartAppIntent(fromActivity, EmailActivity.class);
|
2010-12-04 00:28:25 +00:00
|
|
|
i.putExtra(EXTRA_ACCOUNT_ID, accountId);
|
|
|
|
i.putExtra(EXTRA_MAILBOX_ID, mailboxId);
|
|
|
|
i.putExtra(EXTRA_MESSAGE_ID, messageId);
|
2011-05-09 21:31:11 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2011-06-14 19:31:00 +00:00
|
|
|
/**
|
|
|
|
* Create an intent to launch search activity.
|
|
|
|
*
|
|
|
|
* @param accountId ID of the account for the mailbox. Must not be {@link Account#NO_ACCOUNT}.
|
|
|
|
* @param mailboxId ID of the mailbox to search, or {@link Mailbox#NO_MAILBOX} to perform
|
|
|
|
* global search.
|
|
|
|
* @param query query string.
|
|
|
|
*/
|
|
|
|
public static Intent createSearchIntent(Activity fromActivity, long accountId,
|
|
|
|
long mailboxId, String query) {
|
2011-06-20 20:50:24 +00:00
|
|
|
Preconditions.checkArgument(Account.isNormalAccount(accountId),
|
|
|
|
"Can only search in normal accounts");
|
|
|
|
|
2011-06-23 01:18:17 +00:00
|
|
|
// Note that a search doesn't use a restart intent, as we want another instance of
|
|
|
|
// the activity to sit on the stack for search.
|
|
|
|
Intent i = new Intent(fromActivity, EmailActivity.class);
|
2011-06-14 19:31:00 +00:00
|
|
|
i.putExtra(EXTRA_ACCOUNT_ID, accountId);
|
|
|
|
i.putExtra(EXTRA_MAILBOX_ID, mailboxId);
|
|
|
|
i.putExtra(EXTRA_QUERY_STRING, query);
|
|
|
|
i.setAction(Intent.ACTION_SEARCH);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2011-05-09 21:31:11 +00:00
|
|
|
/**
|
|
|
|
* Initialize {@link #mUIController}.
|
|
|
|
*/
|
|
|
|
private void initUIController() {
|
2012-04-30 22:48:45 +00:00
|
|
|
if (UiUtilities.useTwoPane(this)) {
|
|
|
|
if (getIntent().getAction() != null
|
|
|
|
&& Intent.ACTION_SEARCH.equals(getIntent().getAction())
|
|
|
|
&& !UiUtilities.showTwoPaneSearchResults(this)) {
|
|
|
|
mUIController = new UIControllerSearchTwoPane(this);
|
|
|
|
} else {
|
|
|
|
mUIController = new UIControllerTwoPane(this);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mUIController = new UIControllerOnePane(this);
|
|
|
|
}
|
2010-12-04 00:28:25 +00:00
|
|
|
}
|
|
|
|
|
2010-07-21 21:29:49 +00:00
|
|
|
@Override
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) Log.d(Logging.LOG_TAG, this + " onCreate");
|
2011-05-09 21:31:11 +00:00
|
|
|
|
2011-10-13 21:16:20 +00:00
|
|
|
float fontScale = getResources().getConfiguration().fontScale;
|
|
|
|
if (sLastFontScale != -1 && sLastFontScale != fontScale) {
|
|
|
|
// If the font scale has been initialized, and has been detected to be different than
|
|
|
|
// the last time the Activity ran, it means the user changed the font while no
|
|
|
|
// Email Activity was running - we still need to purge static information though.
|
|
|
|
onFontScaleChangeDetected();
|
|
|
|
}
|
|
|
|
sLastFontScale = fontScale;
|
|
|
|
|
2011-05-09 21:31:11 +00:00
|
|
|
// UIController is used in onPrepareOptionsMenu(), which can be called from within
|
|
|
|
// super.onCreate(), so we need to initialize it here.
|
|
|
|
initUIController();
|
|
|
|
|
2010-07-21 21:29:49 +00:00
|
|
|
super.onCreate(savedInstanceState);
|
2010-11-01 23:15:15 +00:00
|
|
|
ActivityHelper.debugSetWindowFlags(this);
|
2011-05-09 21:31:11 +00:00
|
|
|
setContentView(mUIController.getLayoutId());
|
2010-07-21 21:29:49 +00:00
|
|
|
|
2011-04-26 17:28:18 +00:00
|
|
|
mUIController.onActivityViewReady();
|
2010-07-21 21:29:49 +00:00
|
|
|
|
2010-12-07 23:17:52 +00:00
|
|
|
mController = Controller.getInstance(this);
|
2011-04-26 17:28:18 +00:00
|
|
|
mControllerResult = new ControllerResultUiThreadWrapper<ControllerResult>(new Handler(),
|
|
|
|
new ControllerResult());
|
|
|
|
mController.addResultCallback(mControllerResult);
|
2010-11-12 00:28:21 +00:00
|
|
|
|
2010-11-29 19:42:13 +00:00
|
|
|
// Set up views
|
|
|
|
// TODO Probably better to extract mErrorMessageView related code into a separate class,
|
|
|
|
// so that it'll be easy to reuse for the phone activities.
|
2011-04-01 18:34:03 +00:00
|
|
|
TextView errorMessage = (TextView) findViewById(R.id.error_message);
|
|
|
|
errorMessage.setOnClickListener(this);
|
|
|
|
int errorBannerHeight = getResources().getDimensionPixelSize(R.dimen.error_message_height);
|
|
|
|
mErrorBanner = new BannerController(this, errorMessage, errorBannerHeight);
|
|
|
|
|
2011-04-26 17:28:18 +00:00
|
|
|
if (savedInstanceState != null) {
|
2011-06-14 19:31:00 +00:00
|
|
|
mUIController.onRestoreInstanceState(savedInstanceState);
|
2011-04-26 17:28:18 +00:00
|
|
|
} else {
|
2011-11-10 01:18:18 +00:00
|
|
|
final Intent intent = getIntent();
|
|
|
|
final MessageListContext viewContext = MessageListContext.forIntent(this, intent);
|
|
|
|
if (viewContext == null) {
|
|
|
|
// This might happen if accounts were deleted on another thread, and there aren't
|
|
|
|
// any remaining
|
|
|
|
Welcome.actionStart(this);
|
|
|
|
finish();
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
final long messageId = intent.getLongExtra(EXTRA_MESSAGE_ID, Message.NO_MESSAGE);
|
|
|
|
mUIController.open(viewContext, messageId);
|
|
|
|
}
|
2011-04-26 17:28:18 +00:00
|
|
|
}
|
2011-04-28 00:55:13 +00:00
|
|
|
mUIController.onActivityCreated();
|
2010-07-21 21:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onSaveInstanceState(Bundle outState) {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
|
|
Log.d(Logging.LOG_TAG, this + " onSaveInstanceState");
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
}
|
2010-07-21 21:29:49 +00:00
|
|
|
super.onSaveInstanceState(outState);
|
2011-04-26 17:28:18 +00:00
|
|
|
mUIController.onSaveInstanceState(outState);
|
2010-07-21 21:29:49 +00:00
|
|
|
}
|
|
|
|
|
2011-06-06 18:41:50 +00:00
|
|
|
// FragmentInstallable
|
2011-04-19 21:06:03 +00:00
|
|
|
@Override
|
2011-06-02 23:44:13 +00:00
|
|
|
public void onInstallFragment(Fragment fragment) {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
|
2011-06-02 23:44:13 +00:00
|
|
|
Log.d(Logging.LOG_TAG, this + " onInstallFragment fragment=" + fragment);
|
2011-04-19 21:06:03 +00:00
|
|
|
}
|
2011-06-02 23:44:13 +00:00
|
|
|
mUIController.onInstallFragment(fragment);
|
2011-04-19 21:06:03 +00:00
|
|
|
}
|
|
|
|
|
2011-06-06 18:41:50 +00:00
|
|
|
// FragmentInstallable
|
|
|
|
@Override
|
|
|
|
public void onUninstallFragment(Fragment fragment) {
|
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
|
|
Log.d(Logging.LOG_TAG, this + " onUninstallFragment fragment=" + fragment);
|
|
|
|
}
|
|
|
|
mUIController.onUninstallFragment(fragment);
|
|
|
|
}
|
|
|
|
|
2010-07-21 21:29:49 +00:00
|
|
|
@Override
|
|
|
|
protected void onStart() {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) Log.d(Logging.LOG_TAG, this + " onStart");
|
2010-07-21 21:29:49 +00:00
|
|
|
super.onStart();
|
2011-05-09 21:31:11 +00:00
|
|
|
mUIController.onActivityStart();
|
2010-07-21 21:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onResume() {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) Log.d(Logging.LOG_TAG, this + " onResume");
|
2010-07-21 21:29:49 +00:00
|
|
|
super.onResume();
|
2011-05-09 21:31:11 +00:00
|
|
|
mUIController.onActivityResume();
|
2011-04-01 18:34:03 +00:00
|
|
|
/**
|
|
|
|
* In {@link MessageList#onResume()}, we go back to {@link Welcome} if an account
|
|
|
|
* has been added/removed. We don't need to do that here, because we fetch the most
|
|
|
|
* up-to-date account list. Additionally, we detect and do the right thing if all
|
|
|
|
* of the accounts have been removed.
|
|
|
|
*/
|
2010-07-21 21:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPause() {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) Log.d(Logging.LOG_TAG, this + " onPause");
|
2010-07-21 21:29:49 +00:00
|
|
|
super.onPause();
|
2011-05-09 21:31:11 +00:00
|
|
|
mUIController.onActivityPause();
|
2010-07-21 21:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onStop() {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) Log.d(Logging.LOG_TAG, this + " onStop");
|
2010-07-21 21:29:49 +00:00
|
|
|
super.onStop();
|
2011-05-09 21:31:11 +00:00
|
|
|
mUIController.onActivityStop();
|
2010-07-21 21:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onDestroy() {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) Log.d(Logging.LOG_TAG, this + " onDestroy");
|
2010-12-07 23:17:52 +00:00
|
|
|
mController.removeResultCallback(mControllerResult);
|
2011-03-21 21:08:57 +00:00
|
|
|
mTaskTracker.cancellAllInterrupt();
|
2011-05-09 21:31:11 +00:00
|
|
|
mUIController.onActivityDestroy();
|
2010-07-21 21:29:49 +00:00
|
|
|
super.onDestroy();
|
|
|
|
}
|
|
|
|
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
@Override
|
|
|
|
public void onBackPressed() {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
|
|
Log.d(Logging.LOG_TAG, this + " onBackPressed");
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
}
|
2011-04-28 00:55:13 +00:00
|
|
|
if (!mUIController.onBackPressed(true)) {
|
|
|
|
// Not handled by UIController -- perform the default. i.e. close the app.
|
2010-11-16 19:11:22 +00:00
|
|
|
super.onBackPressed();
|
|
|
|
}
|
2010-07-21 21:29:49 +00:00
|
|
|
}
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
|
2010-11-29 19:42:13 +00:00
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
switch (v.getId()) {
|
|
|
|
case R.id.error_message:
|
|
|
|
dismissErrorMessage();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-07 23:17:52 +00:00
|
|
|
/**
|
|
|
|
* Force dismiss the error banner.
|
|
|
|
*/
|
2010-11-29 19:42:13 +00:00
|
|
|
private void dismissErrorMessage() {
|
2011-04-01 18:34:03 +00:00
|
|
|
mErrorBanner.dismiss();
|
2010-11-29 19:42:13 +00:00
|
|
|
}
|
|
|
|
|
2010-08-04 23:54:08 +00:00
|
|
|
@Override
|
|
|
|
public boolean onCreateOptionsMenu(Menu menu) {
|
2011-04-28 00:55:13 +00:00
|
|
|
return mUIController.onCreateOptionsMenu(getMenuInflater(), menu);
|
2010-08-04 23:54:08 +00:00
|
|
|
}
|
|
|
|
|
2011-01-04 22:02:09 +00:00
|
|
|
@Override
|
|
|
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
2011-04-28 00:55:13 +00:00
|
|
|
return mUIController.onPrepareOptionsMenu(getMenuInflater(), menu);
|
2011-01-04 22:02:09 +00:00
|
|
|
}
|
|
|
|
|
2011-06-14 19:31:00 +00:00
|
|
|
/**
|
|
|
|
* Called when the search key is pressd.
|
|
|
|
*
|
|
|
|
* Use the below command to emulate the key press on devices without the search key.
|
|
|
|
* adb shell input keyevent 84
|
|
|
|
*/
|
2011-03-31 20:29:23 +00:00
|
|
|
@Override
|
|
|
|
public boolean onSearchRequested() {
|
2011-06-14 19:31:00 +00:00
|
|
|
if (Email.DEBUG) {
|
|
|
|
Log.d(Logging.LOG_TAG, this + " onSearchRequested");
|
|
|
|
}
|
|
|
|
mUIController.onSearchRequested();
|
|
|
|
return true; // Event handled.
|
2011-03-31 20:29:23 +00:00
|
|
|
}
|
|
|
|
|
2010-08-04 23:54:08 +00:00
|
|
|
@Override
|
2011-05-04 19:07:08 +00:00
|
|
|
@SuppressWarnings("deprecation")
|
2010-08-04 23:54:08 +00:00
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
2011-04-28 00:55:13 +00:00
|
|
|
if (mUIController.onOptionsItemSelected(item)) {
|
|
|
|
return true;
|
|
|
|
}
|
2010-08-04 23:54:08 +00:00
|
|
|
return super.onOptionsItemSelected(item);
|
|
|
|
}
|
|
|
|
|
2010-12-07 23:17:52 +00:00
|
|
|
/**
|
|
|
|
* A {@link Controller.Result} to detect connection status.
|
|
|
|
*/
|
|
|
|
private class ControllerResult extends Controller.Result {
|
2012-04-25 17:26:46 +00:00
|
|
|
@Override
|
|
|
|
public void sendMailCallback(
|
|
|
|
MessagingException result, long accountId, long messageId, int progress) {
|
|
|
|
handleError(result, accountId, progress);
|
|
|
|
}
|
|
|
|
|
2010-12-07 23:17:52 +00:00
|
|
|
@Override
|
|
|
|
public void serviceCheckMailCallback(
|
|
|
|
MessagingException result, long accountId, long mailboxId, int progress, long tag) {
|
|
|
|
handleError(result, accountId, progress);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void updateMailboxCallback(MessagingException result, long accountId, long mailboxId,
|
2011-05-03 21:42:26 +00:00
|
|
|
int progress, int numNewMessages, ArrayList<Long> addedMessages) {
|
2010-12-07 23:17:52 +00:00
|
|
|
handleError(result, accountId, progress);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void updateMailboxListCallback(
|
|
|
|
MessagingException result, long accountId, int progress) {
|
|
|
|
handleError(result, accountId, progress);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2010-12-10 21:36:18 +00:00
|
|
|
public void loadAttachmentCallback(MessagingException result, long accountId,
|
|
|
|
long messageId, long attachmentId, int progress) {
|
|
|
|
handleError(result, accountId, progress);
|
2010-12-07 23:17:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2010-12-10 21:36:18 +00:00
|
|
|
public void loadMessageForViewCallback(MessagingException result, long accountId,
|
|
|
|
long messageId, int progress) {
|
|
|
|
handleError(result, accountId, progress);
|
2010-12-07 23:17:52 +00:00
|
|
|
}
|
|
|
|
|
2011-04-01 18:34:03 +00:00
|
|
|
private void handleError(final MessagingException result, final long accountId,
|
|
|
|
int progress) {
|
2010-12-07 23:17:52 +00:00
|
|
|
if (accountId == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (result == null) {
|
|
|
|
if (progress > 0) {
|
2011-04-01 18:34:03 +00:00
|
|
|
// Connection now working; clear the error message banner
|
|
|
|
if (mLastErrorAccountId == accountId) {
|
|
|
|
dismissErrorMessage();
|
|
|
|
}
|
2010-12-07 23:17:52 +00:00
|
|
|
}
|
|
|
|
} else {
|
2011-07-17 19:23:49 +00:00
|
|
|
Account account = Account.restoreAccountWithId(EmailActivity.this, accountId);
|
|
|
|
if (account == null) return;
|
|
|
|
String message =
|
|
|
|
MessagingExceptionStrings.getErrorString(EmailActivity.this, result);
|
|
|
|
if (!TextUtils.isEmpty(account.mDisplayName)) {
|
|
|
|
// TODO Use properly designed layout. Don't just concatenate strings;
|
|
|
|
// which is generally poor for I18N.
|
|
|
|
message = message + " (" + account.mDisplayName + ")";
|
|
|
|
}
|
|
|
|
if (mErrorBanner.show(message)) {
|
|
|
|
mLastErrorAccountId = accountId;
|
|
|
|
}
|
|
|
|
}
|
2010-12-07 23:17:52 +00:00
|
|
|
}
|
|
|
|
}
|
2011-10-13 21:16:20 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a change to the system font size. This invalidates some static caches we have.
|
|
|
|
*/
|
|
|
|
private void onFontScaleChangeDetected() {
|
|
|
|
MessageListItem.resetDrawingCaches();
|
|
|
|
}
|
2010-07-21 21:29:49 +00:00
|
|
|
}
|