DialtactsActivity.java revision b1f0e5e8a4de69bd80276742900441e930fa4c86
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 @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 mStrequentFragment = (StrequentContactListFragment) 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(mStrequentFragment); 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 // TODO: Temporarily disable tab text labels (in all 4 tabs in this 175 // activity) so that the current tabs will all fit onscreen in 176 // portrait (bug 4520620). (Also note we do setText("") rather 177 // leaving the text null, to work around bug 4521549.) 178 tab.setText(""); // R.string.dialerIconLabel 179 tab.setTabListener(new TabChangeListener(mDialpadFragment)); 180 tab.setIcon(R.drawable.ic_tab_dialer); 181 getActionBar().addTab(tab); 182 mDialpadFragment.resolveIntent(); 183 } 184 185 private void setupCallLog() { 186 final Tab tab = getActionBar().newTab(); 187 tab.setText(""); // R.string.recentCallsIconLabel 188 tab.setIcon(R.drawable.ic_tab_recent); 189 tab.setTabListener(new TabChangeListener(mCallLogFragment)); 190 getActionBar().addTab(tab); 191 } 192 193 private void setupContacts() { 194 final Tab tab = getActionBar().newTab(); 195 tab.setText(""); // R.string.contactsIconLabel 196 tab.setIcon(R.drawable.ic_tab_contacts); 197 tab.setTabListener(new TabChangeListener(mContactsFragment)); 198 getActionBar().addTab(tab); 199 200 // TODO: We should not artificially create Intents and put them into the Fragment. 201 // It would be nicer to directly pass in the UI constant 202 Intent intent = new Intent(UI.LIST_ALL_CONTACTS_ACTION); 203 intent.setClass(this, PeopleActivity.class); 204 205 ContactsIntentResolver resolver = new ContactsIntentResolver(this); 206 ContactsRequest request = resolver.resolveIntent(intent); 207 final ContactListFilter filter = ContactListFilter.createFilterWithType( 208 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS); 209 mContactsFragment.setFilter(filter, false); 210 mContactsFragment.setSearchMode(request.isSearchMode()); 211 mContactsFragment.setQueryString(request.getQueryString(), false); 212 mContactsFragment.setContactsRequest(request); 213 mContactsFragment.setDirectorySearchMode(request.isDirectorySearchEnabled() 214 ? DirectoryListLoader.SEARCH_MODE_DEFAULT 215 : DirectoryListLoader.SEARCH_MODE_NONE); 216 mContactsFragment.setOnContactListActionListener(mListFragmentListener); 217 } 218 219 private void setupFavorites() { 220 final Tab tab = getActionBar().newTab(); 221 tab.setText(""); // R.string.contactsFavoritesLabel 222 tab.setIcon(R.drawable.ic_tab_starred); 223 tab.setTabListener(new TabChangeListener(mStrequentFragment)); 224 getActionBar().addTab(tab); 225 mStrequentFragment.setListener(mStrequentListener); 226 } 227 228 /** 229 * Returns true if the intent is due to hitting the green send key while in a call. 230 * 231 * @param intent the intent that launched this activity 232 * @param recentCallsRequest true if the intent is requesting to view recent calls 233 * @return true if the intent is due to hitting the green send key while in a call 234 */ 235 private boolean isSendKeyWhileInCall(final Intent intent, final boolean recentCallsRequest) { 236 // If there is a call in progress go to the call screen 237 if (recentCallsRequest) { 238 final boolean callKey = intent.getBooleanExtra("call_key", false); 239 240 try { 241 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 242 if (callKey && phone != null && phone.showCallScreen()) { 243 return true; 244 } 245 } catch (RemoteException e) { 246 Log.e(TAG, "Failed to handle send while in call", e); 247 } 248 } 249 250 return false; 251 } 252 253 /** 254 * Sets the current tab based on the intent's request type 255 * 256 * @param intent Intent that contains information about which tab should be selected 257 */ 258 private void setCurrentTab(Intent intent) { 259 // If we got here by hitting send and we're in call forward along to the in-call activity 260 final boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.getType()); 261 if (isSendKeyWhileInCall(intent, recentCallsRequest)) { 262 finish(); 263 return; 264 } 265 266 // Tell the children activities that they should ignore any possible saved 267 // state and instead reload their state from the parent's intent 268 intent.putExtra(EXTRA_IGNORE_STATE, true); 269 270 // Remember the old manually selected tab index so that it can be restored if it is 271 // overwritten by one of the programmatic tab selections 272 final int savedTabIndex = mLastManuallySelectedTab; 273 274 // Look at the component to determine the tab 275 String componentName = intent.getComponent().getClassName(); 276 if (getClass().getName().equals(componentName)) { 277 if (recentCallsRequest) { 278 getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_CALL_LOG)); 279 } else { 280 getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_DIALER)); 281 } 282 } else { 283 getActionBar().selectTab(getActionBar().getTabAt(mLastManuallySelectedTab)); 284 } 285 286 // Restore to the previous manual selection 287 mLastManuallySelectedTab = savedTabIndex; 288 289 // Tell the children activities that they should honor their saved states 290 // instead of the state from the parent's intent 291 intent.putExtra(EXTRA_IGNORE_STATE, false); 292 } 293 294 @Override 295 public void onNewIntent(Intent newIntent) { 296 setIntent(newIntent); 297 fixIntent(newIntent); 298 setCurrentTab(newIntent); 299 final String action = newIntent.getAction(); 300 if (UI.FILTER_CONTACTS_ACTION.equals(action)) { 301 setupFilterText(newIntent); 302 } else if (isDialIntent(newIntent)) { 303 setupDialUri(newIntent); 304 } 305 // Fill in a phone number again. 306 mDialpadFragment.resolveIntent(); 307 } 308 309 /** Returns true if the given intent contains a phone number to populate the dialer with */ 310 private boolean isDialIntent(Intent intent) { 311 final String action = intent.getAction(); 312 if (Intent.ACTION_DIAL.equals(action)) { 313 return true; 314 } 315 if (Intent.ACTION_VIEW.equals(action)) { 316 final Uri data = intent.getData(); 317 if (data != null && "tel".equals(data.getScheme())) { 318 return true; 319 } 320 } 321 return false; 322 } 323 324 /** 325 * Retrieves the filter text stored in {@link #setupFilterText(Intent)}. 326 * This text originally came from a FILTER_CONTACTS_ACTION intent received 327 * by this activity. The stored text will then be cleared after after this 328 * method returns. 329 * 330 * @return The stored filter text 331 */ 332 public String getAndClearFilterText() { 333 String filterText = mFilterText; 334 mFilterText = null; 335 return filterText; 336 } 337 338 /** 339 * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent. 340 * This is so child activities can check if they are supposed to display a filter. 341 * 342 * @param intent The intent received in {@link #onNewIntent(Intent)} 343 */ 344 private void setupFilterText(Intent intent) { 345 // If the intent was relaunched from history, don't apply the filter text. 346 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { 347 return; 348 } 349 String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY); 350 if (filter != null && filter.length() > 0) { 351 mFilterText = filter; 352 } 353 } 354 355 /** 356 * Retrieves the uri stored in {@link #setupDialUri(Intent)}. This uri 357 * originally came from a dial intent received by this activity. The stored 358 * uri will then be cleared after after this method returns. 359 * 360 * @return The stored uri 361 */ 362 public Uri getAndClearDialUri() { 363 Uri dialUri = mDialUri; 364 mDialUri = null; 365 return dialUri; 366 } 367 368 /** 369 * Stores the uri associated with a dial intent. This is so child activities can 370 * check if they are supposed to display new dial info. 371 * 372 * @param intent The intent received in {@link #onNewIntent(Intent)} 373 */ 374 private void setupDialUri(Intent intent) { 375 // If the intent was relaunched from history, don't reapply the intent. 376 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { 377 return; 378 } 379 mDialUri = intent.getData(); 380 } 381 382 @Override 383 public void onBackPressed() { 384 if (isTaskRoot()) { 385 // Instead of stopping, simply push this to the back of the stack. 386 // This is only done when running at the top of the stack; 387 // otherwise, we have been launched by someone else so need to 388 // allow the user to go back to the caller. 389 moveTaskToBack(false); 390 } else { 391 super.onBackPressed(); 392 } 393 } 394 395 @Override 396 protected void onPostCreate(Bundle savedInstanceState) { 397 super.onPostCreate(savedInstanceState); 398 399 // Pass this lifecycle event down to the fragment 400 mDialpadFragment.onPostCreate(); 401 } 402 403 /** 404 * Tab change listener that is instantiated once for each tab. Handles showing/hiding tabs 405 * and remembers manual tab selections 406 */ 407 private class TabChangeListener implements TabListener { 408 private final Fragment mFragment; 409 410 public TabChangeListener(Fragment fragment) { 411 mFragment = fragment; 412 } 413 414 @Override 415 public void onTabUnselected(Tab tab, FragmentTransaction ft) { 416 ft.hide(mFragment); 417 } 418 419 @Override 420 public void onTabSelected(Tab tab, FragmentTransaction ft) { 421 ft.show(mFragment); 422 423 // Remember this tab index. This function is also called, if the tab is set 424 // automatically in which case the setter (setCurrentTab) has to set this to its old 425 // value afterwards 426 mLastManuallySelectedTab = tab.getPosition(); 427 } 428 429 @Override 430 public void onTabReselected(Tab tab, FragmentTransaction ft) { 431 } 432 } 433 434 private OnContactBrowserActionListener mListFragmentListener = 435 new OnContactBrowserActionListener() { 436 @Override 437 public void onViewContactAction(Uri contactLookupUri) { 438 startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri)); 439 } 440 441 @Override 442 public void onSmsContactAction(Uri contactUri) { 443 } 444 445 @Override 446 public void onSelectionChange() { 447 } 448 449 @Override 450 public void onRemoveFromFavoritesAction(Uri contactUri) { 451 } 452 453 @Override 454 public void onInvalidSelection() { 455 } 456 457 @Override 458 public void onFinishAction() { 459 } 460 461 @Override 462 public void onEditContactAction(Uri contactLookupUri) { 463 } 464 465 @Override 466 public void onDeleteContactAction(Uri contactUri) { 467 } 468 469 @Override 470 public void onCreateNewContactAction() { 471 } 472 473 @Override 474 public void onCallContactAction(Uri contactUri) { 475 PhoneNumberInteraction.startInteractionForPhoneCall( 476 DialtactsActivity.this, contactUri); 477 } 478 479 @Override 480 public void onAddToFavoritesAction(Uri contactUri) { 481 } 482 }; 483 484 private StrequentContactListFragment.Listener mStrequentListener = 485 new StrequentContactListFragment.Listener() { 486 @Override 487 public void onContactSelected(Uri contactUri) { 488 PhoneNumberInteraction.startInteractionForPhoneCall( 489 DialtactsActivity.this, contactUri); 490 } 491 }; 492 493 @Override 494 public boolean onCreateOptionsMenu(Menu menu) { 495 // For now, create the menu in here. It would be nice to do this in the Fragment, 496 // but that Fragment is re-used in other views. 497 final ActionBar actionBar = getActionBar(); 498 if (actionBar == null) return false; 499 final Tab tab = actionBar.getSelectedTab(); 500 if (tab == null) return false; 501 final int tabIndex = tab.getPosition(); 502 if (tabIndex != TAB_INDEX_CONTACTS && tabIndex != TAB_INDEX_FAVORITES) return false; 503 504 MenuInflater inflater = getMenuInflater(); 505 inflater.inflate(R.menu.list, menu); 506 return true; 507 } 508 509 @Override 510 public boolean onOptionsItemSelected(MenuItem item) { 511 // This is currently a copy of the equivalent code of PeopleActivity (with the 512 // exception of menu_add, because we do not select items in the list). 513 // Should be consolidated 514 switch (item.getItemId()) { 515 case R.id.menu_settings: { 516 final Intent intent = new Intent(this, ContactsPreferenceActivity.class); 517 startActivity(intent); 518 return true; 519 } 520 case R.id.menu_search: { 521 onSearchRequested(); 522 return true; 523 } 524 case R.id.menu_add: { 525 final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 526 startActivity(intent); 527 return true; 528 } 529 case R.id.menu_import_export: { 530 ImportExportDialogFragment.show(getFragmentManager()); 531 return true; 532 } 533 case R.id.menu_accounts: { 534 final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS); 535 intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] { 536 ContactsContract.AUTHORITY 537 }); 538 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 539 startActivity(intent); 540 return true; 541 } 542 default: 543 return super.onOptionsItemSelected(item); 544 } 545 } 546} 547