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