Create MailboxList for new UI activity.

* Shows mailboxes
* Click to open mailbox (see messages)
* Basic menus
* Fat title bar (appearance)
* Fat title bar (buttons)

Temporary hack to call this from AccountFolderList (when you click the
account groups and "expand" them, you come here instead.)
This commit is contained in:
Andrew Stadler 2009-07-29 19:21:37 -07:00
parent ea69fc40d7
commit 9e5ee4589f
7 changed files with 655 additions and 6 deletions

View File

@ -119,7 +119,7 @@
</activity>
<activity
android:name=".activity.FolderMessageList">
android:name=".activity.MailboxList">
</activity>
<activity

View File

@ -0,0 +1,79 @@
<?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="fill_parent"
android:layout_height="fill_parent">
<!-- TODO: #include a common fat title bar -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
android:gravity="center" >
<View
android:id="@+id/chip"
android:background="@drawable/appointment_indicator_leftside_1"
android:layout_width="4dip"
android:layout_height="56dip"
android:layout_centerVertical="true"
android:layout_marginRight="4dip" />
<LinearLayout
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
<TextView
android:id="@+id/account_name"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="?android:attr/textColorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/account_status"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorTertiary"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<Button
android:id="@+id/button_compose"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@*android:drawable/ic_menu_compose" />
<!-- TODO: Make refresh & progress take the same space so we can toggle them -->
<Button
android:id="@+id/button_refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@*android:drawable/ic_menu_refresh" />
<ProgressBar
android:id="@+id/progress"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:visibility="gone"
android:paddingRight="10dip" />
</LinearLayout>
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>

View File

@ -0,0 +1,58 @@
<?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="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingLeft="24dip"
android:paddingRight="8dip" >
<!-- TODO: folder icon -->
<LinearLayout
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:gravity="center_vertical">
<TextView
android:id="@+id/mailbox_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="?android:attr/textColorPrimary" />
<TextView
android:id="@+id/mailbox_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorTertiary" />
</LinearLayout>
<TextView
android:id="@+id/new_message_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="?android:attr/textColorPrimary"
android:background="@drawable/text_box_light"
android:paddingLeft="10px"
android:paddingRight="10px"
android:minWidth="64px"
android:gravity="right" />
</LinearLayout>

View File

@ -0,0 +1,20 @@
<?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:title="@string/refresh_action" />
</menu>

View File

@ -0,0 +1,36 @@
<?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="@*android:drawable/ic_menu_refresh"
/>
<item android:id="@+id/compose"
android:alphabeticShortcut="c"
android:title="@string/compose_action"
android:icon="@*android:drawable/ic_menu_compose"
/>
<item android:id="@+id/accounts"
android:title="@string/accounts_action"
android:icon="@*android:drawable/ic_menu_account_list"
/>
<item android:id="@+id/account_settings"
android:title="@string/account_settings_action"
android:icon="@android:drawable/ic_menu_preferences"
/>
</menu>

View File

@ -200,6 +200,14 @@ public class AccountFolderList extends ExpandableListActivity {
public void onGroupExpand(int groupPosition) {
super.onGroupExpand(groupPosition);
// TEMP: Launch new MailboxList activity
if (groupPosition > 0) {
Cursor groupCursor = mListAdapter.getGroup(groupPosition);
long accountId = groupCursor.getLong(EmailContent.Account.CONTENT_ID_COLUMN);
MailboxList.actionHandleAccount(this, accountId);
return;
}
// If we don't have a cursor yet, create one
Cursor childCursor = mListAdapter.getChild(groupPosition, 0);
if (childCursor == null) {
@ -365,15 +373,14 @@ public class AccountFolderList extends ExpandableListActivity {
}
/**
* Returns localized name for splecial folders.
* Returns localized name for special folders.
* @param context
* @param sSpecialMailboxDisplayNameListCache
* @param type
* @return
* @param type The mailbox type of interest
* @return A localized name, or null if the mailbox is not special
*/
private static String getSpecialMailboxDisplayName(Context context, int type) {
final int MAILBOX_TYPE_SIZE = 8;
if (sSpecialMailboxDisplayNameListCache != null) {
if (sSpecialMailboxDisplayNameListCache == null) {
sSpecialMailboxDisplayNameListCache = new String[MAILBOX_TYPE_SIZE];
// TYPE_INBOX = 0
sSpecialMailboxDisplayNameListCache[0] = context.getString(

View File

@ -0,0 +1,449 @@
/*
* 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.R;
import com.android.email.activity.setup.AccountSettings;
import com.android.email.mail.MessagingException;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.MailboxColumns;
import android.app.ListActivity;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.CursorAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
public class MailboxList extends ListActivity implements OnItemClickListener, OnClickListener {
// Intent extras (internal to this activity)
private static final String EXTRA_ACCOUNT_ID = "com.android.email.activity._ACCOUNT_ID";
// UI support
private ListView mListView;
private TextView mAccountNameView;
private TextView mAccountStatusView;
private View mRefreshButton;
private View mProgress;
private MailboxListAdapter mListAdapter;
private MailboxListHandler mHandler = new MailboxListHandler();
private ControllerResults mControllerCallback = new ControllerResults();
private static final int[] mColorChipResIds = new int[] {
R.drawable.appointment_indicator_leftside_1,
R.drawable.appointment_indicator_leftside_2,
R.drawable.appointment_indicator_leftside_3,
R.drawable.appointment_indicator_leftside_4,
R.drawable.appointment_indicator_leftside_5,
R.drawable.appointment_indicator_leftside_6,
R.drawable.appointment_indicator_leftside_7,
R.drawable.appointment_indicator_leftside_8,
R.drawable.appointment_indicator_leftside_9,
R.drawable.appointment_indicator_leftside_10,
R.drawable.appointment_indicator_leftside_11,
R.drawable.appointment_indicator_leftside_12,
R.drawable.appointment_indicator_leftside_13,
R.drawable.appointment_indicator_leftside_14,
R.drawable.appointment_indicator_leftside_15,
R.drawable.appointment_indicator_leftside_16,
R.drawable.appointment_indicator_leftside_17,
R.drawable.appointment_indicator_leftside_18,
R.drawable.appointment_indicator_leftside_19,
R.drawable.appointment_indicator_leftside_20,
R.drawable.appointment_indicator_leftside_21,
};
// DB access
private long mAccountId;
private LoadMailboxesTask mLoadMailboxesTask;
/**
* 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.putExtra(EXTRA_ACCOUNT_ID, accountId);
context.startActivity(intent);
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.mailbox_list);
mListView = getListView();
findViewById(R.id.button_compose).setOnClickListener(this);
mRefreshButton = findViewById(R.id.button_refresh);
mRefreshButton.setOnClickListener(this);
mAccountNameView = (TextView) findViewById(R.id.account_name);
mAccountStatusView = (TextView) findViewById(R.id.account_status);
mProgress = findViewById(R.id.progress);
mListView.setOnItemClickListener(this);
mListView.setItemsCanFocus(false);
registerForContextMenu(mListView);
mListAdapter = new MailboxListAdapter(this);
setListAdapter(mListAdapter);
mAccountId = getIntent().getLongExtra(EXTRA_ACCOUNT_ID, -1);
if (mAccountId != -1) {
mLoadMailboxesTask = new LoadMailboxesTask(mAccountId);
mLoadMailboxesTask.execute();
} else {
finish();
}
// setup fat fitle - color chip, name, status, refresh/progress
int chipResId = mColorChipResIds[(int)mAccountId % mColorChipResIds.length];
findViewById(R.id.chip).setBackgroundResource(chipResId);
mAccountStatusView.setVisibility(View.GONE);
mProgress.setVisibility(View.GONE);
// Go to the database for the account name
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
String result = 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()) {
result = c.getString(0);
}
} finally {
c.close();
}
return result;
}
@Override
protected void onPostExecute(String result) {
if (result == null) {
// something is wrong with this account
finish();
}
mAccountNameView.setText(result);
}
}.execute();
}
@Override
public void onPause() {
super.onPause();
Controller.getInstance(getApplication()).removeResultCallback(mControllerCallback);
}
@Override
public void onResume() {
super.onResume();
Controller.getInstance(getApplication()).addResultCallback(mControllerCallback);
// TODO: may need to clear notifications here
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mLoadMailboxesTask != null &&
mLoadMailboxesTask.getStatus() != LoadMailboxesTask.Status.FINISHED) {
mLoadMailboxesTask.cancel(true);
mLoadMailboxesTask = null;
}
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
onOpenMailbox(id);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.button_compose:
onCompose();
break;
case R.id.button_refresh:
onRefresh(-1);
break;
}
}
@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 R.id.refresh:
onRefresh(-1);
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);
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.mailbox_list_context, menu);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info =
(AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
case R.id.refresh:
onRefresh(info.id);
break;
}
return super.onContextItemSelected(item);
}
/**
* Refresh the mailbox list, or a single mailbox
* @param mailboxId -1 for all
*/
private void onRefresh(long mailboxId) {
Controller controller = Controller.getInstance(getApplication());
Account account = Account.restoreAccountWithId(this, mAccountId);
mHandler.progress(true);
if (mailboxId >= 0) {
Mailbox mailbox = Mailbox.restoreMailboxWithId(this, mailboxId);
controller.updateMailbox(account, mailbox, mControllerCallback);
} else {
controller.updateMailboxList(account, mControllerCallback);
}
}
private void onAccounts() {
AccountFolderList.actionShowAccounts(this);
finish();
}
private void onEditAccount() {
AccountSettings.actionSettings(this, mAccountId);
}
private void onOpenMailbox(long mailboxId) {
MessageList.actionHandleAccount(this, mailboxId, null, null);
}
private void onCompose() {
MessageCompose.actionCompose(this, mAccountId);
}
/**
* Async task for loading the mailboxes for a given account
*/
private class LoadMailboxesTask extends AsyncTask<Void, Void, Cursor> {
private long mAccountKey;
/**
* Special constructor to cache some local info
*/
public LoadMailboxesTask(long accountId) {
mAccountKey = accountId;
}
@Override
protected Cursor doInBackground(Void... params) {
return MailboxList.this.managedQuery(
EmailContent.Mailbox.CONTENT_URI,
MailboxList.this.mListAdapter.PROJECTION,
MailboxColumns.ACCOUNT_KEY + "=?"
+ " AND " + MailboxColumns.TYPE + "<" + Mailbox.TYPE_NOT_EMAIL,
new String[] { String.valueOf(mAccountKey) },
MailboxColumns.TYPE);
}
@Override
protected void onPostExecute(Cursor cursor) {
MailboxList.this.mListAdapter.changeCursor(cursor);
}
}
/**
* Handler for UI-thread operations (when called from callbacks or any other threads)
*/
class MailboxListHandler extends Handler {
private static final int MSG_PROGRESS = 1;
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MSG_PROGRESS:
boolean showProgress = (msg.arg1 != 0);
if (showProgress) {
mRefreshButton.setVisibility(View.GONE);
mProgress.setVisibility(View.VISIBLE);
} else {
mRefreshButton.setVisibility(View.VISIBLE);
mProgress.setVisibility(View.GONE);
}
break;
default:
super.handleMessage(msg);
}
}
/**
* Call from any thread to start/stop progress indicator(s)
* @param progress true to start, false to stop
*/
public void progress(boolean progress) {
android.os.Message msg = android.os.Message.obtain();
msg.what = MSG_PROGRESS;
msg.arg1 = progress ? 1 : 0;
sendMessage(msg);
}
}
/**
* Callback for async Controller results.
*/
private class ControllerResults implements Controller.Result {
public void updateMailboxListCallback(MessagingException result, long accountKey) {
if (accountKey == mAccountId) {
mHandler.progress(false);
}
}
public void updateMailboxCallback(MessagingException result, long accountKey,
long mailboxKey, int totalMessagesInMailbox, int numNewMessages) {
if (accountKey == mAccountId) {
mHandler.progress(false);
}
}
public void loadAttachmentCallback(MessagingException result, long messageId,
long attachmentId, int progress) {
}
}
/**
* The adapter for displaying mailboxes.
*/
/* package */ class MailboxListAdapter extends CursorAdapter {
public final String[] PROJECTION = new String[] { MailboxColumns.ID,
MailboxColumns.DISPLAY_NAME, MailboxColumns.UNREAD_COUNT, MailboxColumns.TYPE };
private final int COLUMN_DISPLAY_NAME = 1;
private final int COLUMN_UNREAD_COUNT = 2;
Context mContext;
private LayoutInflater mInflater;
public MailboxListAdapter(Context context) {
super(context, null);
mContext = context;
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Resources resources = context.getResources();
// TODO get mailbox typename strings here
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
// TODO translation by mailbox type
String text = cursor.getString(COLUMN_DISPLAY_NAME);
if (text != null) {
TextView nameView = (TextView) view.findViewById(R.id.mailbox_name);
nameView.setText(text);
}
// TODO get/track live folder status
text = null;
TextView statusView = (TextView) view.findViewById(R.id.mailbox_status);
if (text != null) {
statusView.setText(text);
statusView.setVisibility(View.VISIBLE);
} else {
statusView.setVisibility(View.GONE);
}
// TODO do we use a different count for special mailboxes (total count vs. unread)
int count = -1;
text = cursor.getString(COLUMN_UNREAD_COUNT);
if (text != null) {
count = Integer.valueOf(text);
}
TextView countView = (TextView) view.findViewById(R.id.new_message_count);
// If the unread count is zero, not to show countView.
if (count > 0) {
countView.setVisibility(View.VISIBLE);
countView.setText(text);
} else {
countView.setVisibility(View.GONE);
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(R.layout.mailbox_list_item, parent, false);
}
}
}