AccountSelectorAdapter.java revision 2ac1eaf8c3f7122da7900f0ea5a1198264631d74
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.email.activity;
18
19import com.android.email.R;
20import com.android.email.data.ThrottlingCursorLoader;
21import com.android.email.provider.EmailContent;
22import com.android.email.provider.EmailContent.Account;
23import com.android.email.provider.EmailContent.Mailbox;
24
25import android.content.Context;
26import android.content.Loader;
27import android.database.Cursor;
28import android.database.MatrixCursor;
29import android.database.MatrixCursor.RowBuilder;
30import android.view.LayoutInflater;
31import android.view.View;
32import android.view.ViewGroup;
33import android.widget.CursorAdapter;
34import android.widget.TextView;
35
36/**
37 * Adapter for the account selector on {@link MessageListXL}.
38 *
39 * TODO Test it!
40 * TODO Use layout?  Or use the standard resources that ActionBarDemo uses?
41 * TODO Revisit the sort order when we get more detailed UI spec.  (current sort order makes things
42 * simpler for now.)  Maybe we can just use SimpleCursorAdapter.
43 */
44public class AccountSelectorAdapter extends CursorAdapter {
45    /** Projection used to query from Account */
46    private static final String[] ACCOUNT_PROJECTION = new String[] {
47        EmailContent.RECORD_ID,
48        EmailContent.Account.DISPLAY_NAME,
49        EmailContent.Account.EMAIL_ADDRESS,
50    };
51
52    /**
53     * Projection for the resulting MatrixCursor -- must be {@link #ACCOUNT_PROJECTION}
54     * with "UNREAD_COUNT".
55     */
56    private static final String[] RESULT_PROJECTION = new String[] {
57        EmailContent.RECORD_ID,
58        EmailContent.Account.DISPLAY_NAME,
59        EmailContent.Account.EMAIL_ADDRESS,
60        "UNREAD_COUNT"
61    };
62
63    private static final int ID_COLUMN = 0;
64    private static final int DISPLAY_NAME_COLUMN = 1;
65    private static final int EMAIL_ADDRESS_COLUMN = 2;
66    private static final int UNREAD_COUNT_COLUMN = 3;
67
68    /** Sort order.  Show the default account first. */
69    private static final String ORDER_BY =
70            EmailContent.Account.IS_DEFAULT + " desc, " + EmailContent.Account.RECORD_ID;
71
72    private final LayoutInflater mInflater;
73
74    public static Loader<Cursor> createLoader(Context context) {
75        return new AccountsLoader(context);
76    }
77
78    public AccountSelectorAdapter(Context context, Cursor c) {
79        super(context, c, 0 /* no auto-requery */);
80        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
81    }
82
83    @Override
84    public View getDropDownView(int position, View convertView, ViewGroup parent) {
85        final View view = mInflater.inflate(R.layout.account_selector_dropdown, null);
86
87        final TextView displayNameView = (TextView) view.findViewById(R.id.display_name);
88        final TextView emailAddressView = (TextView) view.findViewById(R.id.email_address);
89        final TextView unreadCountView = (TextView) view.findViewById(R.id.unread_count);
90
91        final String displayName = getAccountDisplayName(position);
92        final String emailAddress = getAccountEmailAddress(position);
93
94        displayNameView.setText(displayName);
95
96        // Show the email address only when it's different from the display name.
97        // If same, show " " instead of "", so that the text view won't get completely
98        // collapsed. (TextView's height will be 0px if it's "match_content" and the
99        // content is "".)
100        emailAddressView.setText(emailAddress.equals(displayName) ? " " : emailAddress);
101        unreadCountView.setText(Integer.toString(getAccountUnreadCount(position)));
102        return view;
103    }
104
105    @Override
106    public void bindView(View view, Context context, Cursor cursor) {
107        TextView textView = (TextView) view.findViewById(R.id.display_name);
108        textView.setText(getAccountDisplayName(cursor));
109    }
110
111    @Override
112    public View newView(Context context, Cursor cursor, ViewGroup parent) {
113        return mInflater.inflate(R.layout.account_selector, null);
114    }
115
116    /** @return Account id extracted from a Cursor. */
117    public static long getAccountId(Cursor c) {
118        return c.getLong(ID_COLUMN);
119    }
120
121    private String getAccountDisplayName(int position) {
122        final Cursor c = getCursor();
123        return c.moveToPosition(position) ? getAccountDisplayName(c) : null;
124    }
125
126    private String getAccountEmailAddress(int position) {
127        final Cursor c = getCursor();
128        return c.moveToPosition(position) ? getAccountEmailAddress(c) : null;
129    }
130
131    private int getAccountUnreadCount(int position) {
132        final Cursor c = getCursor();
133        return c.moveToPosition(position) ? getAccountUnreadCount(c) : 0;
134    }
135
136    /** @return Account name extracted from a Cursor. */
137    public static String getAccountDisplayName(Cursor cursor) {
138        return cursor.getString(DISPLAY_NAME_COLUMN);
139    }
140
141    /** @return Email address extracted from a Cursor. */
142    public static String getAccountEmailAddress(Cursor cursor) {
143        return cursor.getString(EMAIL_ADDRESS_COLUMN);
144    }
145
146    /** @return Unread count extracted from a Cursor. */
147    public static int getAccountUnreadCount(Cursor cursor) {
148        return cursor.getInt(UNREAD_COUNT_COLUMN);
149    }
150
151    /**
152     * Load the account list.  The resulting cursor contains
153     * - Account info
154     * - # of unread messages in inbox
155     * - The "Combined view" row if there's more than one account.
156     */
157    private static class AccountsLoader extends ThrottlingCursorLoader {
158        private final Context mContext;
159
160        public AccountsLoader(Context context) {
161            // Super class loads a regular account cursor, but we replace it in loadInBackground().
162            super(context, EmailContent.Account.CONTENT_URI, ACCOUNT_PROJECTION, null, null,
163                    ORDER_BY);
164            mContext = context;
165        }
166
167        @Override
168        public Cursor loadInBackground() {
169            // Fetch account list
170            final Cursor accountsCursor = super.loadInBackground();
171
172            // Cursor that's actually returned.
173            // Use ClosingMatrixCursor so that accountsCursor gets closed too when it's closed.
174            final MatrixCursor resultCursor = new ClosingMatrixCursor(RESULT_PROJECTION,
175                    accountsCursor);
176            accountsCursor.moveToPosition(-1);
177
178            // Build the cursor...
179            int totalUnread = 0;
180            while (accountsCursor.moveToNext()) {
181                // Add account, with its unread count.
182                final long accountId = accountsCursor.getLong(0);
183                final int unread = Mailbox.getUnreadCountByAccountAndMailboxType(
184                        mContext, accountId, Mailbox.TYPE_INBOX);
185
186                RowBuilder rb = resultCursor.newRow();
187                rb.add(accountId);
188                rb.add(getAccountDisplayName(accountsCursor));
189                rb.add(getAccountEmailAddress(accountsCursor));
190                rb.add(unread);
191                totalUnread += unread;
192            }
193            // Add "combined view"
194            final int countAccounts = resultCursor.getCount();
195            if (countAccounts > 0) {
196                RowBuilder rb = resultCursor.newRow();
197
198                // Add ID, display name, # of accounts, total unread count.
199                rb.add(Account.ACCOUNT_ID_COMBINED_VIEW);
200                rb.add(mContext.getResources().getString(
201                        R.string.mailbox_list_account_selector_combined_view));
202                rb.add(mContext.getResources().getQuantityString(R.plurals.number_of_accounts,
203                        countAccounts, countAccounts));
204                rb.add(totalUnread);
205            }
206            return resultCursor;
207        }
208    }
209
210    /**
211     * {@link MatrixCursor} which takes an extra {@link Cursor} to the constructor, and close
212     * it when self is closed.
213     */
214    private static class ClosingMatrixCursor extends MatrixCursor {
215        private final Cursor mInnerCursor;
216
217        public ClosingMatrixCursor(String[] columnNames, Cursor innerCursor) {
218            super(columnNames);
219            mInnerCursor = innerCursor;
220        }
221
222        @Override
223        public void close() {
224            mInnerCursor.close();
225            super.close();
226        }
227    }
228}
229