MailboxSelectionActivity.java revision 4b0c0127d24e387a4f49d442b63b5c60cedb6922
1/*
2 * Copyright (C) 2012 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 */
16package com.android.mail.ui;
17
18import com.android.mail.R;
19import com.android.mail.analytics.Analytics;
20import com.android.mail.providers.Account;
21import com.android.mail.providers.MailAppProvider;
22import com.android.mail.providers.UIProvider;
23import com.android.mail.utils.LogTag;
24
25import java.util.ArrayList;
26
27import android.app.ActionBar;
28import android.app.Fragment;
29import android.app.FragmentTransaction;
30import android.app.ListActivity;
31import android.app.LoaderManager;
32import android.appwidget.AppWidgetManager;
33import android.content.ContentResolver;
34import android.content.CursorLoader;
35import android.content.Intent;
36import android.content.Loader;
37import android.database.Cursor;
38import android.os.AsyncTask;
39import android.os.Bundle;
40import android.os.Handler;
41import android.view.View;
42import android.view.View.OnClickListener;
43import android.view.ViewGroup;
44import android.widget.Button;
45import android.widget.ListView;
46import android.widget.SimpleCursorAdapter;
47import android.widget.TextView;
48
49/**
50 * An activity that shows the list of all the available accounts and return the
51 * one selected in onResult().
52 */
53public class MailboxSelectionActivity extends ListActivity implements OnClickListener,
54        LoaderManager.LoaderCallbacks<Cursor> {
55
56    // Used to save our instance state
57    private static final String CREATE_SHORTCUT_KEY = "createShortcut";
58    private static final String CREATE_WIDGET_KEY = "createWidget";
59    private static final String WIDGET_ID_KEY = "widgetId";
60    private static final String WAITING_FOR_ADD_ACCOUNT_RESULT_KEY = "waitingForAddAccountResult";
61
62    private static final String ACCOUNT = "name";
63    private static final String[] COLUMN_NAMES = { ACCOUNT };
64    protected static final String LOG_TAG = LogTag.getLogTag();
65    private static final int RESULT_CREATE_ACCOUNT = 2;
66    private static final int LOADER_ACCOUNT_CURSOR = 0;
67    private static final String TAG_WAIT = "wait-fragment";
68    private final int[] VIEW_IDS = { R.id.mailbox_name };
69    private boolean mCreateShortcut = false;
70    private boolean mConfigureWidget = false;
71    private SimpleCursorAdapter mAdapter;
72    private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
73
74    // Boolean to indicate that we are waiting for the result from an add account
75    // operation.  This boolean is necessary, as there is no guarantee on whether the
76    // AccountManager callback or onResume will be called first.
77    boolean mWaitingForAddAccountResult = false;
78
79    // Can only do certain actions if the Activity is resumed (e.g. setVisible)
80    private boolean mResumed = false;
81    private Handler mHandler = new Handler();
82    private View mContent;
83    private View mWait;
84
85    @Override
86    public void onCreate(Bundle icicle) {
87        super.onCreate(icicle);
88        setContentView(R.layout.mailbox_selection_activity);
89        mContent = findViewById(R.id.content);
90        mWait = findViewById(R.id.wait);
91        if (icicle != null) {
92            restoreState(icicle);
93        } else {
94            if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
95                mCreateShortcut = true;
96            }
97            mAppWidgetId = getIntent().getIntExtra(
98                    AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
99            if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
100                mConfigureWidget = true;
101            }
102        }
103        // We set the default title to "Gmail" or "Google Mail" for consistency
104        // in Task Switcher. If this is for create shortcut or configure widget,
105        // we should set the title to "Select account".
106        if (mCreateShortcut || mConfigureWidget) {
107            setTitle(getResources().getString(R.string.activity_mailbox_selection));
108            ActionBar actionBar = getActionBar();
109            if (actionBar != null) {
110                actionBar.setIcon(R.mipmap.ic_launcher_shortcut_folder);
111            }
112        }
113        ((Button) findViewById(R.id.first_button)).setOnClickListener(this);
114
115        // Initially, assume that the main view is invisible.  It will be made visible,
116        // if we display the account list
117        setVisible(false);
118        setResult(RESULT_CANCELED);
119    }
120
121    @Override
122    protected void onSaveInstanceState(Bundle icicle) {
123        super.onSaveInstanceState(icicle);
124
125        icicle.putBoolean(CREATE_SHORTCUT_KEY, mCreateShortcut);
126        icicle.putBoolean(CREATE_WIDGET_KEY, mConfigureWidget);
127        if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
128            icicle.putInt(WIDGET_ID_KEY, mAppWidgetId);
129        }
130        icicle.putBoolean(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY, mWaitingForAddAccountResult);
131    }
132
133    @Override
134    public void onStart() {
135        super.onStart();
136
137        Analytics.getInstance().activityStart(this);
138    }
139
140    @Override
141    protected void onStop() {
142        super.onStop();
143
144        Analytics.getInstance().activityStop(this);
145    }
146
147    @Override
148    public void onResume() {
149        super.onResume();
150        mResumed = true;
151        // Only fetch the accounts, if we are not handling a response from the
152        // launched child activity.
153        if (!mWaitingForAddAccountResult) {
154            setupWithAccounts();
155        }
156    }
157
158    @Override
159    public void onPause() {
160        super.onPause();
161        mResumed = false;
162    }
163
164    @Override
165    public void onNewIntent(Intent intent) {
166        super.onNewIntent(intent);
167        setIntent(intent);
168    }
169
170    /**
171     * Restores the activity state from a bundle
172     */
173    private void restoreState(Bundle icicle) {
174        if (icicle.containsKey(CREATE_SHORTCUT_KEY)) {
175            mCreateShortcut = icicle.getBoolean(CREATE_SHORTCUT_KEY);
176        }
177        if (icicle.containsKey(CREATE_WIDGET_KEY)) {
178            mConfigureWidget = icicle.getBoolean(CREATE_WIDGET_KEY);
179        }
180        if (icicle.containsKey(WIDGET_ID_KEY)) {
181            mAppWidgetId = icicle.getInt(WIDGET_ID_KEY);
182        }
183        if (icicle.containsKey(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY)) {
184            mWaitingForAddAccountResult = icicle.getBoolean(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY);
185        }
186    }
187
188    private void setupWithAccounts() {
189        final ContentResolver resolver = getContentResolver();
190        new AsyncTask<Void, Void, Void>() {
191            @Override
192            protected Void doInBackground(Void... params) {
193                Cursor cursor = null;
194                try {
195                    cursor = resolver.query(MailAppProvider.getAccountsUri(),
196                            UIProvider.ACCOUNTS_PROJECTION, null, null, null);
197                    completeSetupWithAccounts(cursor);
198                } finally {
199                    if (cursor != null) {
200                        cursor.close();
201                    }
202                }
203                return null;
204            }
205
206        }.execute();
207    }
208
209    private void completeSetupWithAccounts(final Cursor accounts) {
210        mHandler.post(new Runnable() {
211            @Override
212            public void run() {
213                updateAccountList(accounts);
214            }
215        });
216    }
217
218    private void updateAccountList(final Cursor accounts) {
219        boolean displayAccountList = true;
220        // Configuring a widget or shortcut.
221        if (mConfigureWidget || mCreateShortcut) {
222            if (accounts == null || accounts.getCount() == 0) {
223                // No account found, show Add Account screen, for both the widget or
224                // shortcut creation process
225                // No account found, show Add Account screen, for both the widget or
226                // shortcut creation process
227                final Intent noAccountIntent = MailAppProvider.getNoAccountIntent(this);
228                if (noAccountIntent != null) {
229                    startActivityForResult(noAccountIntent, RESULT_CREATE_ACCOUNT);
230                }
231                // No reason to display the account list
232                displayAccountList = false;
233
234                // Indicate that we need to handle the response from the add account action
235                // This allows us to process the results that we get in the AddAccountCallback
236                mWaitingForAddAccountResult = true;
237            } else if (mConfigureWidget && accounts.getCount() == 1) {
238                mWait.setVisibility(View.GONE);
239                // When configuring a widget, if there is only one account, automatically
240                // choose that account.
241                accounts.moveToFirst();
242                selectAccount(Account.builder().buildFrom(accounts));
243                // No reason to display the account list
244                displayAccountList = false;
245            }
246        }
247
248        if (displayAccountList) {
249            mContent.setVisibility(View.VISIBLE);
250            // We are about to display the list, make this activity visible
251            // But only if the Activity is not paused!
252            if (mResumed) {
253                setVisible(true);
254            }
255
256            mAdapter = new SimpleCursorAdapter(this, R.layout.mailbox_item, accounts,
257                    COLUMN_NAMES, VIEW_IDS, 0) {
258                @Override
259                public View getView(int position, View convertView, ViewGroup parent) {
260                    View v = super.getView(position, convertView, parent);
261                    TextView accountView = (TextView) v.findViewById(R.id.mailbox_name);
262                    final Account account = Account.builder().buildFrom((Cursor) getItem(position));
263                    accountView.setText(account.getDisplayName());
264                    return v;
265                }
266            };
267            setListAdapter(mAdapter);
268        }
269    }
270
271    @Override
272    protected void onListItemClick(ListView l, View v, int position, long id) {
273        selectAccount(Account.builder().buildFrom((Cursor)mAdapter.getItem(position)));
274    }
275
276    private void selectAccount(Account account) {
277        if (mCreateShortcut || mConfigureWidget) {
278            // Invoked for a shortcut creation
279            final Intent intent = new Intent(this, getFolderSelectionActivity());
280            intent.setFlags(
281                    Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
282            intent.setAction(mCreateShortcut ?
283                    Intent.ACTION_CREATE_SHORTCUT : AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
284            if (mConfigureWidget) {
285                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
286            }
287            intent.putExtra(FolderSelectionActivity.EXTRA_ACCOUNT_SHORTCUT, account);
288            startActivity(intent);
289            finish();
290        } else {
291            // TODO: (mindyp) handle changing the account for this shortcut.
292            finish();
293        }
294    }
295
296    /**
297     * Return the class responsible for launching the folder selection activity.
298     */
299    protected Class<?> getFolderSelectionActivity() {
300        return FolderSelectionActivity.class;
301    }
302
303    @Override
304    public void onClick(View v) {
305        final int id = v.getId();
306        if (id == R.id.first_button) {
307            setResult(RESULT_CANCELED);
308            finish();
309        }
310    }
311
312    @Override
313    protected final void onActivityResult(int request, int result, Intent data) {
314        if (request == RESULT_CREATE_ACCOUNT) {
315            // We were waiting for the user to create an account
316            if (result != RESULT_OK) {
317                finish();
318            } else {
319                // Watch for accounts to show up!
320                // restart the loader to get the updated list of accounts
321                getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, null, this);
322                showWaitFragment(null);
323            }
324        }
325    }
326
327    private void showWaitFragment(Account account) {
328        WaitFragment fragment = getWaitFragment();
329        if (fragment != null) {
330            fragment.updateAccount(account);
331        } else {
332            mWait.setVisibility(View.VISIBLE);
333            replaceFragment(WaitFragment.newInstance(account, false /* expectingMessages */),
334                    FragmentTransaction.TRANSIT_FRAGMENT_OPEN, TAG_WAIT);
335        }
336        mContent.setVisibility(View.GONE);
337    }
338
339    @Override
340    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
341        switch (id) {
342            case LOADER_ACCOUNT_CURSOR:
343                return new CursorLoader(this, MailAppProvider.getAccountsUri(),
344                        UIProvider.ACCOUNTS_PROJECTION, null, null, null);
345        }
346        return null;
347    }
348
349    private WaitFragment getWaitFragment() {
350        return (WaitFragment) getFragmentManager().findFragmentByTag(TAG_WAIT);
351    }
352
353    private int replaceFragment(Fragment fragment, int transition, String tag) {
354        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
355        fragmentTransaction.setTransition(transition);
356        fragmentTransaction.replace(R.id.wait, fragment, tag);
357        final int transactionId = fragmentTransaction.commitAllowingStateLoss();
358        return transactionId;
359    }
360
361    @Override
362    public void onLoaderReset(Loader<Cursor> arg0) {
363        // Do nothing.
364    }
365
366    @Override
367    public void onLoadFinished(Loader<Cursor> cursor, Cursor data) {
368        if (data != null && data.moveToFirst()) {
369            // there are accounts now!
370            Account account;
371            ArrayList<Account> accounts = new ArrayList<Account>();
372            ArrayList<Account> initializedAccounts = new ArrayList<Account>();
373            do {
374                account = Account.builder().buildFrom(data);
375                if (account.isAccountReady()) {
376                    initializedAccounts.add(account);
377                }
378                accounts.add(account);
379            } while (data.moveToNext());
380            if (initializedAccounts.size() > 0) {
381                mWait.setVisibility(View.GONE);
382                getLoaderManager().destroyLoader(LOADER_ACCOUNT_CURSOR);
383                mContent.setVisibility(View.VISIBLE);
384                updateAccountList(data);
385            } else {
386                // Show "waiting"
387                account = accounts.size() > 0 ? accounts.get(0) : null;
388                showWaitFragment(account);
389            }
390        }
391    }
392
393    @Override
394    public void onBackPressed() {
395        mWaitingForAddAccountResult = false;
396        // If we are showing the wait fragment, just exit.
397        if (getWaitFragment() != null) {
398            finish();
399        } else {
400            super.onBackPressed();
401        }
402    }
403}
404