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