DialtactsActivity.java revision c15062754af08cadc50b4b8ec89c2175a4bec1fe
1/* 2 * Copyright (C) 2008 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.contacts.activities; 18 19import com.android.contacts.R; 20import com.android.contacts.calllog.CallLogFragment; 21import com.android.contacts.dialpad.DialpadFragment; 22import com.android.contacts.interactions.ImportExportInteraction; 23import com.android.contacts.list.ContactListFilter; 24import com.android.contacts.list.ContactsIntentResolver; 25import com.android.contacts.list.ContactsRequest; 26import com.android.contacts.list.DefaultContactBrowseListFragment; 27import com.android.contacts.list.DirectoryListLoader; 28import com.android.contacts.list.OnContactBrowserActionListener; 29import com.android.contacts.preference.ContactsPreferenceActivity; 30import com.android.internal.telephony.ITelephony; 31 32import android.app.ActionBar; 33import android.app.ActionBar.Tab; 34import android.app.ActionBar.TabListener; 35import android.app.Activity; 36import android.app.Dialog; 37import android.app.Fragment; 38import android.app.FragmentManager; 39import android.app.FragmentTransaction; 40import android.content.Intent; 41import android.content.SharedPreferences; 42import android.net.Uri; 43import android.os.Bundle; 44import android.os.RemoteException; 45import android.os.ServiceManager; 46import android.provider.CallLog.Calls; 47import android.provider.ContactsContract; 48import android.provider.ContactsContract.Contacts; 49import android.provider.Settings; 50import android.provider.ContactsContract.Intents.UI; 51import android.util.Log; 52import android.view.Menu; 53import android.view.MenuInflater; 54import android.view.MenuItem; 55 56/** 57 * The dialer activity that has one tab with the virtual 12key 58 * dialer, a tab with recent calls in it, a tab with the contacts and 59 * a tab with the favorite. This is the container and the tabs are 60 * embedded using intents. 61 * The dialer tab's title is 'phone', a more common name (see strings.xml). 62 */ 63public class DialtactsActivity extends Activity { 64 private static final String TAG = "DialtactsActivity"; 65 66 private static final int TAB_INDEX_DIALER = 0; 67 private static final int TAB_INDEX_CALL_LOG = 1; 68 private static final int TAB_INDEX_CONTACTS = 2; 69 private static final int TAB_INDEX_FAVORITES = 3; 70 71 public static final String EXTRA_IGNORE_STATE = "ignore-state"; 72 73 /** Name of the dialtacts shared preferences */ 74 static final String PREFS_DIALTACTS = "dialtacts"; 75 /** If true, when handling the contacts intent the favorites tab will be shown instead */ 76 static final String PREF_FAVORITES_AS_CONTACTS = "favorites_as_contacts"; 77 static final boolean PREF_FAVORITES_AS_CONTACTS_DEFAULT = false; 78 79 /** Last manually selected tab index */ 80 private static final String PREF_LAST_MANUALLY_SELECTED_TAB = "last_manually_selected_tab"; 81 private static final int PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT = TAB_INDEX_DIALER; 82 83 private String mFilterText; 84 private Uri mDialUri; 85 private DialpadFragment mDialpadFragment; 86 private CallLogFragment mCallLogFragment; 87 private DefaultContactBrowseListFragment mContactsFragment; 88 private DefaultContactBrowseListFragment mFavoritesFragment; 89 private ImportExportInteraction mImportExportInteraction; 90 91 /** 92 * The index of the tab that has last been manually selected (the user clicked on a tab). 93 * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call) 94 */ 95 private int mLastManuallySelectedTab; 96 97 @Override 98 protected void onCreate(Bundle icicle) { 99 super.onCreate(icicle); 100 101 final Intent intent = getIntent(); 102 fixIntent(intent); 103 104 setContentView(R.layout.dialtacts_activity); 105 106 final FragmentManager fragmentManager = getFragmentManager(); 107 mDialpadFragment = (DialpadFragment) fragmentManager 108 .findFragmentById(R.id.dialpad_fragment); 109 mCallLogFragment = (CallLogFragment) fragmentManager 110 .findFragmentById(R.id.call_log_fragment); 111 mContactsFragment = (DefaultContactBrowseListFragment) fragmentManager 112 .findFragmentById(R.id.contacts_fragment); 113 mFavoritesFragment = (DefaultContactBrowseListFragment) fragmentManager 114 .findFragmentById(R.id.favorites_fragment); 115 116 // Hide all tabs (the current tab will later be reshown once a tab is selected) 117 final FragmentTransaction transaction = fragmentManager.beginTransaction(); 118 transaction.hide(mDialpadFragment); 119 transaction.hide(mCallLogFragment); 120 transaction.hide(mContactsFragment); 121 transaction.hide(mFavoritesFragment); 122 transaction.commit(); 123 124 // Setup the ActionBar tabs (the order matches the tab-index contants TAB_INDEX_*) 125 setupDialer(); 126 setupCallLog(); 127 setupContacts(); 128 setupFavorites(); 129 getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 130 getActionBar().setDisplayShowTitleEnabled(false); 131 getActionBar().setDisplayShowHomeEnabled(false); 132 133 // Load the last manually loaded tab 134 final SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE); 135 mLastManuallySelectedTab = prefs.getInt(PREF_LAST_MANUALLY_SELECTED_TAB, 136 PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT); 137 138 setCurrentTab(intent); 139 140 if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction()) 141 && icicle == null) { 142 setupFilterText(intent); 143 } 144 } 145 146 @Override 147 protected void onPause() { 148 super.onPause(); 149 150 final int currentTabIndex = getActionBar().getSelectedTab().getPosition(); 151 final SharedPreferences.Editor editor = 152 getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE).edit(); 153 if (currentTabIndex == TAB_INDEX_CONTACTS || currentTabIndex == TAB_INDEX_FAVORITES) { 154 editor.putBoolean(PREF_FAVORITES_AS_CONTACTS, currentTabIndex == TAB_INDEX_FAVORITES); 155 } 156 editor.putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedTab); 157 158 editor.apply(); 159 } 160 161 private void fixIntent(Intent intent) { 162 // This should be cleaned up: the call key used to send an Intent 163 // that just said to go to the recent calls list. It now sends this 164 // abstract action, but this class hasn't been rewritten to deal with it. 165 if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) { 166 intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE); 167 intent.putExtra("call_key", true); 168 setIntent(intent); 169 } 170 } 171 172 private void setupDialer() { 173 final Tab tab = getActionBar().newTab(); 174 tab.setText(R.string.dialerIconLabel); 175 tab.setTabListener(new TabChangeListener(mDialpadFragment)); 176 tab.setIcon(R.drawable.ic_tab_dialer); 177 getActionBar().addTab(tab); 178 mDialpadFragment.resolveIntent(); 179 } 180 181 private void setupCallLog() { 182 final Tab tab = getActionBar().newTab(); 183 tab.setText(R.string.recentCallsIconLabel); 184 tab.setIcon(R.drawable.ic_tab_recent); 185 tab.setTabListener(new TabChangeListener(mCallLogFragment)); 186 getActionBar().addTab(tab); 187 } 188 189 private void setupContacts() { 190 final Tab tab = getActionBar().newTab(); 191 tab.setText(R.string.contactsIconLabel); 192 tab.setIcon(R.drawable.ic_tab_contacts); 193 tab.setTabListener(new TabChangeListener(mContactsFragment)); 194 getActionBar().addTab(tab); 195 196 // TODO: We should not artificially create Intents and put them into the Fragment. 197 // It would be nicer to directly pass in the UI constant 198 Intent intent = new Intent(UI.LIST_ALL_CONTACTS_ACTION); 199 intent.setClass(this, ContactBrowserActivity.class); 200 201 ContactsIntentResolver resolver = new ContactsIntentResolver(this); 202 ContactsRequest request = resolver.resolveIntent(intent); 203 final ContactListFilter filter = new ContactListFilter( 204 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS); 205 mContactsFragment.setFilter(filter, false); 206 mContactsFragment.setSearchMode(request.isSearchMode()); 207 mContactsFragment.setQueryString(request.getQueryString(), false); 208 mContactsFragment.setContactsRequest(request); 209 mContactsFragment.setDirectorySearchMode(request.isDirectorySearchEnabled() 210 ? DirectoryListLoader.SEARCH_MODE_DEFAULT 211 : DirectoryListLoader.SEARCH_MODE_NONE); 212 mContactsFragment.setOnContactListActionListener(mListFragmentListener); 213 } 214 215 private void setupFavorites() { 216 final Tab tab = getActionBar().newTab(); 217 tab.setText(R.string.contactsFavoritesLabel); 218 tab.setIcon(R.drawable.ic_tab_starred); 219 tab.setTabListener(new TabChangeListener(mFavoritesFragment)); 220 getActionBar().addTab(tab); 221 222 // TODO: We should not artificially create Intents and put them into the Fragment. 223 // It would be nicer to directly pass in the UI constant 224 Intent intent = new Intent(UI.LIST_STREQUENT_ACTION); 225 intent.setClass(this, ContactBrowserActivity.class); 226 227 ContactsIntentResolver resolver = new ContactsIntentResolver(this); 228 ContactsRequest request = resolver.resolveIntent(intent); 229 final ContactListFilter filter = new ContactListFilter( 230 ContactListFilter.FILTER_TYPE_STARRED); 231 mFavoritesFragment.setFilter(filter, false); 232 mFavoritesFragment.setSearchMode(request.isSearchMode()); 233 mFavoritesFragment.setQueryString(request.getQueryString(), false); 234 mFavoritesFragment.setContactsRequest(request); 235 mFavoritesFragment.setDirectorySearchMode(request.isDirectorySearchEnabled() 236 ? DirectoryListLoader.SEARCH_MODE_DEFAULT 237 : DirectoryListLoader.SEARCH_MODE_NONE); 238 mFavoritesFragment.setOnContactListActionListener(mListFragmentListener); 239 } 240 241 /** 242 * Returns true if the intent is due to hitting the green send key while in a call. 243 * 244 * @param intent the intent that launched this activity 245 * @param recentCallsRequest true if the intent is requesting to view recent calls 246 * @return true if the intent is due to hitting the green send key while in a call 247 */ 248 private boolean isSendKeyWhileInCall(final Intent intent, final boolean recentCallsRequest) { 249 // If there is a call in progress go to the call screen 250 if (recentCallsRequest) { 251 final boolean callKey = intent.getBooleanExtra("call_key", false); 252 253 try { 254 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 255 if (callKey && phone != null && phone.showCallScreen()) { 256 return true; 257 } 258 } catch (RemoteException e) { 259 Log.e(TAG, "Failed to handle send while in call", e); 260 } 261 } 262 263 return false; 264 } 265 266 /** 267 * Sets the current tab based on the intent's request type 268 * 269 * @param intent Intent that contains information about which tab should be selected 270 */ 271 private void setCurrentTab(Intent intent) { 272 // If we got here by hitting send and we're in call forward along to the in-call activity 273 final boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.getType()); 274 if (isSendKeyWhileInCall(intent, recentCallsRequest)) { 275 finish(); 276 return; 277 } 278 279 // Tell the children activities that they should ignore any possible saved 280 // state and instead reload their state from the parent's intent 281 intent.putExtra(EXTRA_IGNORE_STATE, true); 282 283 // Remember the old manually selected tab index so that it can be restored if it is 284 // overwritten by one of the programmatic tab selections 285 final int savedTabIndex = mLastManuallySelectedTab; 286 287 // Choose the tab based on the inbound intent 288 if (intent.getBooleanExtra(ContactsFrontDoor.EXTRA_FRONT_DOOR, false)) { 289 // Launched through the contacts front door, set the proper contacts tab (sticky 290 // between favorites and contacts) 291 SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE); 292 boolean favoritesAsContacts = prefs.getBoolean(PREF_FAVORITES_AS_CONTACTS, 293 PREF_FAVORITES_AS_CONTACTS_DEFAULT); 294 if (favoritesAsContacts) { 295 getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_FAVORITES)); 296 } else { 297 getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_CONTACTS)); 298 } 299 } else { 300 // Not launched through the front door, look at the component to determine the tab 301 String componentName = intent.getComponent().getClassName(); 302 if (getClass().getName().equals(componentName)) { 303 if (recentCallsRequest) { 304 getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_CALL_LOG)); 305 } else { 306 getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_DIALER)); 307 } 308 } else { 309 getActionBar().selectTab(getActionBar().getTabAt(mLastManuallySelectedTab)); 310 } 311 } 312 313 // Restore to the previous manual selection 314 mLastManuallySelectedTab = savedTabIndex; 315 316 // Tell the children activities that they should honor their saved states 317 // instead of the state from the parent's intent 318 intent.putExtra(EXTRA_IGNORE_STATE, false); 319 } 320 321 @Override 322 public void onNewIntent(Intent newIntent) { 323 setIntent(newIntent); 324 fixIntent(newIntent); 325 setCurrentTab(newIntent); 326 final String action = newIntent.getAction(); 327 if (UI.FILTER_CONTACTS_ACTION.equals(action)) { 328 setupFilterText(newIntent); 329 } else if (isDialIntent(newIntent)) { 330 setupDialUri(newIntent); 331 } 332 } 333 334 /** Returns true if the given intent contains a phone number to populate the dialer with */ 335 private boolean isDialIntent(Intent intent) { 336 final String action = intent.getAction(); 337 if (Intent.ACTION_DIAL.equals(action)) { 338 return true; 339 } 340 if (Intent.ACTION_VIEW.equals(action)) { 341 final Uri data = intent.getData(); 342 if (data != null && "tel".equals(data.getScheme())) { 343 return true; 344 } 345 } 346 return false; 347 } 348 349 /** 350 * Retrieves the filter text stored in {@link #setupFilterText(Intent)}. 351 * This text originally came from a FILTER_CONTACTS_ACTION intent received 352 * by this activity. The stored text will then be cleared after after this 353 * method returns. 354 * 355 * @return The stored filter text 356 */ 357 public String getAndClearFilterText() { 358 String filterText = mFilterText; 359 mFilterText = null; 360 return filterText; 361 } 362 363 /** 364 * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent. 365 * This is so child activities can check if they are supposed to display a filter. 366 * 367 * @param intent The intent received in {@link #onNewIntent(Intent)} 368 */ 369 private void setupFilterText(Intent intent) { 370 // If the intent was relaunched from history, don't apply the filter text. 371 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { 372 return; 373 } 374 String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY); 375 if (filter != null && filter.length() > 0) { 376 mFilterText = filter; 377 } 378 } 379 380 /** 381 * Retrieves the uri stored in {@link #setupDialUri(Intent)}. This uri 382 * originally came from a dial intent received by this activity. The stored 383 * uri will then be cleared after after this method returns. 384 * 385 * @return The stored uri 386 */ 387 public Uri getAndClearDialUri() { 388 Uri dialUri = mDialUri; 389 mDialUri = null; 390 return dialUri; 391 } 392 393 /** 394 * Stores the uri associated with a dial intent. This is so child activities can 395 * check if they are supposed to display new dial info. 396 * 397 * @param intent The intent received in {@link #onNewIntent(Intent)} 398 */ 399 private void setupDialUri(Intent intent) { 400 // If the intent was relaunched from history, don't reapply the intent. 401 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { 402 return; 403 } 404 mDialUri = intent.getData(); 405 } 406 407 @Override 408 public void onBackPressed() { 409 if (isTaskRoot()) { 410 // Instead of stopping, simply push this to the back of the stack. 411 // This is only done when running at the top of the stack; 412 // otherwise, we have been launched by someone else so need to 413 // allow the user to go back to the caller. 414 moveTaskToBack(false); 415 } else { 416 super.onBackPressed(); 417 } 418 } 419 420 @Override 421 protected void onPostCreate(Bundle savedInstanceState) { 422 super.onPostCreate(savedInstanceState); 423 424 // Pass this lifecycle event down to the fragment 425 mDialpadFragment.onPostCreate(); 426 } 427 428 /** 429 * Tab change listener that is instantiated once for each tab. Handles showing/hiding tabs 430 * and remembers manual tab selections 431 */ 432 private class TabChangeListener implements TabListener { 433 private final Fragment mFragment; 434 435 public TabChangeListener(Fragment fragment) { 436 mFragment = fragment; 437 } 438 439 @Override 440 public void onTabUnselected(Tab tab, FragmentTransaction ft) { 441 ft.hide(mFragment); 442 } 443 444 @Override 445 public void onTabSelected(Tab tab, FragmentTransaction ft) { 446 ft.show(mFragment); 447 448 // Remember this tab index. This function is also called, if the tab is set 449 // automatically in which case the setter (setCurrentTab) has to set this to its old 450 // value afterwards 451 mLastManuallySelectedTab = tab.getPosition(); 452 } 453 454 @Override 455 public void onTabReselected(Tab tab, FragmentTransaction ft) { 456 } 457 } 458 459 private OnContactBrowserActionListener mListFragmentListener = 460 new OnContactBrowserActionListener() { 461 @Override 462 public void onViewContactAction(Uri contactLookupUri) { 463 startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri)); 464 } 465 466 @Override 467 public void onSmsContactAction(Uri contactUri) { 468 } 469 470 @Override 471 public void onSelectionChange() { 472 } 473 474 @Override 475 public void onRemoveFromFavoritesAction(Uri contactUri) { 476 } 477 478 @Override 479 public void onInvalidSelection() { 480 } 481 482 @Override 483 public void onFinishAction() { 484 } 485 486 @Override 487 public void onEditContactAction(Uri contactLookupUri) { 488 } 489 490 @Override 491 public void onDeleteContactAction(Uri contactUri) { 492 } 493 494 @Override 495 public void onCreateNewContactAction() { 496 } 497 498 @Override 499 public void onCallContactAction(Uri contactUri) { 500 } 501 502 @Override 503 public void onAddToFavoritesAction(Uri contactUri) { 504 } 505 }; 506 507 @Override 508 public boolean onCreateOptionsMenu(Menu menu) { 509 // For now, create the menu in here. It would be nice to do this in the Fragment, 510 // but that Fragment is re-used in other views. 511 final ActionBar actionBar = getActionBar(); 512 if (actionBar == null) return false; 513 final Tab tab = actionBar.getSelectedTab(); 514 if (tab == null) return false; 515 final int tabIndex = tab.getPosition(); 516 if (tabIndex != TAB_INDEX_CONTACTS && tabIndex != TAB_INDEX_FAVORITES) return false; 517 518 MenuInflater inflater = getMenuInflater(); 519 inflater.inflate(R.menu.list, menu); 520 return true; 521 } 522 523 @Override 524 public boolean onOptionsItemSelected(MenuItem item) { 525 // This is currently a copy of the equivalent code of ContactBrowserActivity (with the 526 // exception of menu_add, because we do not select items in the list). 527 // Should be consolidated 528 switch (item.getItemId()) { 529 case R.id.menu_settings: { 530 final Intent intent = new Intent(this, ContactsPreferenceActivity.class); 531 startActivity(intent); 532 return true; 533 } 534 case R.id.menu_search: { 535 onSearchRequested(); 536 return true; 537 } 538 case R.id.menu_add: { 539 final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 540 startActivity(intent); 541 return true; 542 } 543 case R.id.menu_import_export: { 544 getImportExportInteraction().startInteraction(); 545 return true; 546 } 547 case R.id.menu_accounts: { 548 final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS); 549 intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] { 550 ContactsContract.AUTHORITY 551 }); 552 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 553 startActivity(intent); 554 return true; 555 } 556 default: 557 return super.onOptionsItemSelected(item); 558 } 559 } 560 561 @Override 562 protected Dialog onCreateDialog(int id, Bundle bundle) { 563 Dialog dialog = getImportExportInteraction().onCreateDialog(id, bundle); 564 if (dialog != null) return dialog; 565 return super.onCreateDialog(id, bundle); 566 } 567 568 private ImportExportInteraction getImportExportInteraction() { 569 if (mImportExportInteraction == null) { 570 mImportExportInteraction = new ImportExportInteraction(this); 571 } 572 return mImportExportInteraction; 573 } 574} 575