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