Remove phone activities and implement one-pane

The primary purpose of this CL is to remove phone activities, so the
one pane implementation is very much temporary and primitive, but it
should offer minimal operations.

Change-Id: If57f81db7c605c95664d49044a5cc082beda59c0
This commit is contained in:
Makoto Onuki 2011-05-11 11:38:54 -07:00
parent dfdc8b6da3
commit 94331c96d9
67 changed files with 558 additions and 3575 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 797 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 728 B

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
android:id="@+id/account_folder_list_fragment"
class="com.android.email.activity.AccountFolderListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>

View File

@ -1,102 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<com.android.email.activity.AccountFolderListItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingRight="4dip">
<View
android:id="@+id/chip"
android:background="@drawable/appointment_indicator_leftside_1"
android:layout_width="4dip"
android:layout_height="match_parent"
android:layout_centerVertical="true" />
<ImageView
android:id="@+id/folder_icon"
android:layout_width="30dip"
android:layout_height="30dip"
android:layout_alignParentRight="true"
android:layout_marginLeft="4dip"
android:layout_marginRight="4dip"
android:minWidth="30dip"
android:src="@drawable/ic_list_folder" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_weight="1">
<LinearLayout
android:id="@+id/subject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dip"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/name"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/status"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorTertiary"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:id="@+id/new_message_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dip"
android:background="@drawable/ind_unread"
style="@style/unreadCount"/>
<TextView
android:id="@+id/all_message_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dip"
android:background="@drawable/ind_sum"
style="@style/unreadCount"/>
</LinearLayout>
<ImageView
android:id="@+id/default_sender"
android:layout_width="12dip"
android:layout_height="12dip"
android:minWidth="12dip"
android:layout_margin="5dip"
android:src="@drawable/ind_default"/>
<View
android:id="@+id/folder_separator"
android:background="@color/account_folder_list_item_separator"
android:layout_width="1dip"
android:layout_height="40dip"
android:layout_marginLeft="5dip" />
<ImageView
android:id="@+id/folder_button"
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_alignParentRight="true"
android:minWidth="30dip"
android:paddingTop="5dip"
android:paddingLeft="5dip"
android:src="@drawable/btn_folder"/>
</com.android.email.activity.AccountFolderListItem>

View File

@ -35,10 +35,16 @@
android:ellipsize="end"
android:background="#ffff66"
/>
<FrameLayout
android:id="@+id/fragment_placeholder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
/>
<!-- TODO temporarily gone -->
<include
layout="@layout/message_command_button_view"
android:id="@+id/message_command_buttons"
android:visibility="gone"
/>
</LinearLayout>

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/connection_error_banner" />
<fragment
android:id="@+id/mailbox_list_fragment"
class="com.android.email.activity.MailboxListFragment"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>

View File

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/connection_error_banner" />
<fragment
android:id="@+id/message_list_fragment"
class="com.android.email.activity.MessageListFragment"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>

View File

@ -1,53 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<fragment
android:id="@+id/message_view_fragment"
class="com.android.email.activity.MessageViewFragment"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
/>
<LinearLayout
android:id="@+id/button_panel"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dip"
android:paddingLeft="4dip"
android:paddingRight="4dip"
android:paddingBottom="1dip"
>
<Button
android:id="@+id/moveToNewer"
android:text="@string/message_view_move_to_newer"
android:layout_height="match_parent"
android:layout_width="0dip"
android:layout_weight="1" />
<Button
android:id="@+id/moveToOlder"
android:text="@string/message_view_move_to_older"
android:layout_height="match_parent"
android:layout_width="0dip"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.
-->
<!-- xlarge -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/send"
android:title="@string/send_action"
android:showAsAction="always|withText"
android:icon="@drawable/ic_menu_send_holo_light"
android:alphabeticShortcut="s"
/>
<item
android:id="@+id/save"
android:title="@string/save_draft_action"
android:showAsAction="always"
android:alphabeticShortcut="d"
/>
<item
android:id="@+id/discard"
android:title="@string/discard_action"
android:showAsAction="always"
android:icon="@drawable/ic_menu_trash_holo_light"
android:alphabeticShortcut="q"
/>
</menu>

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/open_folder"
android:title="@string/open_action" />
<item android:id="@+id/compose"
android:title="@string/compose_action" />
<item android:id="@+id/refresh_account"
android:title="@string/refresh_action" />
<item android:id="@+id/edit_account"
android:title="@string/account_settings_action" />
<item android:id="@+id/delete_account"
android:title="@string/remove_account_action" />
</menu>

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/compose"
android:alphabeticShortcut="c"
android:title="@string/compose_action"
android:icon="@drawable/ic_menu_compose"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/check_mail"
android:alphabeticShortcut="r"
android:title="@string/refresh_action"
android:icon="@drawable/ic_menu_refresh"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/add_new_account"
android:title="@string/add_account_action"
android:icon="@android:drawable/ic_menu_add"
/>
<item android:id="@+id/account_settings"
android:title="@string/account_settings_action"
android:icon="@android:drawable/ic_menu_preferences"
android:showAsAction="ifRoom"
/>
<!--
<item android:id="@+id/search"
android:title="@string/search_action" />
<item android:id="@+id/preferences"
android:title="@string/preferences_action" />
-->
</menu>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/open_folder"
android:title="@string/open_action" />
<item android:id="@+id/check_mail"
android:title="@string/refresh_action" />
</menu>

View File

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/compose"
android:alphabeticShortcut="c"
android:title="@string/compose_action"
android:icon="@drawable/ic_menu_compose"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/refresh"
android:alphabeticShortcut="r"
android:title="@string/refresh_action"
android:icon="@drawable/ic_menu_refresh"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/account_settings"
android:title="@string/account_settings_action"
android:icon="@android:drawable/ic_menu_preferences"
/>
</menu>

View File

@ -14,8 +14,6 @@
limitations under the License.
-->
<!-- small -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/send"

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<!-- This context menu is shown for message lists except in trash, outbox, or drafts -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/open" android:title="@string/open_action" />
<item android:id="@+id/delete" android:title="@string/delete_action" />
<item android:id="@+id/forward" android:title="@string/forward_action" />
<item android:id="@+id/reply_all" android:title="@string/reply_all_action" />
<item android:id="@+id/reply" android:title="@string/reply_action" />
<item android:id="@+id/mark_as_read" android:title="@string/mark_as_read_action" />
</menu>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<!-- This context menu is shown for drafts message lists -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/open" android:title="@string/open_action" />
<item android:id="@+id/delete" android:title="@string/discard_action" />
</menu>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<!-- This context menu is shown for outbox message lists -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/open" android:title="@string/open_action" />
<item android:id="@+id/delete" android:title="@string/discard_action" />
</menu>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<!-- This context menu is shown for trash message lists -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/open" android:title="@string/open_action" />
<item android:id="@+id/delete" android:title="@string/delete_action" />
</menu>

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/refresh"
android:alphabeticShortcut="r"
android:title="@string/refresh_action"
android:icon="@drawable/ic_menu_refresh_holo_light"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/compose"
android:alphabeticShortcut="c"
android:title="@string/compose_action"
android:icon="@drawable/ic_menu_compose_holo_light"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/folders"
android:title="@string/folders_action"
android:icon="@drawable/ic_menu_folder"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/accounts"
android:title="@string/accounts_action"
android:icon="@drawable/ic_menu_account_list"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/account_settings"
android:title="@string/account_settings_action"
android:icon="@android:drawable/ic_menu_preferences"
android:showAsAction="ifRoom"
/>
</menu>

View File

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/delete"
android:alphabeticShortcut="q"
android:title="@string/delete_action"
android:icon="@android:drawable/ic_menu_delete"
/>
<item
android:id="@+id/forward"
android:alphabeticShortcut="f"
android:title="@string/forward_action"
android:icon="@drawable/ic_menu_forward_mail"
/>
<item
android:id="@+id/reply"
android:alphabeticShortcut="r"
android:title="@string/reply_action"
android:icon="@drawable/ic_menu_reply"
/>
<item
android:id="@+id/reply_all"
android:alphabeticShortcut="a"
android:title="@string/reply_all_action"
android:icon="@drawable/ic_menu_reply_all"
/>
<item
android:id="@+id/mark_as_unread"
android:alphabeticShortcut="u"
android:title="@string/mark_as_unread_action"
android:icon="@drawable/ic_menu_mark_unread_holo_light"
/>
</menu>

View File

@ -1,313 +0,0 @@
/*
* Copyright (C) 2009 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 com.android.email.Controller;
import com.android.email.ControllerResultUiThreadWrapper;
import com.android.email.Email;
import com.android.email.NotificationController;
import com.android.email.R;
import com.android.email.activity.setup.AccountSettingsXL;
import com.android.email.activity.setup.AccountSetupBasics;
import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Mailbox;
import java.util.ArrayList;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.Toast;
public class AccountFolderList extends Activity implements AccountFolderListFragment.Callback {
private static final int DIALOG_REMOVE_ACCOUNT = 1;
private static final String ICICLE_SELECTED_ACCOUNT = "com.android.email.selectedAccount";
private EmailContent.Account mSelectedContextAccount;
// UI Support
private AccountFolderListFragment mListFragment;
private Controller.Result mControllerCallback;
/**
* Start the Accounts list activity. Uses the CLEAR_TOP flag which means that other stacked
* activities may be killed in order to get back to Accounts.
*/
public static void actionShowAccounts(Context context) {
Intent i = new Intent(context, AccountFolderList.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(i);
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
ActivityHelper.debugSetWindowFlags(this);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.account_folder_list);
mControllerCallback = new ControllerResultUiThreadWrapper<ControllerResults>(
new Handler(), new ControllerResults());
mListFragment =
(AccountFolderListFragment) getFragmentManager()
.findFragmentById(R.id.account_folder_list_fragment);
mListFragment.bindActivityInfo(this);
if (icicle != null && icicle.containsKey(ICICLE_SELECTED_ACCOUNT)) {
mSelectedContextAccount = (Account) icicle.getParcelable(ICICLE_SELECTED_ACCOUNT);
}
// Halt the progress indicator (we'll display it later when needed)
setProgressBarIndeterminate(true);
setProgressBarIndeterminateVisibility(false);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mSelectedContextAccount != null) {
outState.putParcelable(ICICLE_SELECTED_ACCOUNT, mSelectedContextAccount);
}
}
@Override
public void onPause() {
super.onPause();
Controller.getInstance(getApplication()).removeResultCallback(mControllerCallback);
}
@Override
public void onResume() {
super.onResume();
NotificationManager notifMgr = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notifMgr.cancel(1);
Controller.getInstance(getApplication()).addResultCallback(mControllerCallback);
// Exit immediately if the accounts list has changed (e.g. externally deleted)
if (Email.getNotifyUiAccountsChanged()) {
Welcome.actionStart(this);
finish();
return;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
private void onAddNewAccount() {
AccountSetupBasics.actionNewAccount(this);
}
/* Implements AccountFolderListFragment.Callback */
public void onEditAccount(long accountId) {
AccountSettingsXL.actionSettings(this, -1);
}
/* Implements AccountFolderListFragment.Callback */
public void onRefresh(long accountId) {
if (accountId == -1) {
// TODO implement a suitable "Refresh all accounts" / "check mail" comment in Controller
// TODO this is temp
Toast.makeText(this, getString(R.string.account_folder_list_refresh_toast),
Toast.LENGTH_LONG).show();
} else {
showProgressIcon(true);
Controller.getInstance(getApplication()).updateMailboxList(accountId);
// TODO update the inbox too
}
}
/* Implements AccountFolderListFragment.Callback */
public void onCompose(long accountId) {
if (accountId == -1) {
accountId = Account.getDefaultAccountId(this);
}
if (accountId != -1) {
MessageCompose.actionCompose(this, accountId);
} else {
onAddNewAccount();
}
}
/* Implements AccountFolderListFragment.Callback */
public void onDeleteAccount(long accountId) {
mSelectedContextAccount = Account.restoreAccountWithId(this, accountId);
showDialog(DIALOG_REMOVE_ACCOUNT);
}
/* Implements AccountFolderListFragment.Callback */
public void onOpenAccount(long accountId) {
MessageList.actionHandleAccount(this, accountId, Mailbox.TYPE_INBOX);
}
/* Implements AccountFolderListFragment.Callback */
public void onOpenMailbox(long mailboxId) {
MessageList.actionHandleMailbox(this, mailboxId);
}
/* Implements AccountFolderListFragment.Callback */
public void onOpenMailboxes(long accountId) {
MailboxList.actionHandleAccount(this, accountId);
}
@Override
public Dialog onCreateDialog(int id, Bundle args) {
switch (id) {
case DIALOG_REMOVE_ACCOUNT:
return createRemoveAccountDialog();
}
return super.onCreateDialog(id, args);
}
private Dialog createRemoveAccountDialog() {
final Activity activity = this;
return new AlertDialog.Builder(this)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setTitle(R.string.account_delete_dlg_title)
.setMessage(getString(R.string.account_delete_dlg_instructions_fmt,
mSelectedContextAccount.getDisplayName()))
.setPositiveButton(R.string.okay_action, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
final long accountId = mSelectedContextAccount.mId;
dismissDialog(DIALOG_REMOVE_ACCOUNT);
// TODO Do this automatically in the NotificationController as part of a
// ContentObserver
// Dismiss new message notification.
NotificationController.getInstance(activity)
.cancelNewMessageNotification(accountId);
int numAccounts = EmailContent.count(activity,
Account.CONTENT_URI, null, null);
mListFragment.hideDeletingAccount(mSelectedContextAccount.mId);
Controller.getInstance(activity).deleteAccount(accountId);
if (numAccounts == 1) {
AccountSetupBasics.actionNewAccount(activity);
finish();
}
}
})
.setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dismissDialog(DIALOG_REMOVE_ACCOUNT);
}
})
.create();
}
/**
* Update a cached dialog with current values (e.g. account name)
*/
@Override
public void onPrepareDialog(int id, Dialog dialog, Bundle args) {
switch (id) {
case DIALOG_REMOVE_ACCOUNT:
AlertDialog alert = (AlertDialog) dialog;
alert.setMessage(getString(R.string.account_delete_dlg_instructions_fmt,
mSelectedContextAccount.getDisplayName()));
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_new_account:
onAddNewAccount();
break;
case R.id.check_mail:
onRefresh(-1);
break;
case R.id.compose:
onCompose(-1);
break;
case R.id.account_settings:
onEditAccount(-1);
break;
default:
return super.onOptionsItemSelected(item);
}
return true;
}
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.account_folder_list_option, menu);
return true;
}
private void showProgressIcon(boolean show) {
setProgressBarIndeterminateVisibility(show);
}
/**
* Controller results listener. We wrap it with {@link ControllerResultUiThreadWrapper},
* so all methods are called on the UI thread.
*/
private class ControllerResults extends Controller.Result {
@Override
public void updateMailboxListCallback(MessagingException result, long accountKey,
int progress) {
updateProgress(result, progress);
}
@Override
public void updateMailboxCallback(MessagingException result, long accountKey,
long mailboxKey, int progress, int numNewMessages,
ArrayList<Long> addedMessages) {
updateProgress(result, progress);
}
@Override
public void serviceCheckMailCallback(MessagingException result, long accountId,
long mailboxId, int progress, long tag) {
updateProgress(result, progress);
}
@Override
public void sendMailCallback(MessagingException result, long accountId, long messageId,
int progress) {
}
private void updateProgress(MessagingException result, int progress) {
showProgressIcon(result == null && progress < 100);
}
}
}

View File

@ -1,474 +0,0 @@
/*
* 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 com.android.email.Controller;
import com.android.email.ControllerResultUiThreadWrapper;
import com.android.email.R;
import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Mailbox;
import com.android.emailcommon.provider.EmailContent.MailboxColumns;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.EmailContent.MessageColumns;
import com.android.emailcommon.utility.Utility;
import java.util.ArrayList;
import android.app.Activity;
import android.app.ListFragment;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.CursorAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
public class AccountFolderListFragment extends ListFragment
implements OnItemClickListener, AccountsAdapter.Callback {
// UI Support
private Activity mActivity;
private ListView mListView;
private Callback mCallback;
// Tasks and Data
private AccountsAdapter mListAdapter;
private LoadAccountsTask mLoadAccountsTask;
private Controller.Result mControllerCallback;
private static final String FAVORITE_COUNT_SELECTION =
MessageColumns.FLAG_FAVORITE + "= 1";
private static final String MAILBOX_TYPE_SELECTION =
MailboxColumns.TYPE + " =?";
private static final String MAILBOX_ID_SELECTION =
MessageColumns.MAILBOX_KEY + " =?";
private static final String[] MAILBOX_SUM_OF_UNREAD_COUNT_PROJECTION = new String [] {
"sum(" + MailboxColumns.UNREAD_COUNT + ")"
};
/**
* Callback interface that owning activities must implement
*/
public interface Callback {
/** Called when the user clicks on a specific account */
public void onOpenAccount(long accountId);
/** Called when the user clicks on a specific (currently, only magic mailbox) */
public void onOpenMailbox(long mailboxId);
/** Called when the user clicks to open the mailbox list for a specific account */
public void onOpenMailboxes(long accountId);
/** Begin composing a message in a specific account, or -1 for the default account */
public void onCompose(long accountId);
/** Begin refreshing a specific account, or -1 for all accounts */
public void onRefresh(long accountId);
/** Begin edit settings for a specific account */
public void onEditAccount(long accountId);
/** Delete a specific account */
public void onDeleteAccount(long accountId);
}
/**
* Called to do initial creation of a fragment. This is called after
* {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
/**
* Called by activity during onCreate() to bind additional information
* @param callback if non-null, UI clicks (e.g. refresh or open) will be delivered here
*/
public void bindActivityInfo(Callback callback) {
mCallback = callback;
}
/**
* Called when the fragment is instantiated, but not yet displayed.
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mActivity = getActivity();
mListView = getListView();
mListView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
mListView.setOnItemClickListener(this);
mListView.setLongClickable(true);
registerForContextMenu(mListView);
mControllerCallback = new ControllerResultUiThreadWrapper<ControllerResults>(
new Handler(), new ControllerResults());
}
/**
* Called when the Fragment is visible to the user.
*/
@Override
public void onStart() {
super.onStart();
}
/**
* Called when the fragment is visible to the user and actively running.
*/
@Override
public void onResume() {
super.onResume();
Controller.getInstance(mActivity).addResultCallback(mControllerCallback);
updateAccounts();
}
/**
* Called when the fragment is no longer displayed
*/
@Override
public void onPause() {
super.onPause();
Controller.getInstance(mActivity).removeResultCallback(mControllerCallback);
}
/**
* Called when the Fragment is no longer started.
*/
@Override
public void onStop() {
super.onStop();
Utility.cancelTaskInterrupt(mLoadAccountsTask);
mLoadAccountsTask = null;
}
/**
* Called when the fragment is no longer in use.
*/
@Override
public void onDestroy() {
super.onDestroy();
if (mListAdapter != null) {
mListAdapter.changeCursor(null);
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo info) {
super.onCreateContextMenu(menu, v, info);
AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) info;
if (mListAdapter.isMailbox(menuInfo.position)) {
Cursor c = (Cursor) mListView.getItemAtPosition(menuInfo.position);
String displayName = c.getString(Account.CONTENT_DISPLAY_NAME_COLUMN);
menu.setHeaderTitle(displayName);
mActivity.getMenuInflater()
.inflate(R.menu.account_folder_list_smart_folder_context, menu);
} else if (mListAdapter.isAccount(menuInfo.position)) {
Cursor c = (Cursor) mListView.getItemAtPosition(menuInfo.position);
String accountName = c.getString(Account.CONTENT_DISPLAY_NAME_COLUMN);
menu.setHeaderTitle(accountName);
mActivity.getMenuInflater().inflate(R.menu.account_folder_list_context, menu);
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo menuInfo =
(AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
// Drop the event if there's nowhere to send it (it's probably late-arriving)
if (mCallback == null) {
return false;
}
if (mListAdapter.isMailbox(menuInfo.position)) {
Cursor c = (Cursor) mListView.getItemAtPosition(menuInfo.position);
long id = c.getLong(AccountsAdapter.MAILBOX_COLUMN_ID);
switch (item.getItemId()) {
case R.id.open_folder:
mCallback.onOpenMailbox(id);
break;
case R.id.check_mail:
mCallback.onRefresh(-1);
break;
}
return false;
} else if (mListAdapter.isAccount(menuInfo.position)) {
Cursor c = (Cursor) mListView.getItemAtPosition(menuInfo.position);
long accountId = c.getLong(Account.CONTENT_ID_COLUMN);
switch (item.getItemId()) {
case R.id.open_folder:
mCallback.onOpenAccount(accountId);
break;
case R.id.compose:
mCallback.onCompose(accountId);
break;
case R.id.refresh_account:
mCallback.onRefresh(accountId);
break;
case R.id.edit_account:
mCallback.onEditAccount(accountId);
break;
case R.id.delete_account:
mCallback.onDeleteAccount(accountId);
break;
}
return true;
}
return false;
}
/* Implements OnItemClickListener.onItemClick */
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Drop the event if there's nowhere to send it (it's probably late-arriving)
if (mCallback == null) {
return;
}
if (mListAdapter.isMailbox(position)) {
mCallback.onOpenMailbox(id);
} else if (mListAdapter.isAccount(position)) {
mCallback.onOpenAccount(id);
}
}
/* Implements AccountsAdapter.Controller */
public void onClickAccountFolders(long accountId) {
if (mCallback != null) {
mCallback.onOpenMailboxes(accountId);
}
}
/**
* Trigger accounts list reload
*/
private void updateAccounts() {
Utility.cancelTaskInterrupt(mLoadAccountsTask);
mLoadAccountsTask = (LoadAccountsTask) new LoadAccountsTask().execute();
}
/**
* Called by container to mark an account as "being deleted" (we quickly hide it)
*/
public void hideDeletingAccount(long accountId) {
mListAdapter.addOnDeletingAccount(accountId);
}
private static int getUnreadCountByMailboxType(Context context, int type) {
int count = 0;
Cursor c = context.getContentResolver().query(Mailbox.CONTENT_URI,
MAILBOX_SUM_OF_UNREAD_COUNT_PROJECTION,
MAILBOX_TYPE_SELECTION,
new String[] { String.valueOf(type) }, null);
try {
if (c.moveToFirst()) {
return c.getInt(0);
}
} finally {
c.close();
}
return count;
}
private static int getCountByMailboxType(Context context, int type) {
int count = 0;
Cursor c = context.getContentResolver().query(Mailbox.CONTENT_URI,
EmailContent.ID_PROJECTION, MAILBOX_TYPE_SELECTION,
new String[] { String.valueOf(type) }, null);
try {
c.moveToPosition(-1);
while (c.moveToNext()) {
count += EmailContent.count(context, Message.CONTENT_URI,
MAILBOX_ID_SELECTION,
new String[] {
String.valueOf(c.getLong(EmailContent.ID_PROJECTION_COLUMN)) });
}
} finally {
c.close();
}
return count;
}
/**
* Build the group and child cursors that support the summary views (aka "at a glance").
*
* This is a placeholder implementation with significant problems that need to be addressed:
*
* TODO: We should only show summary mailboxes if they are non-empty. So there needs to be
* a more dynamic child-cursor here, probably listening for update notifications on a number
* of other internally-held queries such as count-of-inbox, count-of-unread, etc.
*
* TODO: This simple list is incomplete. For example, we probably want drafts, outbox, and
* (maybe) sent (again, these would be displayed only when non-empty).
*
* TODO: We need a way to count total unread in all inboxes (probably with some provider help)
*
* TODO: We need a way to count total # messages in all other summary boxes (probably with
* some provider help).
*
* TODO use narrower account projection (see LoadAccountsTask)
*/
private MatrixCursor getSummaryChildCursor() {
MatrixCursor childCursor = new MatrixCursor(AccountsAdapter.MAILBOX_PROJECTION);
int count;
RowBuilder row;
// TYPE_INBOX
count = getUnreadCountByMailboxType(mActivity, Mailbox.TYPE_INBOX);
row = childCursor.newRow();
row.add(Long.valueOf(Mailbox.QUERY_ALL_INBOXES)); // MAILBOX_COLUMN_ID = 0;
// MAILBOX_DISPLAY_NAME
row.add(mActivity.getString(R.string.account_folder_list_summary_inbox));
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
row.add(Integer.valueOf(Mailbox.TYPE_INBOX)); // MAILBOX_TYPE = 3;
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
// TYPE_MAIL (FAVORITES)
count = EmailContent.count(mActivity, Message.CONTENT_URI, FAVORITE_COUNT_SELECTION, null);
if (count > 0) {
row = childCursor.newRow();
row.add(Long.valueOf(Mailbox.QUERY_ALL_FAVORITES)); // MAILBOX_COLUMN_ID = 0;
// MAILBOX_DISPLAY_NAME
row.add(mActivity.getString(R.string.account_folder_list_summary_starred));
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
row.add(Integer.valueOf(Mailbox.TYPE_MAIL)); // MAILBOX_TYPE = 3;
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
}
// TYPE_DRAFTS
count = getCountByMailboxType(mActivity, Mailbox.TYPE_DRAFTS);
if (count > 0) {
row = childCursor.newRow();
row.add(Long.valueOf(Mailbox.QUERY_ALL_DRAFTS)); // MAILBOX_COLUMN_ID = 0;
// MAILBOX_DISPLAY_NAME
row.add(mActivity.getString(R.string.account_folder_list_summary_drafts));
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
row.add(Integer.valueOf(Mailbox.TYPE_DRAFTS)); // MAILBOX_TYPE = 3;
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
}
// TYPE_OUTBOX
count = getCountByMailboxType(mActivity, Mailbox.TYPE_OUTBOX);
if (count > 0) {
row = childCursor.newRow();
row.add(Long.valueOf(Mailbox.QUERY_ALL_OUTBOX)); // MAILBOX_COLUMN_ID = 0;
// MAILBOX_DISPLAY_NAME
row.add(mActivity.getString(R.string.account_folder_list_summary_outbox));
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
row.add(Integer.valueOf(Mailbox.TYPE_OUTBOX)); // MAILBOX_TYPE = 3;
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
}
return childCursor;
}
/**
* Async task to handle the accounts query outside of the UI thread
*/
private class LoadAccountsTask extends AsyncTask<Void, Void, Object[]> {
@Override
protected Object[] doInBackground(Void... params) {
// Create the summaries cursor
Cursor c1 = null;
Cursor c2 = null;
Long defaultAccount = null;
if (!isCancelled()) {
// Create the summaries cursor
c1 = getSummaryChildCursor();
}
if (!isCancelled()) {
// TODO use a custom projection and don't have to sample all of these columns
c2 = mActivity.getContentResolver().query(
EmailContent.Account.CONTENT_URI,
EmailContent.Account.CONTENT_PROJECTION, null, null, null);
}
if (!isCancelled()) {
defaultAccount = Account.getDefaultAccountId(mActivity);
}
if (isCancelled()) {
if (c1 != null) c1.close();
if (c2 != null) c2.close();
return null;
}
return new Object[] { c1, c2 , defaultAccount};
}
@Override
protected void onPostExecute(Object[] params) {
if (isCancelled() || params == null) {
if (params != null) {
Cursor c1 = (Cursor)params[0];
if (c1 != null) {
c1.close();
}
Cursor c2 = (Cursor)params[1];
if (c2 != null) {
c2.close();
}
}
return;
}
// Before writing a new list adapter into the listview, we need to
// shut down the old one (if any).
ListAdapter oldAdapter = mListView.getAdapter();
if (oldAdapter != null && oldAdapter instanceof CursorAdapter) {
((CursorAdapter)oldAdapter).changeCursor(null);
}
// Now create a new list adapter and install it
mListAdapter = AccountsAdapter.getInstance((Cursor)params[0], (Cursor)params[1],
mActivity, (Long)params[2], AccountFolderListFragment.this);
setListAdapter(mListAdapter);
}
}
/**
* Controller results listener. We wrap it with {@link ControllerResultUiThreadWrapper},
* so all methods are called on the UI thread.
*/
private class ControllerResults extends Controller.Result {
@Override
public void updateMailboxCallback(MessagingException result, long accountKey,
long mailboxKey, int progress, int numNewMessages,
ArrayList<Long> addedMessages) {
if (progress == 100) {
updateAccounts();
}
}
@Override
public void sendMailCallback(MessagingException result, long accountId, long messageId,
int progress) {
if (progress == 100) {
updateAccounts();
}
}
@Override
public void deleteAccountCallback(long accountId) {
updateAccounts();
}
}
}

View File

@ -1,118 +0,0 @@
/*
* Copyright (C) 2009 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 com.android.email.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;
/**
* This custom View is the list item for the AccountFolderList activity, and serves two purposes:
* 1. It's a container to store row metadata
* 2. It handles internal clicks so we can create virtual "buttons" in the list
*/
public class AccountFolderListItem extends LinearLayout {
public long mAccountId;
private AccountsAdapter mAdapter;
private boolean mHasFolderButton;
private boolean mDownEvent;
private boolean mCachedViewPositions;
private int mFolderLeft;
private final static float FOLDER_PAD = 5.0F;
public AccountFolderListItem(Context context) {
super(context);
}
public AccountFolderListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Called by the adapter at bindView() time
*
* @param adapter the adapter that creates this view
*/
public void bindViewInit(AccountsAdapter adapter, boolean hasFolderButton) {
mAdapter = adapter;
mCachedViewPositions = false;
mHasFolderButton = hasFolderButton;
}
/**
* Overriding this method allows us to "catch" clicks in the checkbox or star
* and process them accordingly.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mAdapter.isOnDeletingAccountView(mAccountId)) {
return true;
}
// Short-circuit all of this for list items w/o folder buttons
if (!mHasFolderButton) {
return super.onTouchEvent(event);
}
boolean handled = false;
int touchX = (int) event.getX();
if (!mCachedViewPositions) {
float paddingScale = getContext().getResources().getDisplayMetrics().density;
int folderPadding = (int) ((FOLDER_PAD * paddingScale) + 0.5);
mFolderLeft = findViewById(R.id.folder_button).getLeft() - folderPadding;
mCachedViewPositions = true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownEvent = true;
if (touchX > mFolderLeft) {
handled = true;
}
break;
case MotionEvent.ACTION_CANCEL:
mDownEvent = false;
break;
case MotionEvent.ACTION_UP:
if (mDownEvent) {
if (touchX > mFolderLeft) {
mAdapter.onClickFolder(this);
handled = true;
}
}
break;
}
if (handled) {
postInvalidate();
} else {
handled = super.onTouchEvent(event);
}
return handled;
}
}

View File

@ -1,381 +0,0 @@
/*
* 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 com.android.email.FolderProperties;
import com.android.email.R;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Mailbox;
import com.android.emailcommon.provider.EmailContent.MailboxColumns;
import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
/**
* Adapter that presents a combined list of smart mailboxes (e.g. combined inbox, all drafts, etc),
* a non-selectable separator, and the list of accounts.
*/
public class AccountsAdapter extends CursorAdapter {
/**
* Reduced mailbox projection used by AccountsAdapter
*/
public final static String[] MAILBOX_PROJECTION = new String[] {
EmailContent.RECORD_ID, MailboxColumns.DISPLAY_NAME,
MailboxColumns.ACCOUNT_KEY, MailboxColumns.TYPE,
MailboxColumns.UNREAD_COUNT,
MailboxColumns.FLAG_VISIBLE, MailboxColumns.FLAGS
};
public final static int MAILBOX_COLUMN_ID = 0;
public final static int MAILBOX_DISPLAY_NAME = 1;
public final static int MAILBOX_ACCOUNT_KEY = 2;
public final static int MAILBOX_TYPE = 3;
public final static int MAILBOX_UNREAD_COUNT = 4;
public final static int MAILBOX_FLAG_VISIBLE = 5;
public final static int MAILBOX_FLAGS = 6;
private static final String[] MAILBOX_UNREAD_COUNT_PROJECTION = new String [] {
MailboxColumns.UNREAD_COUNT
};
private static final int MAILBOX_UNREAD_COUNT_COLUMN_UNREAD_COUNT = 0;
private static final String MAILBOX_INBOX_SELECTION =
MailboxColumns.ACCOUNT_KEY + " =?" + " AND " + MailboxColumns.TYPE +" = "
+ Mailbox.TYPE_INBOX;
private final LayoutInflater mInflater;
private final int mMailboxesCount;
private final int mSeparatorPosition;
private final long mDefaultAccountId;
private final ArrayList<Long> mOnDeletingAccounts = new ArrayList<Long>();
private Callback mCallback;
public static AccountsAdapter getInstance(Cursor mailboxesCursor, Cursor accountsCursor,
Context context, long defaultAccountId, Callback callback) {
Cursor[] cursors = new Cursor[] { mailboxesCursor, accountsCursor };
Cursor mc = new MergeCursor(cursors);
return new AccountsAdapter(mc, context, mailboxesCursor.getCount(), defaultAccountId,
callback);
}
private AccountsAdapter(Cursor c, Context context, int mailboxesCount, long defaultAccountId,
Callback callback) {
super(context, c, true);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mMailboxesCount = mailboxesCount;
mSeparatorPosition = mailboxesCount;
mDefaultAccountId = defaultAccountId;
mCallback = callback;
}
/**
* When changeCursor(null) is called, drop reference(s) to make sure we don't leak the activity
*/
@Override
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
if (cursor == null) {
mCallback = null;
}
}
/**
* Callback interface used to report clicks other than the basic list item click or longpress.
*/
public interface Callback {
/**
* Callback for clicks on the "folder" icon (to open MailboxList)
*/
public void onClickAccountFolders(long accountId);
}
public boolean isMailbox(int position) {
return position < mMailboxesCount;
}
public boolean isAccount(int position) {
return position > mMailboxesCount;
}
public void addOnDeletingAccount(long accountId) {
mOnDeletingAccounts.add(accountId);
}
public boolean isOnDeletingAccountView(long accountId) {
return mOnDeletingAccounts.contains(accountId);
}
/**
* This is an entry point called by the list item for clicks in the folder "button"
*
* @param itemView the item in which the click occurred
*/
public void onClickFolder(AccountFolderListItem itemView) {
if (mCallback != null) {
mCallback.onClickAccountFolders(itemView.mAccountId);
}
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (cursor.getPosition() < mMailboxesCount) {
bindMailboxItem(view, context, cursor, false);
} else {
bindAccountItem(view, context, cursor, false);
}
}
private void bindMailboxItem(View view, Context context, Cursor cursor, boolean isLastChild)
{
// Reset the view (in case it was recycled) and prepare for binding
AccountFolderListItem itemView = (AccountFolderListItem) view;
itemView.bindViewInit(this, false);
// Invisible (not "gone") to maintain spacing
view.findViewById(R.id.chip).setVisibility(View.INVISIBLE);
String text = cursor.getString(MAILBOX_DISPLAY_NAME);
if (text != null) {
TextView nameView = (TextView) view.findViewById(R.id.name);
nameView.setText(text);
}
// TODO get/track live folder status
text = null;
TextView statusView = (TextView) view.findViewById(R.id.status);
if (text != null) {
statusView.setText(text);
statusView.setVisibility(View.VISIBLE);
} else {
statusView.setVisibility(View.GONE);
}
int count = -1;
text = cursor.getString(MAILBOX_UNREAD_COUNT);
if (text != null) {
count = Integer.valueOf(text);
}
TextView unreadCountView = (TextView) view.findViewById(R.id.new_message_count);
TextView allCountView = (TextView) view.findViewById(R.id.all_message_count);
int id = cursor.getInt(MAILBOX_COLUMN_ID);
// If the unread count is zero, not to show countView.
if (count > 0) {
if (id == Mailbox.QUERY_ALL_FAVORITES
|| id == Mailbox.QUERY_ALL_DRAFTS
|| id == Mailbox.QUERY_ALL_OUTBOX) {
unreadCountView.setVisibility(View.GONE);
allCountView.setVisibility(View.VISIBLE);
allCountView.setText(text);
} else {
allCountView.setVisibility(View.GONE);
unreadCountView.setVisibility(View.VISIBLE);
unreadCountView.setText(text);
}
} else {
allCountView.setVisibility(View.GONE);
unreadCountView.setVisibility(View.GONE);
}
view.findViewById(R.id.folder_button).setVisibility(View.GONE);
view.findViewById(R.id.folder_separator).setVisibility(View.GONE);
view.findViewById(R.id.default_sender).setVisibility(View.GONE);
view.findViewById(R.id.folder_icon).setVisibility(View.VISIBLE);
((ImageView)view.findViewById(R.id.folder_icon)).setImageDrawable(
FolderProperties.getInstance(context).getSummaryMailboxIconIds(id));
}
private void bindAccountItem(View view, Context context, Cursor cursor, boolean isExpanded)
{
// Reset the view (in case it was recycled) and prepare for binding
AccountFolderListItem itemView = (AccountFolderListItem) view;
itemView.bindViewInit(this, true);
itemView.mAccountId = cursor.getLong(Account.CONTENT_ID_COLUMN);
long accountId = cursor.getLong(Account.CONTENT_ID_COLUMN);
// No color chip for now. TODO Revisit for phone UI
view.findViewById(R.id.chip).setVisibility(View.GONE);
String text = cursor.getString(Account.CONTENT_DISPLAY_NAME_COLUMN);
if (text != null) {
TextView descriptionView = (TextView) view.findViewById(R.id.name);
descriptionView.setText(text);
}
text = cursor.getString(Account.CONTENT_EMAIL_ADDRESS_COLUMN);
if (text != null) {
TextView emailView = (TextView) view.findViewById(R.id.status);
emailView.setText(text);
emailView.setVisibility(View.VISIBLE);
}
// TODO: We should not be doing a query inside bindAccountItem
int unreadMessageCount = 0;
Cursor c = context.getContentResolver().query(Mailbox.CONTENT_URI,
MAILBOX_UNREAD_COUNT_PROJECTION,
MAILBOX_INBOX_SELECTION,
new String[] { String.valueOf(accountId) }, null);
try {
if (c.moveToFirst()) {
String count = c.getString(MAILBOX_UNREAD_COUNT_COLUMN_UNREAD_COUNT);
if (count != null) {
unreadMessageCount = Integer.valueOf(count);
}
}
} finally {
c.close();
}
view.findViewById(R.id.all_message_count).setVisibility(View.GONE);
TextView unreadCountView = (TextView) view.findViewById(R.id.new_message_count);
if (unreadMessageCount > 0) {
unreadCountView.setText(String.valueOf(unreadMessageCount));
unreadCountView.setVisibility(View.VISIBLE);
} else {
unreadCountView.setVisibility(View.GONE);
}
view.findViewById(R.id.folder_icon).setVisibility(View.GONE);
view.findViewById(R.id.folder_button).setVisibility(View.VISIBLE);
view.findViewById(R.id.folder_separator).setVisibility(View.VISIBLE);
if (accountId == mDefaultAccountId) {
view.findViewById(R.id.default_sender).setVisibility(View.VISIBLE);
} else {
view.findViewById(R.id.default_sender).setVisibility(View.GONE);
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(R.layout.account_folder_list_item, parent, false);
}
/*
* The following series of overrides insert the "Accounts" separator
*/
/**
* Prevents the separator view from recycling into the other views
*/
@Override
public int getItemViewType(int position) {
if (position == mSeparatorPosition) {
return IGNORE_ITEM_VIEW_TYPE;
}
return super.getItemViewType(position);
}
/**
* Injects the separator view when required, and fudges the cursor for other views
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// The base class's getView() checks for mDataValid at the beginning, but we don't have
// to do that, because if the cursor is invalid getCount() returns 0, in which case this
// method wouldn't get called.
// Handle the separator here - create & bind
if (position == mSeparatorPosition) {
TextView view;
view = (TextView) mInflater.inflate(R.layout.list_separator, parent, false);
view.setText(R.string.account_folder_list_separator_accounts);
return view;
}
return super.getView(getRealPosition(position), convertView, parent);
}
/**
* Forces navigation to skip over the separator
*/
@Override
public boolean areAllItemsEnabled() {
return false;
}
/**
* Forces navigation to skip over the separator
*/
@Override
public boolean isEnabled(int position) {
if (position == mSeparatorPosition) {
return false;
} else if (isAccount(position)) {
Long id = ((MergeCursor)getItem(position)).getLong(Account.CONTENT_ID_COLUMN);
return !isOnDeletingAccountView(id);
} else {
return true;
}
}
/**
* Adjusts list count to include separator
*/
@Override
public int getCount() {
int count = super.getCount();
if (count > 0 && (mSeparatorPosition != ListView.INVALID_POSITION)) {
// Increment for separator, if we have anything to show.
count += 1;
}
return count;
}
/**
* Converts list position to cursor position
*/
private int getRealPosition(int pos) {
if (mSeparatorPosition == ListView.INVALID_POSITION) {
// No separator, identity map
return pos;
} else if (pos <= mSeparatorPosition) {
// Before or at the separator, identity map
return pos;
} else {
// After the separator, remove 1 from the pos to get the real underlying pos
return pos - 1;
}
}
/**
* Returns the item using external position numbering (no separator)
*/
@Override
public Object getItem(int pos) {
return super.getItem(getRealPosition(pos));
}
/**
* Returns the item id using external position numbering (no separator)
*/
@Override
public long getItemId(int pos) {
return super.getItemId(getRealPosition(pos));
}
}

View File

@ -134,17 +134,4 @@ public final class ActivityHelper {
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
}
}
public static void updateRefreshMenuIcon(MenuItem item, boolean show, boolean animate) {
if (show) {
item.setVisible(true);
if (animate) {
item.setActionView(R.layout.action_bar_indeterminate_progress);
} else {
item.setActionView(null);
}
} else {
item.setVisible(false);
}
}
}

View File

@ -20,7 +20,6 @@ import com.android.email.Controller;
import com.android.email.ControllerResultUiThreadWrapper;
import com.android.email.Email;
import com.android.email.MessagingExceptionStrings;
import com.android.email.NotificationController;
import com.android.email.R;
import com.android.emailcommon.Logging;
import com.android.emailcommon.mail.MessagingException;

View File

@ -1,331 +0,0 @@
/*
* Copyright (C) 2009 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 com.android.email.Controller;
import com.android.email.ControllerResultUiThreadWrapper;
import com.android.email.Email;
import com.android.email.MessagingExceptionStrings;
import com.android.email.R;
import com.android.email.RefreshManager;
import com.android.email.activity.setup.AccountSettingsXL;
import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.AccountColumns;
import com.android.emailcommon.utility.Utility;
import java.util.ArrayList;
import android.app.ActionBar;
import android.app.Activity;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.animation.AnimationUtils;
import android.widget.TextView;
// TODO: This has a lot in common with MessageList --should we merge them somehow?
// But maybe we'll need to move to a single activity style anyway -- let's not worry about it too
// much for now. We might even completely ditch this activity and switch to a dialog.
public class MailboxList extends Activity implements MailboxListFragment.Callback {
// Intent extras (internal to this activity)
private static final String EXTRA_ACCOUNT_ID = "com.android.email.activity._ACCOUNT_ID";
// UI support
private ActionBar mActionBar;
private TextView mErrorBanner;
private MailboxListFragment mListFragment;
private Controller.Result mControllerCallback;
// DB access
private long mAccountId;
private AsyncTask<Void, Void, String[]> mLoadAccountNameTask;
/**
* Open a specific account.
*
* @param context
* @param accountId the account to view
*/
public static void actionHandleAccount(Context context, long accountId) {
Intent intent = new Intent(context, MailboxList.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(EXTRA_ACCOUNT_ID, accountId);
context.startActivity(intent);
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
ActivityHelper.debugSetWindowFlags(this);
mAccountId = getIntent().getLongExtra(EXTRA_ACCOUNT_ID, -1);
if (mAccountId == -1) {
finish();
return;
}
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.mailbox_list);
mControllerCallback = new ControllerResultUiThreadWrapper<ControllerResults>(
new Handler(), new ControllerResults());
mActionBar = getActionBar();
mErrorBanner = (TextView) findViewById(R.id.connection_error_text);
mListFragment = (MailboxListFragment) getFragmentManager()
.findFragmentById(R.id.mailbox_list_fragment);
mActionBar.setTitle(R.string.mailbox_list_title);
mListFragment.setCallback(this);
mListFragment.openMailboxes(mAccountId, -1);
// Halt the progress indicator (we'll display it later when needed)
setProgressBarIndeterminate(true);
setProgressBarIndeterminateVisibility(false);
// Go to the database for the account name
mLoadAccountNameTask = new AsyncTask<Void, Void, String[]>() {
@Override
protected String[] doInBackground(Void... params) {
String accountName = null;
Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, mAccountId);
Cursor c = MailboxList.this.getContentResolver().query(
uri, new String[] { AccountColumns.DISPLAY_NAME }, null, null, null);
try {
if (c.moveToFirst()) {
accountName = c.getString(0);
}
} finally {
c.close();
}
return new String[] { accountName };
}
@Override
protected void onPostExecute(String[] result) {
if (result == null) {
return;
}
final String accountName = (String) result[0];
// accountName is null if account name can't be retrieved or query exception
if (accountName == null) {
// something is wrong with this account
finish();
}
mActionBar.setTitle(R.string.mailbox_list_title);
mActionBar.setSubtitle(accountName);
}
}.execute();
}
@Override
public void onPause() {
super.onPause();
Controller.getInstance(getApplication()).removeResultCallback(mControllerCallback);
}
@Override
public void onResume() {
super.onResume();
Controller.getInstance(getApplication()).addResultCallback(mControllerCallback);
// Exit immediately if the accounts list has changed (e.g. externally deleted)
if (Email.getNotifyUiAccountsChanged()) {
Welcome.actionStart(this);
finish();
return;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Utility.cancelTaskInterrupt(mLoadAccountNameTask);
mLoadAccountNameTask = null;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.mailbox_list_option, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onAccounts();
return true;
case R.id.refresh:
onRefresh();
return true;
case R.id.compose:
onCompose();
return true;
case R.id.account_settings:
onEditAccount();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Implements MailboxFragment.Callback
*/
@Override
public void onMailboxSelected(long accountId, long mailboxId, boolean navigate,
boolean dragDrop) {
// TODO handle folder navigation on the phone. when will we actually show the messages?
onOpenMailbox(mailboxId);
}
/**
* Implements MailboxFragment.Callback
*/
@Override
public void onAccountSelected(long accountId) {
// Only used on the Combined view, which isn't used on the phone UI.
}
/**
* Implements MailboxFragment.Callback
*/
@Override
public void onCurrentMailboxUpdated(long mailboxId, String mailboxName, int unreadCount) {
}
/**
* Refresh the mailbox list
*/
private void onRefresh() {
Controller controller = Controller.getInstance(getApplication());
showProgressIcon(true);
RefreshManager.getInstance(this).refreshMailboxList(mAccountId);
}
private void onAccounts() {
AccountFolderList.actionShowAccounts(this);
finish();
}
private void onEditAccount() {
AccountSettingsXL.actionSettings(this, mAccountId);
}
private void onOpenMailbox(long mailboxId) {
MessageList.actionHandleMailbox(this, mailboxId);
}
private void onCompose() {
MessageCompose.actionCompose(this, mAccountId);
}
private void showProgressIcon(boolean show) {
setProgressBarIndeterminateVisibility(show);
}
private void showErrorBanner(String message) {
boolean isVisible = mErrorBanner.getVisibility() == View.VISIBLE;
if (message != null) {
mErrorBanner.setText(message);
if (!isVisible) {
mErrorBanner.setVisibility(View.VISIBLE);
mErrorBanner.startAnimation(
AnimationUtils.loadAnimation(
MailboxList.this, R.anim.header_appear));
}
} else {
if (isVisible) {
mErrorBanner.setVisibility(View.GONE);
mErrorBanner.startAnimation(
AnimationUtils.loadAnimation(
MailboxList.this, R.anim.header_disappear));
}
}
}
/**
* Controller results listener. We wrap it with {@link ControllerResultUiThreadWrapper},
* so all methods are called on the UI thread.
*/
private class ControllerResults extends Controller.Result {
@Override
public void updateMailboxListCallback(MessagingException result, long accountKey,
int progress) {
if (accountKey == mAccountId) {
updateBanner(result, progress);
updateProgress(result, progress);
}
}
@Override
public void updateMailboxCallback(MessagingException result, long accountKey,
long mailboxKey, int progress, int numNewMessages, ArrayList<Long> addedMessages) {
if (accountKey == mAccountId) {
updateBanner(result, progress);
updateProgress(result, progress);
}
}
@Override
public void sendMailCallback(MessagingException result, long accountId, long messageId,
int progress) {
if (accountId == mAccountId) {
updateBanner(result, progress);
updateProgress(result, progress);
}
}
private void updateProgress(MessagingException result, int progress) {
showProgressIcon(result == null && progress < 100);
}
/**
* Show or hide the connection error banner, and convert the various MessagingException
* variants into localizable text. There is hysteresis in the show/hide logic: Once shown,
* the banner will remain visible until some progress is made on the connection. The
* goal is to keep it from flickering during retries in a bad connection state.
*
* @param result
* @param progress
*/
private void updateBanner(MessagingException result, int progress) {
if (result != null) {
showErrorBanner(
MessagingExceptionStrings.getErrorString(MailboxList.this, result));
} else if (progress > 0) {
showErrorBanner(null);
}
}
}
}

View File

@ -1,354 +0,0 @@
/*
* Copyright (C) 2009 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 com.android.email.Email;
import com.android.email.R;
import com.android.email.RefreshManager;
import com.android.email.activity.setup.AccountSecurity;
import com.android.email.activity.setup.AccountSettingsXL;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Mailbox;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.widget.TextView;
import java.util.Set;
public class MessageList extends Activity implements MessageListFragment.Callback {
// Intent extras (internal to this activity)
private static final String EXTRA_ACCOUNT_ID = "com.android.email.activity._ACCOUNT_ID";
private static final String EXTRA_MAILBOX_TYPE = "com.android.email.activity.MAILBOX_TYPE";
private static final String EXTRA_MAILBOX_ID = "com.android.email.activity.MAILBOX_ID";
private static final int REQUEST_SECURITY = 0;
// UI support
private MessageListFragment mListFragment;
private TextView mErrorBanner;
private RefreshManager mRefreshManager;
private final RefreshListener mRefreshListener = new RefreshListener();
private MailboxFinder mMailboxFinder;
private final MailboxFinderCallback mMailboxFinderCallback = new MailboxFinderCallback();
/* package */ MessageListFragment getListFragmentForTest() {
return mListFragment;
}
/**
* Open a specific mailbox.
*
* TODO This should just shortcut to a more generic version that can accept a list of
* accounts/mailboxes (e.g. merged inboxes).
*
* @param context
* @param mailboxType mailbox key
*/
public static void actionHandleMailbox(Context context, long mailboxType) {
context.startActivity(createIntent(context, -1, mailboxType, -1));
}
/**
* Open a specific mailbox by account & type
*
* @param context The caller's context (for generating an intent)
* @param accountId The account to open
* @param mailboxType the type of mailbox to open (e.g. @see EmailContent.Mailbox.TYPE_INBOX)
*/
public static void actionHandleAccount(Context context, long accountId, int mailboxType) {
context.startActivity(createIntent(context, accountId, -1, mailboxType));
}
/**
* Return an intent to open a specific mailbox by account & type.
*
* @param context The caller's context (for generating an intent)
* @param accountId The account to open, or -1
* @param mailboxId the ID of the mailbox to open, or -1
* @param mailboxType the type of mailbox to open (e.g. @see Mailbox.TYPE_INBOX) or -1
*/
public static Intent createIntent(Context context, long accountId, long mailboxId,
int mailboxType) {
Intent intent = new Intent(context, MessageList.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (accountId != -1) intent.putExtra(EXTRA_ACCOUNT_ID, accountId);
if (mailboxId != -1) intent.putExtra(EXTRA_MAILBOX_ID, mailboxId);
if (mailboxType != -1) intent.putExtra(EXTRA_MAILBOX_TYPE, mailboxType);
return intent;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
ActivityHelper.debugSetWindowFlags(this);
setContentView(R.layout.message_list);
mRefreshManager = RefreshManager.getInstance(this);
mRefreshManager.registerListener(mRefreshListener);
mListFragment = (MessageListFragment) getFragmentManager()
.findFragmentById(R.id.message_list_fragment);
mErrorBanner = (TextView) findViewById(R.id.connection_error_text);
mListFragment.setCallback(this);
// Show the appropriate account/mailbox specified by an {@link Intent}.
selectAccountAndMailbox(getIntent());
}
/**
* Show the appropriate account/mailbox specified by an {@link Intent}.
*/
private void selectAccountAndMailbox(Intent intent) {
long mailboxId = intent.getLongExtra(EXTRA_MAILBOX_ID, -1);
if (mailboxId != -1) {
mListFragment.openMailbox(mailboxId);
} else {
int mailboxType = intent.getIntExtra(EXTRA_MAILBOX_TYPE, Mailbox.TYPE_INBOX);
Uri uri = intent.getData();
// TODO Possible ANR. getAccountIdFromShortcutSafeUri accesses DB.
long accountId = (uri == null) ? -1
: Account.getAccountIdFromShortcutSafeUri(this, uri);
if (accountId == -1) {
accountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1);
}
if (accountId == -1) {
launchWelcomeAndFinish();
return;
}
mMailboxFinder = new MailboxFinder(this, accountId, mailboxType,
mMailboxFinderCallback);
mMailboxFinder.startLookup();
}
}
@Override
public void onResume() {
super.onResume();
// Exit immediately if the accounts list has changed (e.g. externally deleted)
if (Email.getNotifyUiAccountsChanged()) {
Welcome.actionStart(this);
finish();
return;
}
}
@Override
protected void onDestroy() {
if (mMailboxFinder != null) {
mMailboxFinder.cancel();
mMailboxFinder = null;
}
mRefreshManager.unregisterListener(mRefreshListener);
super.onDestroy();
}
private void launchWelcomeAndFinish() {
Welcome.actionStart(this);
finish();
}
@Override
public void onListLoaded() {
// Now we know if the mailbox is refreshable
updateProgressIcon();
}
/**
* Called when the list fragment can't find mailbox/account.
*/
@Override
public void onMailboxNotFound() {
finish();
}
@Override
public void onMessageOpen(long messageId, long messageMailboxId, long listMailboxId, int type) {
if (type == MessageListFragment.Callback.TYPE_DRAFT) {
MessageCompose.actionEditDraft(this, messageId);
} else {
// WARNING: here we pass "listMailboxId", which can be the negative id of
// a combined mailbox, instead of the mailboxId of the particular message that
// is opened. This is to support the next/prev buttons on the message view
// properly even for combined mailboxes.
MessageView.actionView(this, messageId, listMailboxId);
}
}
@Override
public void onEnterSelectionMode(boolean enter) {
}
@Override
public void onAdvancingOpAccepted(Set<Long> affectedMessages) {
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.message_list_option, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// this method can be called in the very early stage of the activity lifecycle, where
// mListFragment isn't ready yet.
boolean show = (mListFragment != null) && mListFragment.isRefreshable();
boolean animate = (mListFragment != null) && mRefreshManager.isMessageListRefreshing(
mListFragment.getMailboxId());
ActivityHelper.updateRefreshMenuIcon(menu.findItem(R.id.refresh), show, animate);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.refresh:
mListFragment.onRefresh(true);
return true;
case R.id.folders:
onFolders();
return true;
case R.id.accounts:
onAccounts();
return true;
case R.id.compose:
onCompose();
return true;
case R.id.account_settings:
onEditAccount();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void onFolders() {
long accountId = mListFragment.getAccountId();
// accountId will be -1 when a) mailbox is still loading, or for magic mailboxes.
if (accountId != -1) {
MailboxList.actionHandleAccount(this, accountId);
finish();
}
}
private void onAccounts() {
AccountFolderList.actionShowAccounts(this);
finish();
}
private void onCompose() {
// Passing account = -1 is okay -- the default account will be used.
MessageCompose.actionCompose(this, mListFragment.getAccountId());
}
private void onEditAccount() {
// Passing account = -1 is okay
AccountSettingsXL.actionSettings(this, mListFragment.getAccountId());
}
/**
* Handle the eventual result from the security update activity
*
* Note, this is extremely coarse, and it simply returns the user to the Accounts list.
* Anything more requires refactoring of this Activity.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_SECURITY:
onAccounts();
}
}
private void updateProgressIcon() {
invalidateOptionsMenu(); // animate/stop refreshing icon
}
private void showErrorBanner(String message) {
boolean isVisible = mErrorBanner.getVisibility() == View.VISIBLE;
if (!TextUtils.isEmpty(message)) {
mErrorBanner.setText(message);
if (!isVisible) {
mErrorBanner.setVisibility(View.VISIBLE);
mErrorBanner.startAnimation(
AnimationUtils.loadAnimation(
MessageList.this, R.anim.header_appear));
}
} else {
if (isVisible) {
mErrorBanner.setVisibility(View.GONE);
mErrorBanner.startAnimation(
AnimationUtils.loadAnimation(
MessageList.this, R.anim.header_disappear));
}
}
}
private class RefreshListener implements RefreshManager.Listener {
@Override
public void onMessagingError(long accountId, long mailboxId, String message) {
updateProgressIcon();
showErrorBanner(message);
}
@Override
public void onRefreshStatusChanged(long accountId, long mailboxId) {
updateProgressIcon();
showErrorBanner(null);
}
}
private class MailboxFinderCallback implements MailboxFinder.Callback {
@Override
public void onMailboxFound(long accountId, long mailboxId) {
mListFragment.openMailbox(mailboxId);
}
@Override
public void onAccountNotFound() {
// Let the Welcome activity show the default screen.
launchWelcomeAndFinish();
}
@Override
public void onMailboxNotFound(long accountId) {
// Let the Welcome activity show the default screen.
launchWelcomeAndFinish();
}
@Override
public void onAccountSecurityHold(long accountId) {
// launch the security setup activity
Intent i = AccountSecurity.actionUpdateSecurityIntent(
MessageList.this, accountId, true);
startActivityForResult(i, REQUEST_SECURITY);
}
}
}

View File

@ -1263,7 +1263,7 @@ public class MessageListFragment extends ListFragment
mSelectionMode = mode;
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.message_list_selection_mode, menu);
inflater.inflate(R.menu.message_list_fragment_cab_options, menu);
mMarkRead = menu.findItem(R.id.mark_read);
mMarkUnread = menu.findItem(R.id.mark_unread);
mAddStar = menu.findItem(R.id.add_star);

View File

@ -1,374 +0,0 @@
/*
* 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 com.android.email.Email;
import com.android.email.Preferences;
import com.android.email.R;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.EmailContent.Mailbox;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
/**
* Activity to show (non-EML) email messages.
*
* This activity shows regular email messages, which are not file-based. (i.e. not *.eml or *.msg)
*
* See {@link MessageViewBase} for the class relation diagram.
*/
public class MessageView extends MessageViewBase implements View.OnClickListener,
MessageOrderManager.Callback, MessageViewFragment.Callback {
private static final String EXTRA_MESSAGE_ID = "com.android.email.MessageView_message_id";
private static final String EXTRA_MAILBOX_ID = "com.android.email.MessageView_mailbox_id";
// for saveInstanceState()
private static final String STATE_MESSAGE_ID = "messageId";
private long mMessageId;
private long mMailboxId;
private MessageOrderManager mOrderManager;
private MessageViewFragment mFragment;
private View mMoveToNewer;
private View mMoveToOlder;
private View mReplyButton;
private View mReplyAllButton;
// False when a message can't be forwarded/replied, such as trashed messages
private boolean mReplyAndForwardEnabled;
/**
* View a specific message found in the Email provider.
* @param messageId the message to view.
* @param mailboxId identifies the sequence of messages used for newer/older navigation.
*/
public static void actionView(Context context, long messageId, long mailboxId) {
context.startActivity(getActionViewIntent(context, messageId, mailboxId));
}
public static Intent getActionViewIntent(Context context, long messageId, long mailboxId) {
if (messageId < 0) {
throw new IllegalArgumentException("MessageView invalid messageId " + messageId);
}
Intent i = new Intent(context, MessageView.class);
i.putExtra(EXTRA_MESSAGE_ID, messageId);
i.putExtra(EXTRA_MAILBOX_ID, mailboxId);
return i;
}
@Override
protected int getLayoutId() {
return R.layout.message_view;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mFragment = (MessageViewFragment) getFragmentManager()
.findFragmentById(R.id.message_view_fragment);
mFragment.setCallback(this);
mMoveToNewer = UiUtilities.getView(this, R.id.moveToNewer);
mMoveToOlder = UiUtilities.getView(this, R.id.moveToOlder);
mMoveToNewer.setOnClickListener(this);
mMoveToOlder.setOnClickListener(this);
mReplyButton = UiUtilities.getView(this, R.id.reply);
mReplyAllButton = UiUtilities.getView(this, R.id.reply_all);
initFromIntent();
if (icicle != null) {
mMessageId = icicle.getLong(STATE_MESSAGE_ID, mMessageId);
}
}
private void initFromIntent() {
Intent intent = getIntent();
mMessageId = intent.getLongExtra(EXTRA_MESSAGE_ID, -1);
mMailboxId = intent.getLongExtra(EXTRA_MAILBOX_ID, -1);
if (mMessageId == -1 || mMailboxId == -1) {
Log.w(Logging.LOG_TAG, "Insufficient intent parameter. Closing...");
finish();
return;
}
enableForwardReply(false);
}
@Override
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
if (mMessageId != -1) {
state.putLong(STATE_MESSAGE_ID, mMessageId);
}
}
@Override
public void onResume() {
super.onResume();
// Exit immediately if the accounts list has changed (e.g. externally deleted)
if (Email.getNotifyUiAccountsChanged()) {
Welcome.actionStart(this);
finish();
return;
}
mOrderManager = new MessageOrderManager(this, mMailboxId, this);
messageChanged();
}
@Override
public void onPause() {
if (mOrderManager != null) {
mOrderManager.close();
mOrderManager = null;
}
super.onPause();
}
// Note the return type is a subclass of that of the super class method.
@Override
protected MessageViewFragment getFragment() {
return mFragment;
}
@Override
protected long getAccountId() {
return getFragment().getAccountId();
}
@Override
public void onMessageViewShown(int mailboxType) {
super.onMessageViewShown(mailboxType);
enableForwardReply(mailboxType != Mailbox.TYPE_TRASH);
}
private void onDeleteMessage() {
// the delete triggers mCursorObserver in MessageOrderManager.
// first move to older/newer before the actual delete
long messageIdToDelete = mMessageId;
boolean moved = autoAdvance();
ActivityHelper.deleteMessage(this, messageIdToDelete);
if (!moved) {
// this generates a benign warning "Duplicate finish request" because
// MessageOrderManager detects that the current message is gone, and we finish() it
// in the onMessageNotFound() callback.
finish();
}
}
/**
* Auto-advances the message being shown according to the auto-advance policy set in preferences
* @return Whether or not a new message was selected. This will return false either if there are
* no appropriate messages to advance to, or if the preferences indicate we should not
* auto-advance
*/
private boolean autoAdvance() {
switch (Preferences.getPreferences(this).getAutoAdvanceDirection()) {
case Preferences.AUTO_ADVANCE_NEWER:
return moveToNewer();
case Preferences.AUTO_ADVANCE_OLDER:
return moveToOlder();
}
return false;
}
private boolean moveToOlder() {
if (mOrderManager != null && mOrderManager.moveToOlder()) {
mMessageId = mOrderManager.getCurrentMessageId();
messageChanged();
return true;
}
return false;
}
private boolean moveToNewer() {
if (mOrderManager != null && mOrderManager.moveToNewer()) {
mMessageId = mOrderManager.getCurrentMessageId();
messageChanged();
return true;
}
return false;
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.reply:
onReply();
break;
case R.id.reply_all:
onReplyAll();
break;
case R.id.delete:
onDeleteMessage();
break;
case R.id.moveToOlder:
moveToOlder();
break;
case R.id.moveToNewer:
moveToNewer();
break;
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean handled = handleMenuItem(item.getItemId());
if (!handled) {
handled = super.onOptionsItemSelected(item);
}
return handled;
}
/**
* This is the core functionality of onOptionsItemSelected() but broken out and exposed
* for testing purposes (because it's annoying to mock a MenuItem).
*
* @param menuItemId id that was clicked
* @return true if handled here
*/
/* package */ boolean handleMenuItem(int menuItemId) {
switch (menuItemId) {
case R.id.delete:
onDeleteMessage();
break;
case R.id.reply:
onReply();
break;
case R.id.reply_all:
onReplyAll();
break;
case R.id.forward:
onForward();
break;
case R.id.mark_as_unread:
getFragment().onMarkMessageAsRead(false);
break;
default:
return false;
}
return true;
}
@Override
public void onMessageSetUnread() {
if (!autoAdvance()) {
finish();
}
}
private void enableForwardReply(boolean enabled) {
mReplyAndForwardEnabled = enabled;
mReplyButton.setEnabled(enabled);
mReplyAllButton.setEnabled(enabled);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.message_view_option, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.forward).setEnabled(mReplyAndForwardEnabled);
menu.findItem(R.id.reply).setEnabled(mReplyAndForwardEnabled);
menu.findItem(R.id.reply_all).setEnabled(mReplyAndForwardEnabled);
return super.onPrepareOptionsMenu(menu);
}
/**
* Sync the current message.
* - Set message id to the fragment and the message order manager.
* - Update the navigation arrows.
*/
private void messageChanged() {
getFragment().openMessage(mMessageId);
if (mOrderManager != null) {
mOrderManager.moveTo(mMessageId);
}
updateNavigationArrows();
}
/**
* Update the arrows based on the current position of the older/newer cursor.
*/
private void updateNavigationArrows() {
mMoveToNewer.setEnabled((mOrderManager != null) && mOrderManager.canMoveToNewer());
mMoveToOlder.setEnabled((mOrderManager != null) && mOrderManager.canMoveToOlder());
}
/** Implements {@link MessageOrderManager.Callback#onMessageNotFound()}. */
// TODO Name too generic. Rename this.
@Override
public void onMessageNotFound() {
finish();
}
/** Implements {@link MessageOrderManager.Callback#onMessagesChanged()}. */
// TODO Name too generic. Rename this.
@Override
public void onMessagesChanged() {
updateNavigationArrows();
}
@Override
public void onRespondedToInvite(int response) {
if (!autoAdvance()) {
finish();
}
}
@Override
public void onCalendarLinkClicked(long epochEventStartTime) {
ActivityHelper.openCalendar(this, epochEventStartTime);
}
@Override
public void onReply() {
MessageCompose.actionReply(this, mMessageId, false);
finish();
}
@Override
public void onReplyAll() {
MessageCompose.actionReply(this, mMessageId, true);
finish();
}
@Override
public void onForward() {
MessageCompose.actionForward(this, mMessageId);
finish();
}
@Override
public void onBeforeMessageGone() {
// TODO Implement this
}
}

View File

@ -17,37 +17,14 @@
package com.android.email.activity;
import com.android.email.Controller;
import com.android.email.R;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
/**
* Base class for {@link MessageView} and {@link MessageFileView}.
*
* Class relation diagram:
* <pre>
* (activities) (fragments)
* MessageViewBase MessageViewFragmentBase
* | | (with nested interface Callback)
* | |
* |-- MessageFileView -- owns --> |-- MessageFileViewFragment : For EML files.
* | | (with nested interface Callback, which implements
* | | MessageViewFragmentBase.Callback)
* | |
* |-- MessageView -- owns --> |-- MessageViewFragment : For regular messages
*
* MessageView is basically same as MessageFileView, but has more operations, such as "delete",
* "forward", "reply", etc.
*
* Similarly, MessageViewFragment has more operations than MessageFileViewFragment does, such as
* "mark unread", "respond to invite", etc. Also its Callback interface has more method than
* MessageViewFragmentBase.Callback does, for the extra operations.
* </pre>
* TODO Now that MessageView is gone, we can merge it with {@link MessageFileView}.
*/
public abstract class MessageViewBase extends Activity implements MessageViewFragmentBase.Callback {
private ProgressDialog mFetchAttachmentProgressDialog;
private Controller mController;
protected abstract int getLayoutId();
@ -58,12 +35,6 @@ public abstract class MessageViewBase extends Activity implements MessageViewFra
ActivityHelper.debugSetWindowFlags(this);
setContentView(getLayoutId());
// TODO Turn it into a "managed" dialog?
// Managed dialogs survive activity re-creation. (e.g. orientation change)
mFetchAttachmentProgressDialog = new ProgressDialog(this);
mFetchAttachmentProgressDialog.setIndeterminate(true);
mFetchAttachmentProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mController = Controller.getInstance(getApplication());
}

View File

@ -17,12 +17,15 @@
package com.android.email.activity;
import com.android.email.Email;
import com.android.email.R;
import com.android.email.RefreshManager;
import com.android.email.activity.setup.AccountSettingsXL;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.utility.EmailAsyncTask;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
@ -35,11 +38,16 @@ import java.util.ArrayList;
/**
* Base class for the UI controller.
*
* Note: Always use {@link #commitFragmentTransaction} to commit fragment transactions.
* Note: Always use {@link #commitFragmentTransaction} and {@link #popBackStack} to operate fragment
* transactions.
* (Currently we use synchronous transactions only, but we may want to switch back to asynchronous
* later.)
*/
abstract class UIControllerBase {
protected static final String BUNDLE_KEY_ACCOUNT_ID = "UIController.state.account_id";
protected static final String BUNDLE_KEY_MAILBOX_ID = "UIController.state.mailbox_id";
protected static final String BUNDLE_KEY_MESSAGE_ID = "UIController.state.message_id";
/** No account selected */
static final long NO_ACCOUNT = -1;
/** No mailbox selected */
@ -50,10 +58,10 @@ abstract class UIControllerBase {
/** The owner activity */
final EmailActivity mActivity;
final RefreshManager mRefreshManager;
final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
final RefreshManager mRefreshManager;
/**
* List of fragments that are restored by the framework while the activity is being re-created
* for configuration changes (e.g. screen rotation). We'll install them later when the activity
@ -67,6 +75,19 @@ abstract class UIControllerBase {
*/
private boolean mHoldFragmentInstallation = true;
private final RefreshManager.Listener mRefreshListener
= new RefreshManager.Listener() {
@Override
public void onMessagingError(final long accountId, long mailboxId, final String message) {
updateRefreshProgress();
}
@Override
public void onRefreshStatusChanged(long accountId, long mailboxId) {
updateRefreshProgress();
}
};
public UIControllerBase(EmailActivity activity) {
mActivity = activity;
mRefreshManager = RefreshManager.getInstance(mActivity);
@ -101,6 +122,7 @@ abstract class UIControllerBase {
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, this + " onActivityCreated");
}
mRefreshManager.registerListener(mRefreshListener);
}
/**
@ -147,6 +169,7 @@ abstract class UIControllerBase {
Log.d(Logging.LOG_TAG, this + " onActivityDestroy");
}
mHoldFragmentInstallation = true; // No more fragment installation.
mRefreshManager.unregisterListener(mRefreshListener);
mTaskTracker.cancellAllInterrupt();
}
@ -209,6 +232,11 @@ abstract class UIControllerBase {
}
}
// not used
void popBackStack(FragmentManager fm, String name, int flags) {
fm.popBackStackImmediate(name, flags);
}
void commitFragmentTransaction(FragmentTransaction ft) {
ft.commit();
mActivity.getFragmentManager().executePendingTransactions();
@ -222,15 +250,29 @@ abstract class UIControllerBase {
*/
public abstract long getUIAccountId();
/**
* @return true if an account is selected, or the current view is the combined view.
*/
public final boolean isAccountSelected() {
return getUIAccountId() != NO_ACCOUNT;
}
/**
* @return if an actual account is selected. (i.e. {@link Account#ACCOUNT_ID_COMBINED_VIEW}
* is not considered "actual".s)
*/
public final boolean isActualAccountSelected() {
return isAccountSelected() && (getUIAccountId() != Account.ACCOUNT_ID_COMBINED_VIEW);
}
/**
* @return the currently selected account ID. If the current view is the combined view,
* it'll return {@link #NO_ACCOUNT}.
*
* @see #getUIAccountId()
*/
public long getActualAccountId() {
final long uiAccountId = getUIAccountId();
return uiAccountId == Account.ACCOUNT_ID_COMBINED_VIEW ? NO_ACCOUNT : uiAccountId;
public final long getActualAccountId() {
return isActualAccountSelected() ? getUIAccountId() : NO_ACCOUNT;
}
/**
@ -263,19 +305,71 @@ abstract class UIControllerBase {
/**
* Handles the {@link android.app.Activity#onCreateOptionsMenu} callback.
*/
public abstract boolean onCreateOptionsMenu(MenuInflater inflater, Menu menu);
public boolean onCreateOptionsMenu(MenuInflater inflater, Menu menu) {
inflater.inflate(R.menu.email_activity_options, menu);
return true;
}
/**
* Handles the {@link android.app.Activity#onPrepareOptionsMenu} callback.
*/
public abstract boolean onPrepareOptionsMenu(MenuInflater inflater, Menu menu);
public boolean onPrepareOptionsMenu(MenuInflater inflater, Menu menu) {
// Update the refresh button.
MenuItem item = menu.findItem(R.id.refresh);
if (isRefreshEnabled()) {
item.setVisible(true);
if (isRefreshInProgress()) {
item.setActionView(R.layout.action_bar_indeterminate_progress);
} else {
item.setActionView(null);
}
} else {
item.setVisible(false);
}
return true;
}
/**
* Handles the {@link android.app.Activity#onOptionsItemSelected} callback.
*
* @return true if the option item is handled.
*/
public abstract boolean onOptionsItemSelected(MenuItem item);
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// Comes from the action bar when the app icon on the left is pressed.
// It works like a back press, but it won't close the activity.
return onBackPressed(false);
case R.id.compose:
return onCompose();
case R.id.refresh:
onRefresh();
return true;
case R.id.account_settings:
return onAccountSettings();
}
return false;
}
/**
* Opens the message compose activity.
*/
private boolean onCompose() {
if (!isAccountSelected()) {
return false; // this shouldn't really happen
}
MessageCompose.actionCompose(mActivity, getActualAccountId());
return true;
}
/**
* Handles the "Settings" option item. Opens the settings activity.
*/
private boolean onAccountSettings() {
AccountSettingsXL.actionSettings(mActivity, getActualAccountId());
return true;
}
/**
* STOPSHIP For experimental UI. Remove this.
@ -292,10 +386,30 @@ abstract class UIControllerBase {
public abstract long getMailboxSettingsMailboxId();
/**
* STOPSHIP For experimental UI. Remove this.
* STOPSHIP For experimental UI. Make it abstract protected.
*
* Performs "refesh".
*/
public void onRefresh() {
public abstract void onRefresh();
/**
* @return true if refresh is in progress for the current mailbox.
*/
protected abstract boolean isRefreshInProgress();
/**
* @return true if the UI should enable the "refresh" command.
*/
protected abstract boolean isRefreshEnabled();
/**
* Start/stop the "refresh" animation on the action bar according to the current refresh state.
*
* (We start the animation if {@link #isRefreshInProgress} returns true,
* and stop otherwise.)
*/
protected void updateRefreshProgress() {
mActivity.invalidateOptionsMenu();
}
}

View File

@ -16,57 +16,391 @@
package com.android.email.activity;
import com.android.email.Email;
import com.android.email.R;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.EmailContent.Mailbox;
import com.android.emailcommon.utility.Utility;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
import java.util.Set;
/**
* UI Controller for non x-large devices. Supports a single-pane layout.
*
* STOPSHIP Everything in this class is 100% temporary at this point
* - Navigation model is different from what it should be (whatever it'll be).
* e.g. when the app is launched, we should show Inbox, not mailbox list.
*
* - It uses the two-pane action bar only so that we can change accounts
*
* Major TODOs
* - TODO Proper Navigation model, including retaining fragments to keep state such as the scroll
* position and batch selection.
* - TODO Nested folders
* - TODO Newer/Older for message view
* - TODO Implement callbacks
*/
class UIControllerOnePane extends UIControllerBase {
private ActionBarController mActionBarController;
/**
* Current account/mailbox/message IDs.
* Don't use them directly; use the accessors instead, as we might want to get them from the
* topmost fragment in the future.
*/
private long mCurrentAccountId = NO_ACCOUNT;
private long mCurrentMailboxId = NO_MAILBOX;
private long mCurrentMessageId = NO_MESSAGE;
private MessageCommandButtonView mMessageCommandButtons;
private final MailboxListFragment.Callback mMailboxListFragmentCallback =
new MailboxListFragment.Callback() {
@Override
public void onAccountSelected(long accountId) {
openAccount(accountId);
}
@Override
public void onCurrentMailboxUpdated(long mailboxId, String mailboxName, int unreadCount) {
}
@Override
public void onMailboxSelected(
long accountId, long mailboxId, boolean navigate, boolean dragDrop) {
open(accountId, mailboxId, NO_MESSAGE);
}
};
private final MessageListFragment.Callback mMessageListFragmentCallback =
new MessageListFragment.Callback() {
@Override
public void onAdvancingOpAccepted(Set<Long> affectedMessages) {
// Nothing to do on 1 pane.
}
@Override
public void onEnterSelectionMode(boolean enter) {
// TODO Auto-generated method stub
}
@Override
public void onListLoaded() {
// TODO Auto-generated method stub
}
@Override
public void onMailboxNotFound() {
open(getUIAccountId(), NO_MAILBOX, NO_MESSAGE);
}
@Override
public void onMessageOpen(
long messageId, long messageMailboxId, long listMailboxId, int type) {
if (type == MessageListFragment.Callback.TYPE_DRAFT) {
MessageCompose.actionEditDraft(mActivity, messageId);
} else {
open(getUIAccountId(), getMailboxId(), messageId);
}
}
};
private final MessageViewFragment.Callback mMessageViewFragmentCallback =
new MessageViewFragment.Callback() {
@Override
public void onForward() {
MessageCompose.actionForward(mActivity, getMessageId());
}
@Override
public void onReply() {
MessageCompose.actionReply(mActivity, getMessageId(), false);
}
@Override
public void onReplyAll() {
MessageCompose.actionReply(mActivity, getMessageId(), true);
}
@Override
public void onCalendarLinkClicked(long epochEventStartTime) {
ActivityHelper.openCalendar(mActivity, epochEventStartTime);
}
@Override
public boolean onUrlInMessageClicked(String url) {
return ActivityHelper.openUrlInMessage(mActivity, url, getActualAccountId());
}
@Override
public void onBeforeMessageGone() {
// TODO Auto-generated method stub
}
@Override
public void onMessageSetUnread() {
// TODO Auto-generated method stub
}
@Override
public void onRespondedToInvite(int response) {
// TODO Auto-generated method stub
}
@Override
public void onLoadMessageError(String errorMessage) {
// TODO Auto-generated method stub
}
@Override
public void onLoadMessageFinished() {
// TODO Auto-generated method stub
}
@Override
public void onLoadMessageStarted() {
// TODO Auto-generated method stub
}
@Override
public void onMessageNotExists() {
// TODO Auto-generated method stub
}
@Override
public void onMessageViewGone() {
// TODO Auto-generated method stub
}
@Override
public void onMessageViewShown(int mailboxType) {
// TODO Auto-generated method stub
}
};
// This is all temporary as we'll have a different action bar controller for 1-pane.
private final ActionBarController.Callback mActionBarControllerCallback
= new ActionBarController.Callback() {
@Override
public boolean shouldShowMailboxName() {
return false; // no mailbox name/unread count.
}
@Override
public String getCurrentMailboxName() {
return null; // no mailbox name/unread count.
}
@Override
public int getCurrentMailboxUnreadCount() {
return 0; // no mailbox name/unread count.
}
@Override
public boolean shouldShowUp() {
// Always show the UP arrow.
return true;
}
@Override
public long getUIAccountId() {
return UIControllerOnePane.this.getUIAccountId();
}
@Override
public boolean isAccountSelected() {
return UIControllerOnePane.this.isAccountSelected();
}
@Override
public void onAccountSelected(long accountId) {
openAccount(accountId);
}
@Override
public void onNoAccountsFound() {
Welcome.actionStart(mActivity);
mActivity.finish();
}
};
public UIControllerOnePane(EmailActivity activity) {
super(activity);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(BUNDLE_KEY_ACCOUNT_ID, mCurrentAccountId);
outState.putLong(BUNDLE_KEY_MAILBOX_ID, mCurrentMailboxId);
outState.putLong(BUNDLE_KEY_MESSAGE_ID, mCurrentMessageId);
}
@Override
public void restoreInstanceState(Bundle savedInstanceState) {
super.restoreInstanceState(savedInstanceState);
mCurrentAccountId = savedInstanceState.getLong(BUNDLE_KEY_ACCOUNT_ID, NO_ACCOUNT);
mCurrentMailboxId = savedInstanceState.getLong(BUNDLE_KEY_MAILBOX_ID, NO_MAILBOX);
mCurrentMessageId = savedInstanceState.getLong(BUNDLE_KEY_MESSAGE_ID, NO_MESSAGE);
}
@Override
public int getLayoutId() {
return R.layout.email_activity_one_pane;
}
@Override
public void onActivityViewReady() {
super.onActivityViewReady();
mActionBarController = new ActionBarController(mActivity, mActivity.getLoaderManager(),
mActivity.getActionBar(), mActionBarControllerCallback);
mMessageCommandButtons = UiUtilities.getView(mActivity, R.id.message_command_buttons);
mMessageCommandButtons.setCallback(new CommandButtonCallback());
}
@Override
public void onActivityCreated() {
super.onActivityCreated();
mActionBarController.onActivityCreated();
}
@Override
public void onActivityResume() {
super.onActivityResume();
refreshActionBar();
}
@Override
public long getUIAccountId() {
return -1;
return mCurrentAccountId;
}
private long getMailboxId() {
return mCurrentMailboxId;
}
private long getMessageId() {
return mCurrentMessageId;
}
private void refreshActionBar() {
if (mActionBarController != null) {
mActionBarController.refresh();
}
mActivity.invalidateOptionsMenu();
}
private boolean isMailboxListVisible() {
return (getMailboxId() == NO_MAILBOX);
}
private boolean isMessageListVisible() {
return (getMailboxId() != NO_MAILBOX) && (getMessageId() == NO_MESSAGE);
}
private boolean isMessageViewVisible() {
return (getMailboxId() != NO_MAILBOX) && (getMessageId() != NO_MESSAGE);
}
@Override
public boolean onBackPressed(boolean isSystemBackKey) {
return false;
if (isMessageViewVisible()) {
open(getUIAccountId(), getMailboxId(), NO_MESSAGE);
return true;
} else if (isMessageListVisible()) {
open(getUIAccountId(), NO_MAILBOX, NO_MESSAGE);
return true;
} else {
// STOPSHIP Remove this and return false. This is so that the app can be closed
// with the UP press. (usuful when the device doesn't have a HW back key.)
mActivity.finish();
return true;
// return false;
}
}
@Override
public boolean onCreateOptionsMenu(MenuInflater inflater, Menu menu) {
return false;
}
@Override
public boolean onPrepareOptionsMenu(MenuInflater inflater, Menu menu) {
return false;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return false;
}
@Override
public void open(long accountId, long mailboxId, long messageId) {
void installFragment(Fragment fragment) {
super.installFragment(fragment);
if (fragment instanceof MailboxListFragment) {
((MailboxListFragment) fragment).setCallback(mMailboxListFragmentCallback);
} else if (fragment instanceof MessageListFragment) {
((MessageListFragment) fragment).setCallback(mMessageListFragmentCallback);
} else if (fragment instanceof MessageViewFragment) {
((MessageViewFragment) fragment).setCallback(mMessageViewFragmentCallback);
} else {
// Ignore -- uninteresting fragments such as dialogs.
}
}
@Override
public void openAccount(long accountId) {
open(accountId, NO_MAILBOX, NO_MESSAGE);
}
@Override
public void open(final long accountId, final long mailboxId, final long messageId) {
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, this + " open accountId=" + accountId
+ " mailboxId=" + mailboxId + " messageId=" + messageId);
}
if (accountId == NO_ACCOUNT) {
throw new IllegalArgumentException();
}
// !!! It's all temporary to make 1 pane UI (barely) usable !!!
//
// - Nested folders still doesn't work
// - When opening a child view (e.g. message list -> message view), we should retain
// the current fragment so that all the state (selection, scroll position, etc) will be
// restored when back.
if ((getUIAccountId() == accountId) && (getMailboxId() == mailboxId)
&& (getMessageId() == messageId)) {
return;
}
final FragmentManager fm = mActivity.getFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
if (messageId != NO_MESSAGE) {
ft.replace(R.id.fragment_placeholder, MessageViewFragment.newInstance(messageId));
} else if (mailboxId != NO_MAILBOX) {
ft.replace(R.id.fragment_placeholder, MessageListFragment.newInstance(mailboxId));
} else {
ft.replace(R.id.fragment_placeholder,
MailboxListFragment.newInstance(accountId, Mailbox.PARENT_KEY_NONE));
}
mCurrentAccountId = accountId;
mCurrentMailboxId = mailboxId;
mCurrentMessageId = messageId;
commitFragmentTransaction(ft);
refreshActionBar();
}
/*
@ -74,7 +408,8 @@ class UIControllerOnePane extends UIControllerBase {
*/
@Override
public long getMailboxSettingsMailboxId() {
// Mailbox settigns is still experimental, and doesn't have to work on the phone.
// Mailbox settings is still experimental, and doesn't have to work on the phone.
Utility.showToast(mActivity, "STOPSHIP: Mailbox settings not supported on 1 pane");
return -1;
}
@ -84,6 +419,51 @@ class UIControllerOnePane extends UIControllerBase {
@Override
public long getSearchMailboxId() {
// Search is still experimental, and doesn't have to work on the phone.
Utility.showToast(mActivity, "STOPSHIP: Search not supported on 1 pane");
return -1;
}
private class CommandButtonCallback implements MessageCommandButtonView.Callback {
@Override
public void onMoveToNewer() {
// TODO
}
@Override
public void onMoveToOlder() {
// TODO
}
}
@Override
protected boolean isRefreshEnabled() {
// Refreshable only when an actual account is selected, and message view isn't shown.
// (i.e. only available on the mailbox list or the message view, but not on the combined
// one)
return isActualAccountSelected() && !isMessageViewVisible();
}
@Override
public void onRefresh() {
if (!isRefreshEnabled()) {
return;
}
if (isMessageListVisible()) {
mRefreshManager.refreshMessageList(getActualAccountId(), getMailboxId(), true);
} else {
mRefreshManager.refreshMailboxList(getActualAccountId());
}
}
@Override
protected boolean isRefreshInProgress() {
if (!isRefreshEnabled()) {
return false;
}
if (isMessageListVisible()) {
return mRefreshManager.isMessageListRefreshing(getMailboxId());
} else {
return mRefreshManager.isMailboxListRefreshing(getActualAccountId());
}
}
}

View File

@ -22,7 +22,6 @@ import com.android.email.Preferences;
import com.android.email.R;
import com.android.email.RefreshManager;
import com.android.email.activity.setup.AccountSecurity;
import com.android.email.activity.setup.AccountSettingsXL;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Mailbox;
@ -36,9 +35,6 @@ import android.app.FragmentTransaction;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import java.security.InvalidParameterException;
import java.util.Set;
@ -53,9 +49,6 @@ class UIControllerTwoPane extends UIControllerBase implements
MailboxListFragment.Callback,
MessageListFragment.Callback,
MessageViewFragment.Callback {
private static final String BUNDLE_KEY_ACCOUNT_ID = "UIControllerTwoPane.state.account_id";
private static final String BUNDLE_KEY_MAILBOX_ID = "UIControllerTwoPane.state.mailbox_id";
private static final String BUNDLE_KEY_MESSAGE_ID = "UIControllerTwoPane.state.message_id";
private static final String BUNDLE_KEY_MAILBOX_STACK
= "UIControllerTwoPane.state.mailbox_stack";
@ -102,7 +95,6 @@ class UIControllerTwoPane extends UIControllerBase implements
private MailboxFinder mMailboxFinder;
private final RefreshListener mRefreshListener = new RefreshListener();
private MessageOrderManager mOrderManager;
private final MessageOrderManagerCallback mMessageOrderManagerCallback =
new MessageOrderManagerCallback();
@ -383,9 +375,7 @@ class UIControllerTwoPane extends UIControllerBase implements
*/
@Override
public void onActivityViewReady() {
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, this + " onActivityViewReady");
}
super.onActivityViewReady();
mActionBarController = new ActionBarController(mActivity, mActivity.getLoaderManager(),
mActivity.getActionBar(), mActionBarControllerCallback);
@ -407,17 +397,6 @@ class UIControllerTwoPane extends UIControllerBase implements
return mAccountId;
}
/**
* @return the currently selected account ID. If the current view is the combined view,
* it'll return {@link #NO_ACCOUNT}.
*
* @see #getUIAccountId()
*/
@Override
public long getActualAccountId() {
return mAccountId == Account.ACCOUNT_ID_COMBINED_VIEW ? NO_ACCOUNT : mAccountId;
}
/**
* Returns the id of the mailbox used for the message list fragment.
* IMPORTANT: Do not confuse this with {@link #mMailboxListMailboxId} which is the id used
@ -449,25 +428,19 @@ class UIControllerTwoPane extends UIControllerBase implements
return mMessageId;
}
/**
* @return true if an account is selected, or the current view is the combined view.
*/
public boolean isAccountSelected() {
return getUIAccountId() != NO_ACCOUNT;
}
public boolean isMailboxSelected() {
private boolean isMailboxSelected() {
return getMessageListMailboxId() != NO_MAILBOX;
}
public boolean isMessageSelected() {
private boolean isMessageSelected() {
return getMessageId() != NO_MESSAGE;
}
/**
* @return true if refresh is in progress for the current mailbox.
*/
public boolean isRefreshInProgress() {
@Override
protected boolean isRefreshInProgress() {
long messageListMailboxId = getMessageListMailboxId();
return (messageListMailboxId >= 0)
&& mRefreshManager.isMessageListRefreshing(messageListMailboxId);
@ -476,7 +449,8 @@ class UIControllerTwoPane extends UIControllerBase implements
/**
* @return true if the UI should enable the "refresh" command.
*/
public boolean isRefreshEnabled() {
@Override
protected boolean isRefreshEnabled() {
// - Don't show for combined inboxes, but
// - Show even for non-refreshable mailboxes, in which case we refresh the mailbox list
return -1 != getActualAccountId();
@ -488,7 +462,6 @@ class UIControllerTwoPane extends UIControllerBase implements
@Override
public void onActivityCreated() {
super.onActivityCreated();
mRefreshManager.registerListener(mRefreshListener);
mActionBarController.onActivityCreated();
}
@ -525,7 +498,6 @@ class UIControllerTwoPane extends UIControllerBase implements
@Override
public void onActivityDestroy() {
closeMailboxFinder();
mRefreshManager.unregisterListener(mRefreshListener);
super.onActivityDestroy();
}
@ -924,43 +896,6 @@ class UIControllerTwoPane extends UIControllerBase implements
return false;
}
/**
* Handles {@link android.app.Activity#onCreateOptionsMenu} callback.
*/
@Override
public boolean onCreateOptionsMenu(MenuInflater inflater, Menu menu) {
inflater.inflate(R.menu.email_activity_options, menu);
return true;
}
/** {@inheritDoc} */
@Override
public boolean onPrepareOptionsMenu(MenuInflater inflater, Menu menu) {
ActivityHelper.updateRefreshMenuIcon(menu.findItem(R.id.refresh),
isRefreshEnabled(),
isRefreshInProgress());
return true;
}
/** {@inheritDoc} */
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// Comes from the action bar when the app icon on the left is pressed.
// It works like a back press, but it won't close the activity.
return onBackPressed(false);
case R.id.compose:
return onCompose();
case R.id.refresh:
onRefresh();
return true;
case R.id.account_settings:
return onAccountSettings();
}
return false;
}
/** {@inheritDoc} */
@Override
public boolean onBackPressed(boolean isSystemBackKey) {
@ -979,25 +914,6 @@ class UIControllerTwoPane extends UIControllerBase implements
return false;
}
/**
* Handles the "Compose" option item. Opens the message compose activity.
*/
private boolean onCompose() {
if (!isAccountSelected()) {
return false; // this shouldn't really happen
}
MessageCompose.actionCompose(mActivity, getActualAccountId());
return true;
}
/**
* Handles the "Compose" option item. Opens the settings activity.
*/
private boolean onAccountSettings() {
AccountSettingsXL.actionSettings(mActivity, getActualAccountId());
return true;
}
/**
* Handles the "refresh" option item. Opens the settings activity.
* TODO used by experimental code in the activity -- otherwise can be private.
@ -1009,29 +925,6 @@ class UIControllerTwoPane extends UIControllerBase implements
getMessageListMailboxId()).cancelPreviousAndExecuteParallel();
}
/**
* Start/stop the "refresh" animation on the action bar according to the current refresh state.
*
* (We start the animation if {@link UIControllerTwoPane#isRefreshInProgress} returns true,
* and stop otherwise.)
*/
private void updateRefreshProgress() {
mActivity.invalidateOptionsMenu();
}
private class RefreshListener
implements RefreshManager.Listener {
@Override
public void onMessagingError(final long accountId, long mailboxId, final String message) {
updateRefreshProgress();
}
@Override
public void onRefreshStatusChanged(long accountId, long mailboxId) {
updateRefreshProgress();
}
}
/**
* Class to handle refresh.
*

View File

@ -23,14 +23,12 @@ import com.android.email.provider.AccountBackupRestore;
import com.android.email.service.MailService;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Mailbox;
import com.android.emailcommon.utility.EmailAsyncTask;
import com.google.common.annotations.VisibleForTesting;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
@ -77,16 +75,6 @@ public class Welcome extends Activity {
private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
/**
* @return true if the obsolete phone UI should be used.
*
* STOPSHIP remove this. temporary support for the old activities.
*/
public static boolean useOldPhoneActivities(Context context) {
final int screenLayout = context.getResources().getConfiguration().screenLayout;
return (screenLayout & Configuration.SCREENLAYOUT_SIZE_XLARGE) == 0;
}
/**
* Launch this activity. Note: It's assumed that this activity is only called as a means to
* 'reset' the UI state; Because of this, it is always launched with FLAG_ACTIVITY_CLEAR_TOP,
@ -244,39 +232,20 @@ public class Welcome extends Activity {
} else {
final long accountId = resolveAccountId(mFromActivity, mAccountId, mAccountUuid);
// Use the old phone activities on x-large devices, only when the debug pane mode
// is not specified.
// If the debug pane mode is specified, always use EmailActivity.
// STOPSHIP remove this. temporary support for the old activities.
final boolean useOldPhoneActivities = mDebugPaneMode == 0
&& useOldPhoneActivities(mFromActivity);
if (!useOldPhoneActivities) {
final Intent i;
if (isMessageSelected()) {
i = EmailActivity.createOpenMessageIntent(mFromActivity, accountId,
mMailboxId, mMessageId);
} else if (isMailboxSelected()) {
i = EmailActivity.createOpenMailboxIntent(mFromActivity, accountId,
mMailboxId);
} else {
i = EmailActivity.createOpenAccountIntent(mFromActivity, accountId);
}
if (mDebugPaneMode != 0) {
EmailActivity.forcePaneMode(i, mDebugPaneMode == 2);
}
mFromActivity.startActivity(i);
final Intent i;
if (isMessageSelected()) {
i = EmailActivity.createOpenMessageIntent(mFromActivity, accountId,
mMailboxId, mMessageId);
} else if (isMailboxSelected()) {
i = EmailActivity.createOpenMailboxIntent(mFromActivity, accountId,
mMailboxId);
} else {
// STOPSHIP remove this. temporary support for the old activities.
if (isMessageSelected()) {
MessageView.actionView(mFromActivity, mMessageId, mMailboxId);
} else if (isMailboxSelected()) {
MessageList.actionHandleMailbox(mFromActivity, mMailboxId);
} else {
MessageList.actionHandleAccount(
mFromActivity, accountId, Mailbox.TYPE_INBOX);
}
i = EmailActivity.createOpenAccountIntent(mFromActivity, accountId);
}
if (mDebugPaneMode != 0) {
EmailActivity.forcePaneMode(i, mDebugPaneMode == 2);
}
mFromActivity.startActivity(i);
}
return null;
}

View File

@ -1,278 +0,0 @@
/*
* 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 com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Mailbox;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.database.MatrixCursor.RowBuilder;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
/**
* Basic unit tests of AccountsAdapter
*/
@SmallTest
public class AccountsAdapterTest extends AndroidTestCase {
private Cursor mUpperCursor = null;
private Cursor mLowerCursor = null;
/**
* Make an empty set of magic mailboxes in mUpperCursor
*/
private void setupUpperCursor() {
MatrixCursor childCursor = new MatrixCursor(AccountsAdapter.MAILBOX_PROJECTION);
mUpperCursor = childCursor;
}
/**
* Make a simple set of magic mailboxes in mUpperCursor
*/
private void populateUpperCursor() {
MatrixCursor childCursor = (MatrixCursor) mUpperCursor;
int count;
RowBuilder row;
// TYPE_INBOX
count = 10;
row = childCursor.newRow();
row.add(Long.valueOf(Mailbox.QUERY_ALL_INBOXES)); // MAILBOX_COLUMN_ID = 0;
row.add("Inbox"); // MAILBOX_DISPLAY_NAME
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
row.add(Integer.valueOf(Mailbox.TYPE_INBOX)); // MAILBOX_TYPE = 3;
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
// TYPE_MAIL (FAVORITES)
count = 20;
row = childCursor.newRow();
row.add(Long.valueOf(Mailbox.QUERY_ALL_FAVORITES)); // MAILBOX_COLUMN_ID = 0;
row.add("Favorites"); // MAILBOX_DISPLAY_NAME
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
row.add(Integer.valueOf(Mailbox.TYPE_MAIL)); // MAILBOX_TYPE = 3;
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
// TYPE_DRAFTS
count = 30;
row = childCursor.newRow();
row.add(Long.valueOf(Mailbox.QUERY_ALL_DRAFTS)); // MAILBOX_COLUMN_ID = 0;
row.add("Drafts"); // MAILBOX_DISPLAY_NAME
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
row.add(Integer.valueOf(Mailbox.TYPE_DRAFTS)); // MAILBOX_TYPE = 3;
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
// TYPE_OUTBOX
count = 40;
row = childCursor.newRow();
row.add(Long.valueOf(Mailbox.QUERY_ALL_OUTBOX)); // MAILBOX_COLUMN_ID = 0;
row.add("Outbox"); // MAILBOX_DISPLAY_NAME
row.add(null); // MAILBOX_ACCOUNT_KEY = 2;
row.add(Integer.valueOf(Mailbox.TYPE_OUTBOX)); // MAILBOX_TYPE = 3;
row.add(Integer.valueOf(count)); // MAILBOX_UNREAD_COUNT = 4;
}
/**
* Make an empty set of accounts in mLowerCursor
*/
private void setupLowerCursor() {
MatrixCursor childCursor = new MatrixCursor(Account.CONTENT_PROJECTION);
mLowerCursor = childCursor;
}
/**
* Make a simple set of "accounts".
* Note: We don't fill in the entire width of the projection because the accounts adapter
* only looks at a few of the columns anyway.
*/
private void populateLowerCursor() {
MatrixCursor childCursor = (MatrixCursor) mLowerCursor;
RowBuilder row;
// Account #1
row = childCursor.newRow();
row.add(Long.valueOf(1)); // CONTENT_ID_COLUMN = 0;
row.add("Account 1"); // CONTENT_DISPLAY_NAME_COLUMN = 1;
row.add("account1@android.com"); // CONTENT_EMAIL_ADDRESS_COLUMN = 2;
// Account #2
row = childCursor.newRow();
row.add(Long.valueOf(2)); // CONTENT_ID_COLUMN = 0;
row.add("Account 2"); // CONTENT_DISPLAY_NAME_COLUMN = 1;
row.add("account2@android.com"); // CONTENT_EMAIL_ADDRESS_COLUMN = 2;
}
/**
* Test: General handling of separator
*/
public void testSeparator() {
// Test with fully populated upper and lower sections
setupUpperCursor();
populateUpperCursor();
setupLowerCursor();
populateLowerCursor();
AccountsAdapter adapter = AccountsAdapter.getInstance(mUpperCursor, mLowerCursor,
getContext(), -1, null);
checkAdapter("fully populated", adapter, mUpperCursor, mLowerCursor);
// Test with empty upper and populated lower
setupUpperCursor();
setupLowerCursor();
populateLowerCursor();
adapter = AccountsAdapter.getInstance(mUpperCursor, mLowerCursor, getContext(), -1, null);
checkAdapter("lower populated", adapter, mUpperCursor, mLowerCursor);
// Test with both empty
setupUpperCursor();
setupLowerCursor();
adapter = AccountsAdapter.getInstance(mUpperCursor, mLowerCursor, getContext(), -1, null);
checkAdapter("both empty", adapter, mUpperCursor, mLowerCursor);
}
/**
* Helper to check the various APIs related to the upper/lower separator
*/
private void checkAdapter(String tag, AccountsAdapter adapter, Cursor upper, Cursor lower) {
// Check total count
int expectedCount = 0;
if (upper != null) expectedCount += upper.getCount();
if (lower != null) expectedCount += lower.getCount();
// If one or more items are shown, the adapter inserts the separator as well
if (expectedCount > 0) expectedCount++;
assertEquals(tag, expectedCount, adapter.getCount());
// Check separator-related APIs
int separatorIndex = -1;
if (upper != null) {
separatorIndex = upper.getCount();
}
// Get the MergeCursor that the adapter created
MergeCursor mc = (MergeCursor) adapter.getCursor();
// Check APIs for the position above the separator index
// This will be the last entry in the "upper" cursor
if (separatorIndex > 0) {
int checkIndex = separatorIndex - 1;
assertTrue(tag, adapter.isMailbox(checkIndex));
assertFalse(tag, adapter.isAccount(checkIndex));
assertTrue(tag, adapter.isEnabled(checkIndex));
Cursor c = (Cursor) adapter.getItem(checkIndex);
assertEquals(tag, mc, c);
assertEquals(tag, checkIndex, c.getPosition());
upper.moveToLast();
long id = upper.getLong(0);
assertEquals(tag, id, adapter.getItemId(checkIndex));
}
// Check APIs for position at the separator index
if (separatorIndex >= 0) {
int checkIndex = separatorIndex;
assertFalse(tag, adapter.isMailbox(checkIndex));
assertFalse(tag, adapter.isAccount(checkIndex));
assertFalse(tag, adapter.isEnabled(checkIndex));
// getItem and getItemId should never be called because it should not be enabled
}
// Check APIs for the position below the separator index
// This will be the first entry in the "lower" cursor
if (lower != null && lower.getCount() > 0) {
int checkIndex = separatorIndex + 1;
assertFalse(tag, adapter.isMailbox(checkIndex));
assertTrue(tag, adapter.isAccount(checkIndex));
assertTrue(tag, adapter.isEnabled(checkIndex));
Cursor c = (Cursor) adapter.getItem(checkIndex);
assertEquals(tag, mc, c);
assertEquals(tag, separatorIndex, c.getPosition());
lower.moveToFirst();
long id = lower.getLong(0);
assertEquals(tag, id, adapter.getItemId(checkIndex));
}
}
/**
* Test: isOnDeletingAccountView
*/
public void testDeletingAccount() {
// Test with fully populated upper and lower sections
setupUpperCursor();
populateUpperCursor();
setupLowerCursor();
populateLowerCursor();
AccountsAdapter adapter = AccountsAdapter.getInstance(mUpperCursor, mLowerCursor,
getContext(), -1, null);
// Check enabled state - all should be enabled except for separator
for (int i = 0; i < adapter.getCount(); i++) {
boolean expectEnabled = adapter.isMailbox(i) || adapter.isAccount(i);
assertEquals(expectEnabled, adapter.isEnabled(i));
}
// "Delete" the first account
adapter.addOnDeletingAccount(1); // account Id of 1st account
int account1Position = mUpperCursor.getCount() + 1; // first entry after separator
for (int i = 0; i < adapter.getCount(); i++) {
boolean isNotSeparator = adapter.isMailbox(i) || adapter.isAccount(i);
boolean expectEnabled = isNotSeparator && (i != account1Position);
assertEquals(expectEnabled, adapter.isEnabled(i));
}
}
/**
* Test: callback(s)
*/
public void testCallbacks() {
// Test with fully populated upper and lower sections
setupUpperCursor();
populateUpperCursor();
setupLowerCursor();
populateLowerCursor();
Callback cb = new Callback();
AccountsAdapter adapter = AccountsAdapter.getInstance(mUpperCursor, mLowerCursor,
getContext(), -1, cb);
AccountFolderListItem itemView = new AccountFolderListItem(mContext);
itemView.mAccountId = 1;
adapter.onClickFolder(itemView);
assertTrue(cb.called);
assertEquals(1, cb.id);
}
private static class Callback implements AccountsAdapter.Callback {
public boolean called = false;
public long id = -1;
public void onClickAccountFolders(long accountId) {
called = true;
id = accountId;
}
}
/**
* Tests for cursor management. Make sure that the underlying cursor is closed properly
* when we set the cursor to null.
*/
public void testCursorManagement() {
setupUpperCursor();
setupLowerCursor();
AccountsAdapter adapter = AccountsAdapter.getInstance(mUpperCursor, mLowerCursor,
getContext(), -1, null);
Cursor c = adapter.getCursor();
assertFalse(c.isClosed());
adapter.changeCursor(null);
assertTrue(c.isClosed());
}
}

View File

@ -27,7 +27,7 @@ import android.net.Uri;
import android.test.ActivityInstrumentationTestCase2;
/**
* Test case for {@link MessageTestView}.
* Test case for {@link MessageFileView}.
*
* TODO Add more tests. Any good way to test fragment??
*/
@ -49,7 +49,7 @@ public class MessageFileViewTest extends ActivityInstrumentationTestCase2<Messag
}
private void setUpIntent(Uri uri) {
final Intent i = new Intent(getInstrumentation().getTargetContext(), MessageView.class);
final Intent i = new Intent(getInstrumentation().getTargetContext(), MessageFileView.class);
i.setData(uri);
setActivityIntent(i);
}

View File

@ -1,110 +0,0 @@
/*
* 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 com.android.email.DBTestHelper;
import com.android.email.TestUtils;
import android.content.Context;
import android.content.Intent;
import android.test.ActivityInstrumentationTestCase2;
/**
* Test case for {@link MessageView}.
*
* TODO Add more tests. Any good way to test fragment??
*/
public class MessageViewTest extends ActivityInstrumentationTestCase2<MessageView> {
private static final String EXTRA_MESSAGE_ID = "com.android.email.MessageView_message_id";
private static final String EXTRA_DISABLE_REPLY =
"com.android.email.MessageView_disable_reply";
private static final String EXTRA_MAILBOX_ID = "com.android.email.MessageView_mailbox_id";
private static int TIMEOUT = 10; // in seconds
private Context mProviderContext;
public MessageViewTest() {
super(MessageView.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
mProviderContext = DBTestHelper.ProviderContextSetupHelper.getProviderContext(
getInstrumentation().getTargetContext());
}
private void setUpIntent(long messageId, long mailboxId, boolean disableReply) {
final Intent i = new Intent(getInstrumentation().getTargetContext(), MessageView.class);
i.putExtra(EXTRA_MESSAGE_ID, messageId);
i.putExtra(EXTRA_MAILBOX_ID, mailboxId);
if (disableReply) {
i.putExtra(EXTRA_DISABLE_REPLY, true);
}
setActivityIntent(i);
}
/**
* Open the activity without setting an Intent.
*
* Expected: Activity will close itself.
*/
public void testCreateWithoutParamter() throws Throwable {
// No intent parameters specified. The activity will close itself.
final MessageView activity = getActivity();
TestUtils.waitUntil("", new TestUtils.Condition() {
@Override
public boolean isMet() {
return activity.isFinishing();
}
}, TIMEOUT);
}
// This test doesn't work, because it sets up messages in an separated context, but
// the main activity looks at the actual context and fails to find the message.
// /**
// * Set up account/mailbox/message, and open the activity.
// *
// * Expected: Message opens.
// */
// public void testOpenMessage() throws Exception {
// final Context c = mProviderContext;
// final Account acct1 = ProviderTestUtils.setupAccount("test1", true, c);
// final Account acct2 = ProviderTestUtils.setupAccount("test2", true, c);
// final Mailbox acct2inbox = ProviderTestUtils.setupMailbox("inbox", acct2.mId, true, c);
// final Message msg1 = ProviderTestUtils.setupMessage("message1", acct2.mId, acct2inbox.mId,
// true, true, c);
// final Message msg2 = ProviderTestUtils.setupMessage("message2", acct2.mId, acct2inbox.mId,
// true, true, c);
//
// setUpIntent(msg2.mId, msg2.mMailboxKey, false);
//
// final MessageView activity = getActivity();
//
// TestUtils.waitUntil(new TestUtils.Condition() {
// @Override
// public boolean isMet() {
// MessageViewFragment f = activity.getFragment();
// return f != null && f.isMessageLoadedForTest();
// }
// }, TIMEOUT);
//
// // TODO Check UI elements, once our UI is settled.
// }
}