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 
ContactSelectionActivity()92     public ContactSelectionActivity() {
93         mIntentResolver = new ContactsIntentResolver(this);
94     }
95 
96     @Override
onAttachFragment(Fragment fragment)97     public void onAttachFragment(Fragment fragment) {
98         if (fragment instanceof ContactEntryListFragment<?>) {
99             mListFragment = (ContactEntryListFragment<?>) fragment;
100             setupActionListener();
101         }
102     }
103 
104     @Override
onCreate(Bundle savedState)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 
prepareSearchViewAndActionBar()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 
configureSearchMode()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
onOptionsItemSelected(MenuItem item)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
onSaveInstanceState(Bundle outState)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 
configureActivityTitle()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      */
configureListFragment()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 
getPhoneNumberPickerFragment(ContactsRequest request)373     private PhoneNumberPickerFragment getPhoneNumberPickerFragment(ContactsRequest request) {
374         if (mRequest.isLegacyCompatibilityMode()) {
375             return new LegacyPhoneNumberPickerFragment();
376         } else {
377             return new PhoneNumberPickerFragment();
378         }
379     }
380 
setupActionListener()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
onCreateNewContactAction()404         public void onCreateNewContactAction() {
405             startCreateNewContactActivity();
406         }
407 
408         @Override
onEditContactAction(Uri contactLookupUri)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
onPickContactAction(Uri contactUri)435         public void onPickContactAction(Uri contactUri) {
436             returnPickerResult(contactUri);
437         }
438 
439         @Override
onShortcutIntentCreated(Intent intent)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          */
launchAddToContactDialog(Bundle extras)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
onPickPhoneNumberAction(Uri dataUri)486         public void onPickPhoneNumberAction(Uri dataUri) {
487             returnPickerResult(dataUri);
488         }
489 
490         @Override
onCallNumberDirectly(String phoneNumber)491         public void onCallNumberDirectly(String phoneNumber) {
492             Log.w(TAG, "Unsupported call.");
493         }
494 
495         @Override
onCallNumberDirectly(String phoneNumber, boolean isVideoCall)496         public void onCallNumberDirectly(String phoneNumber, boolean isVideoCall) {
497             Log.w(TAG, "Unsupported call.");
498         }
499 
500         @Override
onShortcutIntentCreated(Intent intent)501         public void onShortcutIntentCreated(Intent intent) {
502             returnPickerResult(intent);
503         }
504 
onHomeInActionBarSelected()505         public void onHomeInActionBarSelected() {
506             ContactSelectionActivity.this.onBackPressed();
507         }
508     }
509 
510     private final class JoinContactActionListener implements OnContactPickerActionListener {
511         @Override
onPickContactAction(Uri contactUri)512         public void onPickContactAction(Uri contactUri) {
513             Intent intent = new Intent(null, contactUri);
514             setResult(RESULT_OK, intent);
515             finish();
516         }
517 
518         @Override
onShortcutIntentCreated(Intent intent)519         public void onShortcutIntentCreated(Intent intent) {
520         }
521 
522         @Override
onCreateNewContactAction()523         public void onCreateNewContactAction() {
524         }
525 
526         @Override
onEditContactAction(Uri contactLookupUri)527         public void onEditContactAction(Uri contactLookupUri) {
528         }
529     }
530 
531     private final class PostalAddressPickerActionListener implements
532             OnPostalAddressPickerActionListener {
533         @Override
onPickPostalAddressAction(Uri dataUri)534         public void onPickPostalAddressAction(Uri dataUri) {
535             returnPickerResult(dataUri);
536         }
537     }
538 
539     private final class EmailAddressPickerActionListener implements
540             OnEmailAddressPickerActionListener {
541         @Override
onPickEmailAddressAction(Uri dataUri)542         public void onPickEmailAddressAction(Uri dataUri) {
543             returnPickerResult(dataUri);
544         }
545     }
546 
startActivityAndForwardResult(final Intent intent)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
onQueryTextChange(String newText)566     public boolean onQueryTextChange(String newText) {
567         mListFragment.setQueryString(newText, true);
568         return false;
569     }
570 
571     @Override
onQueryTextSubmit(String query)572     public boolean onQueryTextSubmit(String query) {
573         return false;
574     }
575 
576     @Override
onClose()577     public boolean onClose() {
578         if (!TextUtils.isEmpty(mSearchView.getQuery())) {
579             mSearchView.setQuery(null, true);
580         }
581         return true;
582     }
583 
584     @Override
onFocusChange(View view, boolean hasFocus)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 
returnPickerResult(Uri data)595     public void returnPickerResult(Uri data) {
596         Intent intent = new Intent();
597         intent.setData(data);
598         returnPickerResult(intent);
599     }
600 
returnPickerResult(Intent intent)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
onClick(View view)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 
getTargetContactId()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 
startCreateNewContactActivity()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 
showInputMethod(View view)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
onActivityResult(int requestCode, int resultCode, Intent data)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
onCreateOptionsMenu(Menu menu)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
onBackPressed()672     public void onBackPressed() {
673         if (mIsSearchMode) {
674             mIsSearchMode = false;
675             configureSearchMode();
676         } else {
677             super.onBackPressed();
678         }
679     }
680 }
681