Home | History | Annotate | Download | only in activities
      1 /*
      2  * Copyright (C) 2007 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 
     17 package com.android.contacts.activities;
     18 
     19 import android.app.ActionBar;
     20 import android.app.ActionBar.LayoutParams;
     21 import android.app.Activity;
     22 import android.app.Fragment;
     23 import android.content.ActivityNotFoundException;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.net.Uri;
     27 import android.os.Bundle;
     28 import android.provider.ContactsContract.Contacts;
     29 import android.provider.ContactsContract.Intents.Insert;
     30 import android.provider.ContactsContract.Intents.UI;
     31 import android.text.TextUtils;
     32 import android.util.Log;
     33 import android.view.LayoutInflater;
     34 import android.view.Menu;
     35 import android.view.MenuInflater;
     36 import android.view.MenuItem;
     37 import android.view.View;
     38 import android.view.View.OnClickListener;
     39 import android.view.View.OnFocusChangeListener;
     40 import android.view.inputmethod.InputMethodManager;
     41 import android.widget.SearchView;
     42 import android.widget.SearchView.OnCloseListener;
     43 import android.widget.SearchView.OnQueryTextListener;
     44 import android.widget.Toast;
     45 
     46 import com.android.contacts.ContactsActivity;
     47 import com.android.contacts.R;
     48 import com.android.contacts.common.list.ContactEntryListFragment;
     49 import com.android.contacts.list.ContactPickerFragment;
     50 import com.android.contacts.list.ContactsIntentResolver;
     51 import com.android.contacts.list.ContactsRequest;
     52 import com.android.contacts.common.list.DirectoryListLoader;
     53 import com.android.contacts.list.EmailAddressPickerFragment;
     54 import com.android.contacts.list.JoinContactListFragment;
     55 import com.android.contacts.list.LegacyPhoneNumberPickerFragment;
     56 import com.android.contacts.list.OnContactPickerActionListener;
     57 import com.android.contacts.list.OnEmailAddressPickerActionListener;
     58 import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
     59 import com.android.contacts.list.OnPostalAddressPickerActionListener;
     60 import com.android.contacts.common.list.PhoneNumberPickerFragment;
     61 import com.android.contacts.list.PostalAddressPickerFragment;
     62 import com.google.common.collect.Sets;
     63 
     64 import java.util.Set;
     65 
     66 /**
     67  * Displays a list of contacts (or phone numbers or postal addresses) for the
     68  * purposes of selecting one.
     69  */
     70 public class ContactSelectionActivity extends ContactsActivity
     71         implements View.OnCreateContextMenuListener, OnQueryTextListener, OnClickListener,
     72                 OnCloseListener, OnFocusChangeListener {
     73     private static final String TAG = "ContactSelectionActivity";
     74 
     75     private static final int SUBACTIVITY_ADD_TO_EXISTING_CONTACT = 0;
     76 
     77     private static final String KEY_ACTION_CODE = "actionCode";
     78     private static final String KEY_SEARCH_MODE = "searchMode";
     79     private static final int DEFAULT_DIRECTORY_RESULT_LIMIT = 20;
     80 
     81     private ContactsIntentResolver mIntentResolver;
     82     protected ContactEntryListFragment<?> mListFragment;
     83 
     84     private int mActionCode = -1;
     85     private boolean mIsSearchMode;
     86     private boolean mIsSearchSupported;
     87 
     88     private ContactsRequest mRequest;
     89     private SearchView mSearchView;
     90     private View mSearchViewContainer;
     91 
     92     public ContactSelectionActivity() {
     93         mIntentResolver = new ContactsIntentResolver(this);
     94     }
     95 
     96     @Override
     97     public void onAttachFragment(Fragment fragment) {
     98         if (fragment instanceof ContactEntryListFragment<?>) {
     99             mListFragment = (ContactEntryListFragment<?>) fragment;
    100             setupActionListener();
    101         }
    102     }
    103 
    104     @Override
    105     protected void onCreate(Bundle savedState) {
    106         super.onCreate(savedState);
    107 
    108         if (savedState != null) {
    109             mActionCode = savedState.getInt(KEY_ACTION_CODE);
    110             mIsSearchMode = savedState.getBoolean(KEY_SEARCH_MODE);
    111         }
    112 
    113         // Extract relevant information from the intent
    114         mRequest = mIntentResolver.resolveIntent(getIntent());
    115         if (!mRequest.isValid()) {
    116             setResult(RESULT_CANCELED);
    117             finish();
    118             return;
    119         }
    120 
    121         Intent redirect = mRequest.getRedirectIntent();
    122         if (redirect != null) {
    123             // Need to start a different activity
    124             startActivity(redirect);
    125             finish();
    126             return;
    127         }
    128 
    129         configureActivityTitle();
    130 
    131         setContentView(R.layout.contact_picker);
    132 
    133         if (mActionCode != mRequest.getActionCode()) {
    134             mActionCode = mRequest.getActionCode();
    135             configureListFragment();
    136         }
    137 
    138         prepareSearchViewAndActionBar();
    139     }
    140 
    141     private void prepareSearchViewAndActionBar() {
    142         final ActionBar actionBar = getActionBar();
    143         mSearchViewContainer = LayoutInflater.from(actionBar.getThemedContext())
    144                 .inflate(R.layout.custom_action_bar, null);
    145         mSearchView = (SearchView) mSearchViewContainer.findViewById(R.id.search_view);
    146 
    147         // Postal address pickers (and legacy pickers) don't support search, so just show
    148         // "HomeAsUp" button and title.
    149         if (mRequest.getActionCode() == ContactsRequest.ACTION_PICK_POSTAL ||
    150                 mRequest.isLegacyCompatibilityMode()) {
    151             mSearchView.setVisibility(View.GONE);
    152             if (actionBar != null) {
    153                 actionBar.setDisplayShowHomeEnabled(true);
    154                 actionBar.setDisplayHomeAsUpEnabled(true);
    155                 actionBar.setDisplayShowTitleEnabled(true);
    156             }
    157             mIsSearchSupported = false;
    158             configureSearchMode();
    159             return;
    160         }
    161 
    162         actionBar.setDisplayShowHomeEnabled(true);
    163         actionBar.setDisplayHomeAsUpEnabled(true);
    164 
    165         // In order to make the SearchView look like "shown via search menu", we need to
    166         // manually setup its state. See also DialtactsActivity.java and ActionBarAdapter.java.
    167         mSearchView.setIconifiedByDefault(true);
    168         mSearchView.setQueryHint(getString(R.string.hint_findContacts));
    169         mSearchView.setIconified(false);
    170         mSearchView.setFocusable(true);
    171 
    172         mSearchView.setOnQueryTextListener(this);
    173         mSearchView.setOnCloseListener(this);
    174         mSearchView.setOnQueryTextFocusChangeListener(this);
    175 
    176         actionBar.setCustomView(mSearchViewContainer,
    177                 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
    178         actionBar.setDisplayShowCustomEnabled(true);
    179 
    180         mIsSearchSupported = true;
    181         configureSearchMode();
    182     }
    183 
    184     private void configureSearchMode() {
    185         final ActionBar actionBar = getActionBar();
    186         if (mIsSearchMode) {
    187             actionBar.setDisplayShowTitleEnabled(false);
    188             mSearchViewContainer.setVisibility(View.VISIBLE);
    189             mSearchView.requestFocus();
    190         } else {
    191             actionBar.setDisplayShowTitleEnabled(true);
    192             mSearchViewContainer.setVisibility(View.GONE);
    193             mSearchView.setQuery(null, true);
    194         }
    195         invalidateOptionsMenu();
    196     }
    197 
    198     @Override
    199     public boolean onOptionsItemSelected(MenuItem item) {
    200         switch (item.getItemId()) {
    201             case android.R.id.home:
    202                 // Go back to previous screen, intending "cancel"
    203                 setResult(RESULT_CANCELED);
    204                 onBackPressed();
    205                 return true;
    206             case R.id.menu_search:
    207                 mIsSearchMode = !mIsSearchMode;
    208                 configureSearchMode();
    209                 return true;
    210         }
    211         return super.onOptionsItemSelected(item);
    212     }
    213 
    214     @Override
    215     protected void onSaveInstanceState(Bundle outState) {
    216         super.onSaveInstanceState(outState);
    217         outState.putInt(KEY_ACTION_CODE, mActionCode);
    218         outState.putBoolean(KEY_SEARCH_MODE, mIsSearchMode);
    219     }
    220 
    221     private void configureActivityTitle() {
    222         if (!TextUtils.isEmpty(mRequest.getActivityTitle())) {
    223             setTitle(mRequest.getActivityTitle());
    224             return;
    225         }
    226 
    227         int actionCode = mRequest.getActionCode();
    228         switch (actionCode) {
    229             case ContactsRequest.ACTION_INSERT_OR_EDIT_CONTACT: {
    230                 setTitle(R.string.contactInsertOrEditActivityTitle);
    231                 break;
    232             }
    233 
    234             case ContactsRequest.ACTION_PICK_CONTACT: {
    235                 setTitle(R.string.contactPickerActivityTitle);
    236                 break;
    237             }
    238 
    239             case ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT: {
    240                 setTitle(R.string.contactPickerActivityTitle);
    241                 break;
    242             }
    243 
    244             case ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT: {
    245                 setTitle(R.string.shortcutActivityTitle);
    246                 break;
    247             }
    248 
    249             case ContactsRequest.ACTION_PICK_PHONE: {
    250                 setTitle(R.string.contactPickerActivityTitle);
    251                 break;
    252             }
    253 
    254             case ContactsRequest.ACTION_PICK_EMAIL: {
    255                 setTitle(R.string.contactPickerActivityTitle);
    256                 break;
    257             }
    258 
    259             case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
    260                 setTitle(R.string.callShortcutActivityTitle);
    261                 break;
    262             }
    263 
    264             case ContactsRequest.ACTION_CREATE_SHORTCUT_SMS: {
    265                 setTitle(R.string.messageShortcutActivityTitle);
    266                 break;
    267             }
    268 
    269             case ContactsRequest.ACTION_PICK_POSTAL: {
    270                 setTitle(R.string.contactPickerActivityTitle);
    271                 break;
    272             }
    273 
    274             case ContactsRequest.ACTION_PICK_JOIN: {
    275                 setTitle(R.string.titleJoinContactDataWith);
    276                 break;
    277             }
    278         }
    279     }
    280 
    281     /**
    282      * Creates the fragment based on the current request.
    283      */
    284     public void configureListFragment() {
    285         switch (mActionCode) {
    286             case ContactsRequest.ACTION_INSERT_OR_EDIT_CONTACT: {
    287                 ContactPickerFragment fragment = new ContactPickerFragment();
    288                 fragment.setEditMode(true);
    289                 fragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
    290                 fragment.setCreateContactEnabled(!mRequest.isSearchMode());
    291                 mListFragment = fragment;
    292                 break;
    293             }
    294 
    295             case ContactsRequest.ACTION_DEFAULT:
    296             case ContactsRequest.ACTION_PICK_CONTACT: {
    297                 ContactPickerFragment fragment = new ContactPickerFragment();
    298                 fragment.setIncludeProfile(mRequest.shouldIncludeProfile());
    299                 mListFragment = fragment;
    300                 break;
    301             }
    302 
    303             case ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT: {
    304                 ContactPickerFragment fragment = new ContactPickerFragment();
    305                 fragment.setCreateContactEnabled(!mRequest.isSearchMode());
    306                 mListFragment = fragment;
    307                 break;
    308             }
    309 
    310             case ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT: {
    311                 ContactPickerFragment fragment = new ContactPickerFragment();
    312                 fragment.setShortcutRequested(true);
    313                 mListFragment = fragment;
    314                 break;
    315             }
    316 
    317             case ContactsRequest.ACTION_PICK_PHONE: {
    318                 PhoneNumberPickerFragment fragment = getPhoneNumberPickerFragment(mRequest);
    319                 mListFragment = fragment;
    320                 break;
    321             }
    322 
    323             case ContactsRequest.ACTION_PICK_EMAIL: {
    324                 mListFragment = new EmailAddressPickerFragment();
    325                 break;
    326             }
    327 
    328             case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
    329                 PhoneNumberPickerFragment fragment = getPhoneNumberPickerFragment(mRequest);
    330                 fragment.setShortcutAction(Intent.ACTION_CALL);
    331 
    332                 mListFragment = fragment;
    333                 break;
    334             }
    335 
    336             case ContactsRequest.ACTION_CREATE_SHORTCUT_SMS: {
    337                 PhoneNumberPickerFragment fragment = getPhoneNumberPickerFragment(mRequest);
    338                 fragment.setShortcutAction(Intent.ACTION_SENDTO);
    339 
    340                 mListFragment = fragment;
    341                 break;
    342             }
    343 
    344             case ContactsRequest.ACTION_PICK_POSTAL: {
    345                 PostalAddressPickerFragment fragment = new PostalAddressPickerFragment();
    346 
    347                 mListFragment = fragment;
    348                 break;
    349             }
    350 
    351             case ContactsRequest.ACTION_PICK_JOIN: {
    352                 JoinContactListFragment joinFragment = new JoinContactListFragment();
    353                 joinFragment.setTargetContactId(getTargetContactId());
    354                 mListFragment = joinFragment;
    355                 break;
    356             }
    357 
    358             default:
    359                 throw new IllegalStateException("Invalid action code: " + mActionCode);
    360         }
    361 
    362         // Setting compatibility is no longer needed for PhoneNumberPickerFragment since that logic
    363         // has been separated into LegacyPhoneNumberPickerFragment.  But we still need to set
    364         // compatibility for other fragments.
    365         mListFragment.setLegacyCompatibilityMode(mRequest.isLegacyCompatibilityMode());
    366         mListFragment.setDirectoryResultLimit(DEFAULT_DIRECTORY_RESULT_LIMIT);
    367 
    368         getFragmentManager().beginTransaction()
    369                 .replace(R.id.list_container, mListFragment)
    370                 .commitAllowingStateLoss();
    371     }
    372 
    373     private PhoneNumberPickerFragment getPhoneNumberPickerFragment(ContactsRequest request) {
    374         if (mRequest.isLegacyCompatibilityMode()) {
    375             return new LegacyPhoneNumberPickerFragment();
    376         } else {
    377             return new PhoneNumberPickerFragment();
    378         }
    379     }
    380 
    381     public void setupActionListener() {
    382         if (mListFragment instanceof ContactPickerFragment) {
    383             ((ContactPickerFragment) mListFragment).setOnContactPickerActionListener(
    384                     new ContactPickerActionListener());
    385         } else if (mListFragment instanceof PhoneNumberPickerFragment) {
    386             ((PhoneNumberPickerFragment) mListFragment).setOnPhoneNumberPickerActionListener(
    387                     new PhoneNumberPickerActionListener());
    388         } else if (mListFragment instanceof PostalAddressPickerFragment) {
    389             ((PostalAddressPickerFragment) mListFragment).setOnPostalAddressPickerActionListener(
    390                     new PostalAddressPickerActionListener());
    391         } else if (mListFragment instanceof EmailAddressPickerFragment) {
    392             ((EmailAddressPickerFragment) mListFragment).setOnEmailAddressPickerActionListener(
    393                     new EmailAddressPickerActionListener());
    394         } else if (mListFragment instanceof JoinContactListFragment) {
    395             ((JoinContactListFragment) mListFragment).setOnContactPickerActionListener(
    396                     new JoinContactActionListener());
    397         } else {
    398             throw new IllegalStateException("Unsupported list fragment type: " + mListFragment);
    399         }
    400     }
    401 
    402     private final class ContactPickerActionListener implements OnContactPickerActionListener {
    403         @Override
    404         public void onCreateNewContactAction() {
    405             startCreateNewContactActivity();
    406         }
    407 
    408         @Override
    409         public void onEditContactAction(Uri contactLookupUri) {
    410             Bundle extras = getIntent().getExtras();
    411             if (launchAddToContactDialog(extras)) {
    412                 // Show a confirmation dialog to add the value(s) to the existing contact.
    413                 Intent intent = new Intent(ContactSelectionActivity.this,
    414                         ConfirmAddDetailActivity.class);
    415                 intent.setData(contactLookupUri);
    416                 if (extras != null) {
    417                     // First remove name key if present because the dialog does not support name
    418                     // editing. This is fine because the user wants to add information to an
    419                     // existing contact, who should already have a name and we wouldn't want to
    420                     // override the name.
    421                     extras.remove(Insert.NAME);
    422                     intent.putExtras(extras);
    423                 }
    424 
    425                 // Wait for the activity result because we want to keep the picker open (in case the
    426                 // user cancels adding the info to a contact and wants to pick someone else).
    427                 startActivityForResult(intent, SUBACTIVITY_ADD_TO_EXISTING_CONTACT);
    428             } else {
    429                 // Otherwise launch the full contact editor.
    430                 startActivityAndForwardResult(new Intent(Intent.ACTION_EDIT, contactLookupUri));
    431             }
    432         }
    433 
    434         @Override
    435         public void onPickContactAction(Uri contactUri) {
    436             returnPickerResult(contactUri);
    437         }
    438 
    439         @Override
    440         public void onShortcutIntentCreated(Intent intent) {
    441             returnPickerResult(intent);
    442         }
    443 
    444         /**
    445          * Returns true if is a single email or single phone number provided in the {@link Intent}
    446          * extras bundle so that a pop-up confirmation dialog can be used to add the data to
    447          * a contact. Otherwise return false if there are other intent extras that require launching
    448          * the full contact editor. Ignore extras with the key {@link Insert.NAME} because names
    449          * are a special case and we typically don't want to replace the name of an existing
    450          * contact.
    451          */
    452         private boolean launchAddToContactDialog(Bundle extras) {
    453             if (extras == null) {
    454                 return false;
    455             }
    456 
    457             // Copy extras because the set may be modified in the next step
    458             Set<String> intentExtraKeys = Sets.newHashSet();
    459             intentExtraKeys.addAll(extras.keySet());
    460 
    461             // Ignore name key because this is an existing contact.
    462             if (intentExtraKeys.contains(Insert.NAME)) {
    463                 intentExtraKeys.remove(Insert.NAME);
    464             }
    465 
    466             int numIntentExtraKeys = intentExtraKeys.size();
    467             if (numIntentExtraKeys == 2) {
    468                 boolean hasPhone = intentExtraKeys.contains(Insert.PHONE) &&
    469                         intentExtraKeys.contains(Insert.PHONE_TYPE);
    470                 boolean hasEmail = intentExtraKeys.contains(Insert.EMAIL) &&
    471                         intentExtraKeys.contains(Insert.EMAIL_TYPE);
    472                 return hasPhone || hasEmail;
    473             } else if (numIntentExtraKeys == 1) {
    474                 return intentExtraKeys.contains(Insert.PHONE) ||
    475                         intentExtraKeys.contains(Insert.EMAIL);
    476             }
    477             // Having 0 or more than 2 intent extra keys means that we should launch
    478             // the full contact editor to properly handle the intent extras.
    479             return false;
    480         }
    481     }
    482 
    483     private final class PhoneNumberPickerActionListener implements
    484             OnPhoneNumberPickerActionListener {
    485         @Override
    486         public void onPickPhoneNumberAction(Uri dataUri) {
    487             returnPickerResult(dataUri);
    488         }
    489 
    490         @Override
    491         public void onCallNumberDirectly(String phoneNumber) {
    492             Log.w(TAG, "Unsupported call.");
    493         }
    494 
    495         @Override
    496         public void onCallNumberDirectly(String phoneNumber, boolean isVideoCall) {
    497             Log.w(TAG, "Unsupported call.");
    498         }
    499 
    500         @Override
    501         public void onShortcutIntentCreated(Intent intent) {
    502             returnPickerResult(intent);
    503         }
    504 
    505         public void onHomeInActionBarSelected() {
    506             ContactSelectionActivity.this.onBackPressed();
    507         }
    508     }
    509 
    510     private final class JoinContactActionListener implements OnContactPickerActionListener {
    511         @Override
    512         public void onPickContactAction(Uri contactUri) {
    513             Intent intent = new Intent(null, contactUri);
    514             setResult(RESULT_OK, intent);
    515             finish();
    516         }
    517 
    518         @Override
    519         public void onShortcutIntentCreated(Intent intent) {
    520         }
    521 
    522         @Override
    523         public void onCreateNewContactAction() {
    524         }
    525 
    526         @Override
    527         public void onEditContactAction(Uri contactLookupUri) {
    528         }
    529     }
    530 
    531     private final class PostalAddressPickerActionListener implements
    532             OnPostalAddressPickerActionListener {
    533         @Override
    534         public void onPickPostalAddressAction(Uri dataUri) {
    535             returnPickerResult(dataUri);
    536         }
    537     }
    538 
    539     private final class EmailAddressPickerActionListener implements
    540             OnEmailAddressPickerActionListener {
    541         @Override
    542         public void onPickEmailAddressAction(Uri dataUri) {
    543             returnPickerResult(dataUri);
    544         }
    545     }
    546 
    547     public void startActivityAndForwardResult(final Intent intent) {
    548         intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    549 
    550         // Forward extras to the new activity
    551         Bundle extras = getIntent().getExtras();
    552         if (extras != null) {
    553             intent.putExtras(extras);
    554         }
    555         try {
    556             startActivity(intent);
    557         } catch (ActivityNotFoundException e) {
    558             Log.e(TAG, "startActivity() failed: " + e);
    559             Toast.makeText(ContactSelectionActivity.this, R.string.missing_app,
    560                     Toast.LENGTH_SHORT).show();
    561         }
    562         finish();
    563     }
    564 
    565     @Override
    566     public boolean onQueryTextChange(String newText) {
    567         mListFragment.setQueryString(newText, true);
    568         return false;
    569     }
    570 
    571     @Override
    572     public boolean onQueryTextSubmit(String query) {
    573         return false;
    574     }
    575 
    576     @Override
    577     public boolean onClose() {
    578         if (!TextUtils.isEmpty(mSearchView.getQuery())) {
    579             mSearchView.setQuery(null, true);
    580         }
    581         return true;
    582     }
    583 
    584     @Override
    585     public void onFocusChange(View view, boolean hasFocus) {
    586         switch (view.getId()) {
    587             case R.id.search_view: {
    588                 if (hasFocus) {
    589                     showInputMethod(mSearchView.findFocus());
    590                 }
    591             }
    592         }
    593     }
    594 
    595     public void returnPickerResult(Uri data) {
    596         Intent intent = new Intent();
    597         intent.setData(data);
    598         returnPickerResult(intent);
    599     }
    600 
    601     public void returnPickerResult(Intent intent) {
    602         intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    603         setResult(RESULT_OK, intent);
    604         finish();
    605     }
    606 
    607     @Override
    608     public void onClick(View view) {
    609         switch (view.getId()) {
    610             case R.id.floating_action_button: {
    611                 startCreateNewContactActivity();
    612                 break;
    613             }
    614         }
    615     }
    616 
    617     private long getTargetContactId() {
    618         Intent intent = getIntent();
    619         final long targetContactId = intent.getLongExtra(UI.TARGET_CONTACT_ID_EXTRA_KEY, -1);
    620         if (targetContactId == -1) {
    621             Log.e(TAG, "Intent " + intent.getAction() + " is missing required extra: "
    622                     + UI.TARGET_CONTACT_ID_EXTRA_KEY);
    623             setResult(RESULT_CANCELED);
    624             finish();
    625             return -1;
    626         }
    627         return targetContactId;
    628     }
    629 
    630     private void startCreateNewContactActivity() {
    631         Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
    632         intent.putExtra(ContactEditorActivity.INTENT_KEY_FINISH_ACTIVITY_ON_SAVE_COMPLETED, true);
    633         startActivityAndForwardResult(intent);
    634     }
    635 
    636     private void showInputMethod(View view) {
    637         final InputMethodManager imm = (InputMethodManager)
    638                 getSystemService(Context.INPUT_METHOD_SERVICE);
    639         if (imm != null) {
    640             if (!imm.showSoftInput(view, 0)) {
    641                 Log.w(TAG, "Failed to show soft input method.");
    642             }
    643         }
    644     }
    645 
    646     @Override
    647     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    648         super.onActivityResult(requestCode, resultCode, data);
    649         if (requestCode == SUBACTIVITY_ADD_TO_EXISTING_CONTACT) {
    650             if (resultCode == Activity.RESULT_OK) {
    651                 if (data != null) {
    652                     startActivity(data);
    653                 }
    654                 finish();
    655             }
    656         }
    657     }
    658 
    659     @Override
    660     public boolean onCreateOptionsMenu(Menu menu) {
    661         super.onCreateOptionsMenu(menu);
    662 
    663         final MenuInflater inflater = getMenuInflater();
    664         inflater.inflate(R.menu.search_menu, menu);
    665 
    666         final MenuItem searchItem = menu.findItem(R.id.menu_search);
    667         searchItem.setVisible(!mIsSearchMode && mIsSearchSupported);
    668         return true;
    669     }
    670 
    671     @Override
    672     public void onBackPressed() {
    673         if (mIsSearchMode) {
    674             mIsSearchMode = false;
    675             configureSearchMode();
    676         } else {
    677             super.onBackPressed();
    678         }
    679     }
    680 }
    681