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.Fragment;
20 import android.content.ActivityNotFoundException;
21 import android.content.Intent;
22 import android.graphics.PorterDuff;
23 import android.graphics.drawable.Drawable;
24 import android.net.Uri;
25 import android.os.Bundle;
26 import android.provider.ContactsContract.Contacts;
27 import androidx.core.content.ContextCompat;
28 import androidx.appcompat.widget.Toolbar;
29 import android.text.TextUtils;
30 import android.util.Log;
31 import android.view.Menu;
32 import android.view.MenuInflater;
33 import android.view.MenuItem;
34 import android.view.View;
35 import android.view.View.OnClickListener;
36 import android.view.View.OnFocusChangeListener;
37 import android.widget.TextView;
38 import android.widget.Toast;
39 
40 import com.android.contacts.AppCompatContactsActivity;
41 import com.android.contacts.R;
42 import com.android.contacts.editor.EditorIntents;
43 import com.android.contacts.list.ContactEntryListFragment;
44 import com.android.contacts.list.ContactPickerFragment;
45 import com.android.contacts.list.ContactsIntentResolver;
46 import com.android.contacts.list.ContactsRequest;
47 import com.android.contacts.list.DirectoryListLoader;
48 import com.android.contacts.list.EmailAddressPickerFragment;
49 import com.android.contacts.list.GroupMemberPickerFragment;
50 import com.android.contacts.list.JoinContactListFragment;
51 import com.android.contacts.list.LegacyPhoneNumberPickerFragment;
52 import com.android.contacts.list.MultiSelectContactsListFragment;
53 import com.android.contacts.list.MultiSelectContactsListFragment.OnCheckBoxListActionListener;
54 import com.android.contacts.list.MultiSelectEmailAddressesListFragment;
55 import com.android.contacts.list.MultiSelectPhoneNumbersListFragment;
56 import com.android.contacts.list.OnContactPickerActionListener;
57 import com.android.contacts.list.OnEmailAddressPickerActionListener;
58 import com.android.contacts.list.OnPhoneNumberPickerActionListener;
59 import com.android.contacts.list.OnPostalAddressPickerActionListener;
60 import com.android.contacts.list.PhoneNumberPickerFragment;
61 import com.android.contacts.list.PostalAddressPickerFragment;
62 import com.android.contacts.list.UiIntentActions;
63 import com.android.contacts.logging.ListEvent;
64 import com.android.contacts.util.ImplicitIntentsUtil;
65 import com.android.contacts.util.ViewUtil;
66 
67 import java.util.ArrayList;
68 
69 /**
70  * Displays a list of contacts (or phone numbers or postal addresses) for the
71  * purposes of selecting one.
72  */
73 public class ContactSelectionActivity extends AppCompatContactsActivity implements
74         View.OnCreateContextMenuListener, ActionBarAdapter.Listener, OnClickListener,
75         OnFocusChangeListener, OnCheckBoxListActionListener {
76     private static final String TAG = "ContactSelection";
77 
78     private static final String KEY_ACTION_CODE = "actionCode";
79     private static final String KEY_SEARCH_MODE = "searchMode";
80     private static final int DEFAULT_DIRECTORY_RESULT_LIMIT = 20;
81 
82     private ContactsIntentResolver mIntentResolver;
83     protected ContactEntryListFragment<?> mListFragment;
84 
85     private int mActionCode = -1;
86     private boolean mIsSearchMode;
87     private boolean mIsSearchSupported;
88 
89     private ContactsRequest mRequest;
90 
91     private ActionBarAdapter mActionBarAdapter;
92     private Toolbar mToolbar;
93 
ContactSelectionActivity()94     public ContactSelectionActivity() {
95         mIntentResolver = new ContactsIntentResolver(this);
96     }
97 
98     @Override
onAttachFragment(Fragment fragment)99     public void onAttachFragment(Fragment fragment) {
100         if (fragment instanceof ContactEntryListFragment<?>) {
101             mListFragment = (ContactEntryListFragment<?>) fragment;
102             setupActionListener();
103         }
104     }
105 
106     @Override
onCreate(Bundle savedState)107     protected void onCreate(Bundle savedState) {
108         super.onCreate(savedState);
109 
110         getWindow().setHideOverlayWindows(true);
111 
112         RequestPermissionsActivity.startPermissionActivityIfNeeded(this);
113 
114         if (savedState != null) {
115             mActionCode = savedState.getInt(KEY_ACTION_CODE);
116             mIsSearchMode = savedState.getBoolean(KEY_SEARCH_MODE);
117         }
118 
119         // Extract relevant information from the intent
120         mRequest = mIntentResolver.resolveIntent(getIntent());
121         if (!mRequest.isValid()) {
122             setResult(RESULT_CANCELED);
123             finish();
124             return;
125         }
126 
127         setContentView(R.layout.contact_picker);
128 
129         if (mActionCode != mRequest.getActionCode()) {
130             mActionCode = mRequest.getActionCode();
131             configureListFragment();
132         }
133 
134         prepareSearchViewAndActionBar(savedState);
135         configureActivityTitle();
136     }
137 
isSelectionMode()138     public boolean isSelectionMode() {
139         return mActionBarAdapter.isSelectionMode();
140     }
141 
isSearchMode()142     public boolean isSearchMode() {
143         return mActionBarAdapter.isSearchMode();
144     }
145 
prepareSearchViewAndActionBar(Bundle savedState)146     private void prepareSearchViewAndActionBar(Bundle savedState) {
147         mToolbar = getView(R.id.toolbar);
148         setSupportActionBar(mToolbar);
149 
150         // Add a shadow under the toolbar.
151         ViewUtil.addRectangularOutlineProvider(findViewById(R.id.toolbar_parent), getResources());
152 
153         mActionBarAdapter = new ActionBarAdapter(this, this, getSupportActionBar(), mToolbar,
154                 R.string.enter_contact_name);
155         mActionBarAdapter.setShowHomeIcon(true);
156         mActionBarAdapter.setShowHomeAsUp(true);
157         mActionBarAdapter.initialize(savedState, mRequest);
158 
159         // Postal address pickers (and legacy pickers) don't support search, so just show
160         // "HomeAsUp" button and title.
161         mIsSearchSupported = mRequest.getActionCode() != ContactsRequest.ACTION_PICK_POSTAL
162                 && mRequest.getActionCode() != ContactsRequest.ACTION_PICK_EMAILS
163                 && mRequest.getActionCode() != ContactsRequest.ACTION_PICK_PHONES
164                 && !mRequest.isLegacyCompatibilityMode();
165         configureSearchMode();
166     }
167 
configureSearchMode()168     private void configureSearchMode() {
169         mActionBarAdapter.setSearchMode(mIsSearchMode);
170         invalidateOptionsMenu();
171     }
172 
173     @Override
onOptionsItemSelected(MenuItem item)174     public boolean onOptionsItemSelected(MenuItem item) {
175         final int id = item.getItemId();
176         if (id == android.R.id.home) {// Go back to previous screen, intending "cancel"
177             setResult(RESULT_CANCELED);
178             onBackPressed();
179         } else if (id == R.id.menu_search) {
180             mIsSearchMode = !mIsSearchMode;
181             configureSearchMode();
182         } else {
183             return super.onOptionsItemSelected(item);
184         }
185         return true;
186     }
187 
188     @Override
onSaveInstanceState(Bundle outState)189     protected void onSaveInstanceState(Bundle outState) {
190         super.onSaveInstanceState(outState);
191         outState.putInt(KEY_ACTION_CODE, mActionCode);
192         outState.putBoolean(KEY_SEARCH_MODE, mIsSearchMode);
193         if (mActionBarAdapter != null) {
194             mActionBarAdapter.onSaveInstanceState(outState);
195         }
196     }
197 
configureActivityTitle()198     private void configureActivityTitle() {
199         if (!TextUtils.isEmpty(mRequest.getActivityTitle())) {
200             getSupportActionBar().setTitle(mRequest.getActivityTitle());
201             return;
202         }
203         int titleResId = -1;
204         int actionCode = mRequest.getActionCode();
205         switch (actionCode) {
206             case ContactsRequest.ACTION_INSERT_OR_EDIT_CONTACT: {
207                 titleResId = R.string.contactInsertOrEditActivityTitle;
208                 break;
209             }
210             case ContactsRequest.ACTION_PICK_CONTACT: {
211                 titleResId = R.string.contactPickerActivityTitle;
212                 break;
213             }
214             case ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT: {
215                 titleResId = R.string.contactPickerActivityTitle;
216                 break;
217             }
218             case ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT: {
219                 titleResId = R.string.shortcutActivityTitle;
220                 break;
221             }
222             case ContactsRequest.ACTION_PICK_PHONE: {
223                 titleResId = R.string.contactPickerActivityTitle;
224                 break;
225             }
226             case ContactsRequest.ACTION_PICK_EMAIL: {
227                 titleResId = R.string.contactPickerActivityTitle;
228                 break;
229             }
230             case ContactsRequest.ACTION_PICK_PHONES: {
231                 titleResId = R.string.pickerSelectContactsActivityTitle;
232                 break;
233             }
234             case ContactsRequest.ACTION_PICK_EMAILS: {
235                 titleResId = R.string.pickerSelectContactsActivityTitle;
236                 break;
237             }
238             case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
239                 titleResId = R.string.shortcutActivityTitle;
240                 break;
241             }
242             case ContactsRequest.ACTION_CREATE_SHORTCUT_SMS: {
243                 titleResId = R.string.shortcutActivityTitle;
244                 break;
245             }
246             case ContactsRequest.ACTION_PICK_POSTAL: {
247                 titleResId = R.string.contactPickerActivityTitle;
248                 break;
249             }
250             case ContactsRequest.ACTION_PICK_JOIN: {
251                 titleResId = R.string.titleJoinContactDataWith;
252                 break;
253             }
254             case ContactsRequest.ACTION_PICK_GROUP_MEMBERS: {
255                 titleResId = R.string.groupMemberPickerActivityTitle;
256                 break;
257             }
258         }
259         if (titleResId > 0) {
260             getSupportActionBar().setTitle(titleResId);
261         }
262     }
263 
264     /**
265      * Creates the fragment based on the current request.
266      */
configureListFragment()267     public void configureListFragment() {
268         switch (mActionCode) {
269             case ContactsRequest.ACTION_INSERT_OR_EDIT_CONTACT: {
270                 ContactPickerFragment fragment = new ContactPickerFragment();
271                 fragment.setEditMode(true);
272                 fragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
273                 fragment.setCreateContactEnabled(!mRequest.isSearchMode());
274                 fragment.setListType(ListEvent.ListType.PICK_CONTACT);
275                 mListFragment = fragment;
276                 break;
277             }
278 
279             case ContactsRequest.ACTION_DEFAULT:
280             case ContactsRequest.ACTION_PICK_CONTACT: {
281                 ContactPickerFragment fragment = new ContactPickerFragment();
282                 fragment.setIncludeFavorites(mRequest.shouldIncludeFavorites());
283                 fragment.setListType(ListEvent.ListType.PICK_CONTACT);
284                 mListFragment = fragment;
285                 break;
286             }
287 
288             case ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT: {
289                 ContactPickerFragment fragment = new ContactPickerFragment();
290                 fragment.setCreateContactEnabled(!mRequest.isSearchMode());
291                 fragment.setListType(ListEvent.ListType.PICK_CONTACT);
292                 mListFragment = fragment;
293                 break;
294             }
295 
296             case ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT: {
297                 ContactPickerFragment fragment = new ContactPickerFragment();
298                 fragment.setShortcutRequested(true);
299                 fragment.setListType(ListEvent.ListType.PICK_CONTACT_FOR_SHORTCUT);
300                 mListFragment = fragment;
301                 break;
302             }
303 
304             case ContactsRequest.ACTION_PICK_PHONE: {
305                 PhoneNumberPickerFragment fragment = getPhoneNumberPickerFragment(mRequest);
306                 fragment.setListType(ListEvent.ListType.PICK_PHONE);
307                 mListFragment = fragment;
308                 break;
309             }
310 
311             case ContactsRequest.ACTION_PICK_EMAIL: {
312                 mListFragment = new EmailAddressPickerFragment();
313                 mListFragment.setListType(ListEvent.ListType.PICK_EMAIL);
314                 break;
315             }
316 
317             case ContactsRequest.ACTION_PICK_PHONES: {
318                 mListFragment = new MultiSelectPhoneNumbersListFragment();
319                 mListFragment.setArguments(getIntent().getExtras());
320                 break;
321             }
322 
323             case ContactsRequest.ACTION_PICK_EMAILS: {
324                 mListFragment = new MultiSelectEmailAddressesListFragment();
325                 mListFragment.setArguments(getIntent().getExtras());
326                 break;
327             }
328             case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
329                 PhoneNumberPickerFragment fragment = getPhoneNumberPickerFragment(mRequest);
330                 fragment.setShortcutAction(Intent.ACTION_CALL);
331                 fragment.setListType(ListEvent.ListType.PICK_CONTACT_FOR_SHORTCUT);
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                 fragment.setListType(ListEvent.ListType.PICK_CONTACT_FOR_SHORTCUT);
340                 mListFragment = fragment;
341                 break;
342             }
343 
344             case ContactsRequest.ACTION_PICK_POSTAL: {
345                 PostalAddressPickerFragment fragment = new PostalAddressPickerFragment();
346                 fragment.setListType(ListEvent.ListType.PICK_POSTAL);
347                 mListFragment = fragment;
348                 break;
349             }
350 
351             case ContactsRequest.ACTION_PICK_JOIN: {
352                 JoinContactListFragment joinFragment = new JoinContactListFragment();
353                 joinFragment.setTargetContactId(getTargetContactId());
354                 joinFragment.setListType(ListEvent.ListType.PICK_JOIN);
355                 mListFragment = joinFragment;
356                 break;
357             }
358 
359             case ContactsRequest.ACTION_PICK_GROUP_MEMBERS: {
360                 final String accountName = getIntent().getStringExtra(
361                         UiIntentActions.GROUP_ACCOUNT_NAME);
362                 final String accountType = getIntent().getStringExtra(
363                         UiIntentActions.GROUP_ACCOUNT_TYPE);
364                 final String accountDataSet = getIntent().getStringExtra(
365                         UiIntentActions.GROUP_ACCOUNT_DATA_SET);
366                 final ArrayList<String> contactIds = getIntent().getStringArrayListExtra(
367                         UiIntentActions.GROUP_CONTACT_IDS);
368                 mListFragment = GroupMemberPickerFragment.newInstance(
369                         accountName, accountType, accountDataSet, contactIds);
370                 mListFragment.setListType(ListEvent.ListType.PICK_GROUP_MEMBERS);
371                 break;
372             }
373 
374             default:
375                 throw new IllegalStateException("Invalid action code: " + mActionCode);
376         }
377 
378         // Setting compatibility is no longer needed for PhoneNumberPickerFragment since that logic
379         // has been separated into LegacyPhoneNumberPickerFragment.  But we still need to set
380         // compatibility for other fragments.
381         mListFragment.setLegacyCompatibilityMode(mRequest.isLegacyCompatibilityMode());
382         mListFragment.setDirectoryResultLimit(DEFAULT_DIRECTORY_RESULT_LIMIT);
383 
384         getFragmentManager().beginTransaction()
385                 .replace(R.id.list_container, mListFragment)
386                 .commitAllowingStateLoss();
387     }
388 
getPhoneNumberPickerFragment(ContactsRequest request)389     private PhoneNumberPickerFragment getPhoneNumberPickerFragment(ContactsRequest request) {
390         if (mRequest.isLegacyCompatibilityMode()) {
391             return new LegacyPhoneNumberPickerFragment();
392         } else {
393             return new PhoneNumberPickerFragment();
394         }
395     }
396 
setupActionListener()397     public void setupActionListener() {
398         if (mListFragment instanceof ContactPickerFragment) {
399             ((ContactPickerFragment) mListFragment).setOnContactPickerActionListener(
400                     new ContactPickerActionListener());
401         } else if (mListFragment instanceof PhoneNumberPickerFragment) {
402             ((PhoneNumberPickerFragment) mListFragment).setOnPhoneNumberPickerActionListener(
403                     new PhoneNumberPickerActionListener());
404         } else if (mListFragment instanceof PostalAddressPickerFragment) {
405             ((PostalAddressPickerFragment) mListFragment).setOnPostalAddressPickerActionListener(
406                     new PostalAddressPickerActionListener());
407         } else if (mListFragment instanceof EmailAddressPickerFragment) {
408             ((EmailAddressPickerFragment) mListFragment).setOnEmailAddressPickerActionListener(
409                     new EmailAddressPickerActionListener());
410         } else if (mListFragment instanceof MultiSelectEmailAddressesListFragment) {
411             ((MultiSelectEmailAddressesListFragment) mListFragment).setCheckBoxListListener(this);
412         } else if (mListFragment instanceof MultiSelectPhoneNumbersListFragment) {
413             ((MultiSelectPhoneNumbersListFragment) mListFragment).setCheckBoxListListener(this);
414         } else if (mListFragment instanceof JoinContactListFragment) {
415             ((JoinContactListFragment) mListFragment).setOnContactPickerActionListener(
416                     new JoinContactActionListener());
417         } else if (mListFragment instanceof GroupMemberPickerFragment) {
418             ((GroupMemberPickerFragment) mListFragment).setListener(
419                     new GroupMemberPickerListener());
420             getMultiSelectListFragment().setCheckBoxListListener(this);
421         } else {
422             throw new IllegalStateException("Unsupported list fragment type: " + mListFragment);
423         }
424     }
425 
getMultiSelectListFragment()426     private MultiSelectContactsListFragment getMultiSelectListFragment() {
427         if (mListFragment instanceof MultiSelectContactsListFragment) {
428             return (MultiSelectContactsListFragment) mListFragment;
429         }
430         return null;
431     }
432 
433     @Override
onAction(int action)434     public void onAction(int action) {
435         switch (action) {
436             case ActionBarAdapter.Listener.Action.START_SEARCH_MODE:
437                 mIsSearchMode = true;
438                 configureSearchMode();
439                 break;
440             case ActionBarAdapter.Listener.Action.CHANGE_SEARCH_QUERY:
441                 final String queryString = mActionBarAdapter.getQueryString();
442                 mListFragment.setQueryString(queryString, /* delaySelection */ false);
443                 break;
444             case ActionBarAdapter.Listener.Action.START_SELECTION_MODE:
445                 if (getMultiSelectListFragment() != null) {
446                     getMultiSelectListFragment().displayCheckBoxes(true);
447                 }
448                 invalidateOptionsMenu();
449                 break;
450             case ActionBarAdapter.Listener.Action.STOP_SEARCH_AND_SELECTION_MODE:
451                 mListFragment.setQueryString("", /* delaySelection */ false);
452                 mActionBarAdapter.setSearchMode(false);
453                 if (getMultiSelectListFragment() != null) {
454                     getMultiSelectListFragment().displayCheckBoxes(false);
455                 }
456                 invalidateOptionsMenu();
457                 break;
458         }
459     }
460 
461     @Override
onUpButtonPressed()462     public void onUpButtonPressed() {
463         onBackPressed();
464     }
465 
466     @Override
onStartDisplayingCheckBoxes()467     public void onStartDisplayingCheckBoxes() {
468         mActionBarAdapter.setSelectionMode(true);
469     }
470 
471     @Override
onSelectedContactIdsChanged()472     public void onSelectedContactIdsChanged() {
473         if (mListFragment instanceof MultiSelectContactsListFragment) {
474             final int count = getMultiSelectListFragment().getSelectedContactIds().size();
475             mActionBarAdapter.setSelectionCount(count);
476             updateAddContactsButton(count);
477 
478             // Show or hide the multi select "Done" button
479             invalidateOptionsMenu();
480         }
481     }
482 
updateAddContactsButton(int count)483     private void updateAddContactsButton(int count) {
484         final TextView textView = (TextView) mActionBarAdapter.getSelectionContainer()
485                 .findViewById(R.id.add_contacts);
486         if (count > 0) {
487             textView.setVisibility(View.VISIBLE);
488             textView.setAllCaps(true);
489             textView.setOnClickListener(new OnClickListener() {
490                 @Override
491                 public void onClick(View v) {
492                     final long[] contactIds =
493                             getMultiSelectListFragment().getSelectedContactIdsArray();
494                     returnSelectedContacts(contactIds);
495                 }
496             });
497         } else {
498             textView.setVisibility(View.GONE);
499         }
500     }
501 
502     @Override
onStopDisplayingCheckBoxes()503     public void onStopDisplayingCheckBoxes() {
504         mActionBarAdapter.setSelectionMode(false);
505     }
506 
507     private final class ContactPickerActionListener implements OnContactPickerActionListener {
508         @Override
onCreateNewContactAction()509         public void onCreateNewContactAction() {
510             startCreateNewContactActivity();
511         }
512 
513         @Override
onEditContactAction(Uri contactLookupUri)514         public void onEditContactAction(Uri contactLookupUri) {
515             startActivityAndForwardResult(EditorIntents.createEditContactIntent(
516                     ContactSelectionActivity.this, contactLookupUri, /* materialPalette =*/ null,
517                     /* photoId =*/ -1));
518         }
519 
520         @Override
onPickContactAction(Uri contactUri)521         public void onPickContactAction(Uri contactUri) {
522             returnPickerResult(contactUri);
523         }
524 
525         @Override
onShortcutIntentCreated(Intent intent)526         public void onShortcutIntentCreated(Intent intent) {
527             returnPickerResult(intent);
528         }
529     }
530 
531     private final class PhoneNumberPickerActionListener implements
532             OnPhoneNumberPickerActionListener {
533         @Override
onPickDataUri(Uri dataUri, boolean isVideoCall, int callInitiationType)534         public void onPickDataUri(Uri dataUri, boolean isVideoCall, int callInitiationType) {
535             returnPickerResult(dataUri);
536         }
537 
538         @Override
onPickPhoneNumber(String phoneNumber, boolean isVideoCall, int callInitiationType)539         public void onPickPhoneNumber(String phoneNumber, boolean isVideoCall,
540                                       int callInitiationType) {
541             Log.w(TAG, "Unsupported call.");
542         }
543 
544         @Override
onShortcutIntentCreated(Intent intent)545         public void onShortcutIntentCreated(Intent intent) {
546             returnPickerResult(intent);
547         }
548 
549         @Override
onHomeInActionBarSelected()550         public void onHomeInActionBarSelected() {
551             ContactSelectionActivity.this.onBackPressed();
552         }
553     }
554 
555     private final class JoinContactActionListener implements OnContactPickerActionListener {
556         @Override
onPickContactAction(Uri contactUri)557         public void onPickContactAction(Uri contactUri) {
558             Intent intent = new Intent(null, contactUri);
559             setResult(RESULT_OK, intent);
560             finish();
561         }
562 
563         @Override
onShortcutIntentCreated(Intent intent)564         public void onShortcutIntentCreated(Intent intent) {
565         }
566 
567         @Override
onCreateNewContactAction()568         public void onCreateNewContactAction() {
569         }
570 
571         @Override
onEditContactAction(Uri contactLookupUri)572         public void onEditContactAction(Uri contactLookupUri) {
573         }
574     }
575 
576     private final class GroupMemberPickerListener implements GroupMemberPickerFragment.Listener {
577 
578         @Override
onGroupMemberClicked(long contactId)579         public void onGroupMemberClicked(long contactId) {
580             final Intent intent = new Intent();
581             intent.putExtra(UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, contactId);
582             returnPickerResult(intent);
583         }
584 
585         @Override
onSelectGroupMembers()586         public void onSelectGroupMembers() {
587             mActionBarAdapter.setSelectionMode(true);
588         }
589     }
590 
returnSelectedContacts(long[] contactIds)591     private void returnSelectedContacts(long[] contactIds) {
592         final Intent intent = new Intent();
593         intent.putExtra(UiIntentActions.TARGET_CONTACT_IDS_EXTRA_KEY, contactIds);
594         returnPickerResult(intent);
595     }
596 
597     private final class PostalAddressPickerActionListener implements
598             OnPostalAddressPickerActionListener {
599         @Override
onPickPostalAddressAction(Uri dataUri)600         public void onPickPostalAddressAction(Uri dataUri) {
601             returnPickerResult(dataUri);
602         }
603     }
604 
605     private final class EmailAddressPickerActionListener implements
606             OnEmailAddressPickerActionListener {
607         @Override
onPickEmailAddressAction(Uri dataUri)608         public void onPickEmailAddressAction(Uri dataUri) {
609             returnPickerResult(dataUri);
610         }
611     }
612 
startActivityAndForwardResult(final Intent intent)613     public void startActivityAndForwardResult(final Intent intent) {
614         intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
615 
616         // Forward extras to the new activity
617         Bundle extras = getIntent().getExtras();
618         if (extras != null) {
619             intent.putExtras(extras);
620         }
621         try {
622             ImplicitIntentsUtil.startActivityInApp(ContactSelectionActivity.this, intent);
623         } catch (ActivityNotFoundException e) {
624             Log.e(TAG, "startActivity() failed: " + e);
625             Toast.makeText(ContactSelectionActivity.this, R.string.missing_app,
626                     Toast.LENGTH_SHORT).show();
627         }
628         finish();
629     }
630 
631     @Override
onFocusChange(View view, boolean hasFocus)632     public void onFocusChange(View view, boolean hasFocus) {
633         if (view.getId() == R.id.search_view) {
634             if (hasFocus) {
635                 mActionBarAdapter.setFocusOnSearchView();
636             }
637         }
638     }
639 
returnPickerResult(Uri data)640     public void returnPickerResult(Uri data) {
641         Intent intent = new Intent();
642         intent.setData(data);
643         returnPickerResult(intent);
644     }
645 
returnPickerResult(Intent intent)646     public void returnPickerResult(Intent intent) {
647         intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
648         setResult(RESULT_OK, intent);
649         finish();
650     }
651 
652     @Override
onClick(View view)653     public void onClick(View view) {
654         if (view.getId() == R.id.floating_action_button) {
655             startCreateNewContactActivity();
656         }
657     }
658 
getTargetContactId()659     private long getTargetContactId() {
660         Intent intent = getIntent();
661         final long targetContactId = intent.getLongExtra(
662                 UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, -1);
663         if (targetContactId == -1) {
664             Log.e(TAG, "Intent " + intent.getAction() + " is missing required extra: "
665                     + UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY);
666             setResult(RESULT_CANCELED);
667             finish();
668             return -1;
669         }
670         return targetContactId;
671     }
672 
startCreateNewContactActivity()673     private void startCreateNewContactActivity() {
674         Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
675         intent.putExtra(ContactEditorActivity.
676                 INTENT_KEY_FINISH_ACTIVITY_ON_SAVE_COMPLETED, true);
677         startActivityAndForwardResult(intent);
678     }
679 
680     @Override
onCreateOptionsMenu(Menu menu)681     public boolean onCreateOptionsMenu(Menu menu) {
682         super.onCreateOptionsMenu(menu);
683 
684         final MenuInflater inflater = getMenuInflater();
685         inflater.inflate(R.menu.search_menu, menu);
686 
687         final MenuItem searchItem = menu.findItem(R.id.menu_search);
688         searchItem.setVisible(!mIsSearchMode && mIsSearchSupported);
689 
690         final Drawable searchIcon = searchItem.getIcon();
691         if (searchIcon != null) {
692             searchIcon.mutate().setColorFilter(ContextCompat.getColor(this,
693                     R.color.actionbar_icon_color), PorterDuff.Mode.SRC_ATOP);
694         }
695         return true;
696     }
697 
698     @Override
onBackPressed()699     public void onBackPressed() {
700         if (!isSafeToCommitTransactions()) {
701             return;
702         }
703 
704         if (isSelectionMode()) {
705             mActionBarAdapter.setSelectionMode(false);
706             if (getMultiSelectListFragment() != null) {
707                 getMultiSelectListFragment().displayCheckBoxes(false);
708             }
709         } else if (mIsSearchMode) {
710             mIsSearchMode = false;
711             configureSearchMode();
712         } else {
713             super.onBackPressed();
714         }
715     }
716 }
717