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 */ 16 package com.android.mail.ui; 17 18 import com.android.mail.R; 19 import com.android.mail.analytics.Analytics; 20 import com.android.mail.providers.Account; 21 import com.android.mail.providers.MailAppProvider; 22 import com.android.mail.providers.UIProvider; 23 import com.android.mail.utils.LogTag; 24 25 import java.util.ArrayList; 26 27 import android.app.ActionBar; 28 import android.app.Fragment; 29 import android.app.FragmentTransaction; 30 import android.app.ListActivity; 31 import android.app.LoaderManager; 32 import android.appwidget.AppWidgetManager; 33 import android.content.ContentResolver; 34 import android.content.CursorLoader; 35 import android.content.Intent; 36 import android.content.Loader; 37 import android.database.Cursor; 38 import android.os.AsyncTask; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.view.View; 42 import android.view.View.OnClickListener; 43 import android.view.ViewGroup; 44 import android.widget.Button; 45 import android.widget.ListView; 46 import android.widget.SimpleCursorAdapter; 47 import 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 */ 53 public 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(new Account(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 accountView.setText(new Account((Cursor) getItem(position)).name); 263 return v; 264 } 265 }; 266 setListAdapter(mAdapter); 267 } 268 } 269 270 @Override 271 protected void onListItemClick(ListView l, View v, int position, long id) { 272 selectAccount(new Account((Cursor)mAdapter.getItem(position))); 273 } 274 275 private void selectAccount(Account account) { 276 if (mCreateShortcut || mConfigureWidget) { 277 // Invoked for a shortcut creation 278 final Intent intent = new Intent(this, getFolderSelectionActivity()); 279 intent.setFlags( 280 Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_FORWARD_RESULT); 281 intent.setAction(mCreateShortcut ? 282 Intent.ACTION_CREATE_SHORTCUT : AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); 283 if (mConfigureWidget) { 284 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); 285 } 286 intent.putExtra(FolderSelectionActivity.EXTRA_ACCOUNT_SHORTCUT, account); 287 startActivity(intent); 288 finish(); 289 } else { 290 // TODO: (mindyp) handle changing the account for this shortcut. 291 finish(); 292 } 293 } 294 295 /** 296 * Return the class responsible for launching the folder selection activity. 297 */ 298 protected Class<?> getFolderSelectionActivity() { 299 return FolderSelectionActivity.class; 300 } 301 302 @Override 303 public void onClick(View v) { 304 final int id = v.getId(); 305 if (id == R.id.first_button) { 306 setResult(RESULT_CANCELED); 307 finish(); 308 } 309 } 310 311 @Override 312 protected final void onActivityResult(int request, int result, Intent data) { 313 if (request == RESULT_CREATE_ACCOUNT) { 314 // We were waiting for the user to create an account 315 if (result != RESULT_OK) { 316 finish(); 317 } else { 318 // Watch for accounts to show up! 319 // restart the loader to get the updated list of accounts 320 getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, null, this); 321 showWaitFragment(null); 322 } 323 } 324 } 325 326 private void showWaitFragment(Account account) { 327 WaitFragment fragment = getWaitFragment(); 328 if (fragment != null) { 329 fragment.updateAccount(account); 330 } else { 331 mWait.setVisibility(View.VISIBLE); 332 replaceFragment(WaitFragment.newInstance(account, true), 333 FragmentTransaction.TRANSIT_FRAGMENT_OPEN, TAG_WAIT); 334 } 335 mContent.setVisibility(View.GONE); 336 } 337 338 @Override 339 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 340 switch (id) { 341 case LOADER_ACCOUNT_CURSOR: 342 return new CursorLoader(this, MailAppProvider.getAccountsUri(), 343 UIProvider.ACCOUNTS_PROJECTION, null, null, null); 344 } 345 return null; 346 } 347 348 private WaitFragment getWaitFragment() { 349 return (WaitFragment) getFragmentManager().findFragmentByTag(TAG_WAIT); 350 } 351 352 private int replaceFragment(Fragment fragment, int transition, String tag) { 353 FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); 354 fragmentTransaction.setTransition(transition); 355 fragmentTransaction.replace(R.id.wait, fragment, tag); 356 final int transactionId = fragmentTransaction.commitAllowingStateLoss(); 357 return transactionId; 358 } 359 360 @Override 361 public void onLoaderReset(Loader<Cursor> arg0) { 362 // Do nothing. 363 } 364 365 @Override 366 public void onLoadFinished(Loader<Cursor> cursor, Cursor data) { 367 if (data != null && data.moveToFirst()) { 368 // there are accounts now! 369 Account account; 370 ArrayList<Account> accounts = new ArrayList<Account>(); 371 ArrayList<Account> initializedAccounts = new ArrayList<Account>(); 372 do { 373 account = new Account(data); 374 if (account.isAccountReady()) { 375 initializedAccounts.add(account); 376 } 377 accounts.add(account); 378 } while (data.moveToNext()); 379 if (initializedAccounts.size() > 0) { 380 mWait.setVisibility(View.GONE); 381 getLoaderManager().destroyLoader(LOADER_ACCOUNT_CURSOR); 382 mContent.setVisibility(View.VISIBLE); 383 updateAccountList(data); 384 } else { 385 // Show "waiting" 386 account = accounts.size() > 0 ? accounts.get(0) : null; 387 showWaitFragment(account); 388 } 389 } 390 } 391 392 @Override 393 public void onBackPressed() { 394 mWaitingForAddAccountResult = false; 395 // If we are showing the wait fragment, just exit. 396 if (getWaitFragment() != null) { 397 finish(); 398 } else { 399 super.onBackPressed(); 400 } 401 } 402 } 403