PeopleActivity.java revision b5760b94bbf56ce348876ec36f4669d20a1530f5
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.contacts.activities;
18
19import com.android.contacts.ContactSaveService;
20import com.android.contacts.ContactsActivity;
21import com.android.contacts.R;
22import com.android.contacts.calllog.CallLogFragment;
23import com.android.contacts.detail.ContactDetailFragment;
24import com.android.contacts.dialpad.DialpadFragment;
25import com.android.contacts.group.GroupBrowseListFragment;
26import com.android.contacts.interactions.ContactDeletionInteraction;
27import com.android.contacts.interactions.GroupDeletionDialogFragment;
28import com.android.contacts.interactions.GroupRenamingDialogFragment;
29import com.android.contacts.interactions.ImportExportDialogFragment;
30import com.android.contacts.interactions.PhoneNumberInteraction;
31import com.android.contacts.list.ContactBrowseListContextMenuAdapter;
32import com.android.contacts.list.ContactBrowseListFragment;
33import com.android.contacts.list.ContactEntryListFragment;
34import com.android.contacts.list.ContactListFilter;
35import com.android.contacts.list.ContactListFilterController;
36import com.android.contacts.list.ContactsIntentResolver;
37import com.android.contacts.list.ContactsRequest;
38import com.android.contacts.list.ContactsUnavailableFragment;
39import com.android.contacts.list.CustomContactListFilterActivity;
40import com.android.contacts.list.DefaultContactBrowseListFragment;
41import com.android.contacts.list.DirectoryListLoader;
42import com.android.contacts.list.OnContactBrowserActionListener;
43import com.android.contacts.list.OnContactsUnavailableActionListener;
44import com.android.contacts.list.ProviderStatusLoader;
45import com.android.contacts.list.ProviderStatusLoader.ProviderStatusListener;
46import com.android.contacts.list.StrequentContactListFragment;
47import com.android.contacts.model.AccountTypeManager;
48import com.android.contacts.preference.ContactsPreferenceActivity;
49import com.android.contacts.util.AccountSelectionUtil;
50import com.android.contacts.util.DialogManager;
51import com.android.contacts.widget.ContextMenuAdapter;
52
53import android.accounts.Account;
54import android.app.ActionBar;
55import android.app.ActionBar.Tab;
56import android.app.ActionBar.TabListener;
57import android.app.Activity;
58import android.app.Dialog;
59import android.app.Fragment;
60import android.app.FragmentManager;
61import android.app.FragmentTransaction;
62import android.content.ActivityNotFoundException;
63import android.content.ContentValues;
64import android.content.Intent;
65import android.content.res.TypedArray;
66import android.net.Uri;
67import android.os.Bundle;
68import android.provider.ContactsContract;
69import android.provider.ContactsContract.Contacts;
70import android.provider.ContactsContract.Intents;
71import android.provider.ContactsContract.ProviderStatus;
72import android.provider.Settings;
73import android.util.Log;
74import android.view.KeyEvent;
75import android.view.Menu;
76import android.view.MenuInflater;
77import android.view.MenuItem;
78import android.view.View;
79import android.view.View.OnClickListener;
80import android.view.Window;
81import android.widget.SearchView;
82import android.widget.Toast;
83
84import java.util.ArrayList;
85
86/**
87 * Displays a list to browse contacts. For xlarge screens, this also displays a detail-pane on
88 * the right.
89 */
90public class PeopleActivity extends ContactsActivity
91        implements View.OnCreateContextMenuListener, ActionBarAdapter.Listener,
92        DialogManager.DialogShowingViewActivity,
93        ContactListFilterController.ContactListFilterListener, ProviderStatusListener {
94
95    private static final String TAG = "ContactBrowserActivity";
96
97    private static final int SUBACTIVITY_NEW_CONTACT = 2;
98    private static final int SUBACTIVITY_EDIT_CONTACT = 3;
99    private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 4;
100
101    private static final String KEY_SEARCH_MODE = "searchMode";
102
103    private DialogManager mDialogManager = new DialogManager(this);
104
105    private ContactsIntentResolver mIntentResolver;
106    private ContactsRequest mRequest;
107
108    private ActionBarAdapter mActionBarAdapter;
109
110    private boolean mSearchMode;
111
112    private ContactBrowseListFragment mListFragment;
113
114    /**
115     * Whether we have a right-side contact pane for displaying contact info while browsing.
116     * Generally means "this is a tablet".
117     */
118    private boolean mContactContentDisplayed;
119
120    private ContactDetailFragment mDetailFragment;
121    private DetailFragmentListener mDetailFragmentListener = new DetailFragmentListener();
122
123    private PhoneNumberInteraction mPhoneNumberCallInteraction;
124    private PhoneNumberInteraction mSendTextMessageInteraction;
125
126    private boolean mSearchInitiated;
127
128    private ContactListFilterController mContactListFilterController;
129
130    private View mAddContactImageView;
131
132    private ContactsUnavailableFragment mContactsUnavailableFragment;
133    private ProviderStatusLoader mProviderStatusLoader;
134    private int mProviderStatus = -1;
135
136    private boolean mOptionsMenuContactsAvailable;
137    private boolean mOptionsMenuGroupActionsEnabled;
138
139    private DefaultContactBrowseListFragment mContactsFragment;
140    private StrequentContactListFragment mFavoritesFragment;
141    private GroupBrowseListFragment mGroupsFragment;
142
143    public PeopleActivity() {
144        mIntentResolver = new ContactsIntentResolver(this);
145        // TODO: Get rid of the ContactListFilterController class because there aren't any
146        // dropdown filters anymore. Just store the selected filter as a member variable.
147        mContactListFilterController = new ContactListFilterController(this);
148        mContactListFilterController.addListener(this);
149        mProviderStatusLoader = new ProviderStatusLoader(this);
150    }
151
152    public boolean areContactsAvailable() {
153        return mProviderStatus == ProviderStatus.STATUS_NORMAL;
154    }
155
156    @Override
157    public void onAttachFragment(Fragment fragment) {
158        if (fragment instanceof ContactBrowseListFragment) {
159            mListFragment = (ContactBrowseListFragment)fragment;
160            mListFragment.setOnContactListActionListener(new ContactBrowserActionListener());
161            if (!getWindow().hasFeature(Window.FEATURE_ACTION_BAR)) {
162                mListFragment.setContextMenuAdapter(
163                        new ContactBrowseListContextMenuAdapter(mListFragment));
164            }
165        } else if (fragment instanceof ContactDetailFragment) {
166            mDetailFragment = (ContactDetailFragment)fragment;
167            mDetailFragment.setListener(mDetailFragmentListener);
168            mContactContentDisplayed = true;
169        } else if (fragment instanceof ContactsUnavailableFragment) {
170            mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment;
171            mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader);
172            mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
173                    new ContactsUnavailableFragmentListener());
174        }
175    }
176
177    @Override
178    protected void onCreate(Bundle savedState) {
179        super.onCreate(savedState);
180
181        mAddContactImageView = getLayoutInflater().inflate(
182                R.layout.add_contact_menu_item, null, false);
183        View item = mAddContactImageView.findViewById(R.id.menu_item);
184        item.setOnClickListener(new OnClickListener() {
185            @Override
186            public void onClick(View v) {
187                final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
188                startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
189            }
190        });
191
192        configureContentView(true, savedState);
193    }
194
195    @Override
196    protected void onNewIntent(Intent intent) {
197        setIntent(intent);
198        configureContentView(false, null);
199    }
200
201    private void configureContentView(boolean createContentView, Bundle savedState) {
202        // Extract relevant information from the intent
203        mRequest = mIntentResolver.resolveIntent(getIntent());
204        if (!mRequest.isValid()) {
205            setResult(RESULT_CANCELED);
206            finish();
207            return;
208        }
209
210        Intent redirect = mRequest.getRedirectIntent();
211        if (redirect != null) {
212            // Need to start a different activity
213            startActivity(redirect);
214            finish();
215            return;
216        }
217
218        if (createContentView) {
219            setContentView(R.layout.people_activity);
220
221            final FragmentManager fragmentManager = getFragmentManager();
222            mFavoritesFragment = (StrequentContactListFragment) fragmentManager
223                    .findFragmentById(R.id.favorites_fragment);
224            mContactsFragment = (DefaultContactBrowseListFragment) fragmentManager
225                    .findFragmentById(R.id.contacts_fragment);
226            mGroupsFragment = (GroupBrowseListFragment) fragmentManager
227                    .findFragmentById(R.id.groups_fragment);
228
229            // Hide all tabs (the current tab will later be reshown once a tab is selected)
230            final FragmentTransaction transaction = fragmentManager.beginTransaction();
231            transaction.hide(mFavoritesFragment);
232            transaction.hide(mContactsFragment);
233            transaction.hide(mGroupsFragment);
234            transaction.commit();
235        }
236
237        if (mRequest.getActionCode() == ContactsRequest.ACTION_VIEW_CONTACT
238                && !mContactContentDisplayed) {
239            redirect = new Intent(this, ContactDetailActivity.class);
240            redirect.setAction(Intent.ACTION_VIEW);
241            redirect.setData(mRequest.getContactUri());
242            startActivity(redirect);
243            finish();
244            return;
245        }
246
247        setTitle(mRequest.getActivityTitle());
248        ActionBar actionBar = getActionBar();
249        mActionBarAdapter = new ActionBarAdapter(this);
250        mActionBarAdapter.onCreate(savedState, mRequest, getActionBar());
251        mActionBarAdapter.setContactListFilterController(mContactListFilterController);
252
253        if (createContentView) {
254            actionBar.removeAllTabs();
255            Tab favoritesTab = actionBar.newTab();
256            favoritesTab.setText(getString(R.string.strequentList));
257            favoritesTab.setTabListener(new TabChangeListener(mFavoritesFragment));
258            actionBar.addTab(favoritesTab);
259
260            Tab peopleTab = actionBar.newTab();
261            peopleTab.setText(getString(R.string.people));
262            peopleTab.setTabListener(new TabChangeListener(mContactsFragment));
263            actionBar.addTab(peopleTab);
264
265            Tab groupsTab = actionBar.newTab();
266            groupsTab.setText(getString(R.string.contactsGroupsLabel));
267            groupsTab.setTabListener(new TabChangeListener(mGroupsFragment));
268            actionBar.addTab(groupsTab);
269            actionBar.setDisplayShowTitleEnabled(true);
270
271            TypedArray a = obtainStyledAttributes(null, R.styleable.ActionBarHomeIcon);
272            boolean showHomeIcon = a.getBoolean(R.styleable.ActionBarHomeIcon_show_home_icon, true);
273            actionBar.setDisplayShowHomeEnabled(showHomeIcon);
274
275            invalidateOptionsMenu();
276        }
277
278        configureFragments(savedState == null);
279    }
280
281    /**
282     * Tab change listener that is instantiated once for each tab. Handles showing/hiding fragments.
283     * TODO: Use ViewPager so that tabs can be swiped left and right. Figure out how to use the
284     * support library in our app.
285     */
286    private class TabChangeListener implements TabListener {
287        private final Fragment mFragment;
288
289        public TabChangeListener(Fragment fragment) {
290            mFragment = fragment;
291        }
292
293        @Override
294        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
295            ft.hide(mFragment);
296        }
297
298        @Override
299        public void onTabSelected(Tab tab, FragmentTransaction ft) {
300            ft.show(mFragment);
301        }
302
303        @Override
304        public void onTabReselected(Tab tab, FragmentTransaction ft) {
305        }
306    }
307
308    @Override
309    protected void onPause() {
310        if (mActionBarAdapter != null) {
311            mActionBarAdapter.setListener(null);
312        }
313
314        mOptionsMenuContactsAvailable = false;
315        mOptionsMenuGroupActionsEnabled = false;
316
317        mProviderStatus = -1;
318        mProviderStatusLoader.setProviderStatusListener(null);
319        super.onPause();
320    }
321
322    @Override
323    protected void onResume() {
324        super.onResume();
325        if (mActionBarAdapter != null) {
326            mActionBarAdapter.setListener(this);
327        }
328        mProviderStatusLoader.setProviderStatusListener(this);
329        updateFragmentVisibility();
330    }
331
332    @Override
333    protected void onStart() {
334        mContactListFilterController.onStart();
335        super.onStart();
336    }
337
338    private void configureFragments(boolean fromRequest) {
339        if (fromRequest) {
340            ContactListFilter filter = null;
341            int actionCode = mRequest.getActionCode();
342            switch (actionCode) {
343                case ContactsRequest.ACTION_ALL_CONTACTS:
344                    filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
345                    break;
346                case ContactsRequest.ACTION_CONTACTS_WITH_PHONES:
347                    filter = new ContactListFilter(
348                            ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY);
349                    break;
350
351                // TODO: handle FREQUENT and STREQUENT according to the spec
352                case ContactsRequest.ACTION_FREQUENT:
353                case ContactsRequest.ACTION_STREQUENT:
354                    // For now they are treated the same as STARRED
355                case ContactsRequest.ACTION_STARRED:
356                    filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_STARRED);
357                    break;
358            }
359
360            mSearchMode = mRequest.isSearchMode();
361            if (filter != null) {
362                mContactListFilterController.setContactListFilter(filter, false);
363                mSearchMode = false;
364            } else if (mRequest.getActionCode() == ContactsRequest.ACTION_ALL_CONTACTS) {
365                mContactListFilterController.setContactListFilter(new ContactListFilter(
366                        ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), false);
367            }
368
369            if (mRequest.getContactUri() != null) {
370                mSearchMode = false;
371            }
372
373            mListFragment.setContactsRequest(mRequest);
374            configureListFragmentForRequest();
375
376        } else {
377            mSearchMode = mActionBarAdapter.isSearchMode();
378        }
379
380        configureListFragment();
381
382        invalidateOptionsMenu();
383    }
384
385    @Override
386    public void onContactListFiltersLoaded() {
387        if (mListFragment == null || !mListFragment.isAdded()) {
388            return;
389        }
390
391        mListFragment.setFilter(mContactListFilterController.getFilter());
392
393        invalidateOptionsMenu();
394    }
395
396    @Override
397    public void onContactListFilterChanged() {
398        if (mListFragment == null || !mListFragment.isAdded()) {
399            return;
400        }
401
402        mListFragment.setFilter(mContactListFilterController.getFilter());
403
404        invalidateOptionsMenu();
405    }
406
407    @Override
408    public void onContactListFilterCustomizationRequest() {
409        startActivityForResult(new Intent(this, CustomContactListFilterActivity.class),
410                SUBACTIVITY_CUSTOMIZE_FILTER);
411    }
412
413    private void setupContactDetailFragment(final Uri contactLookupUri) {
414        mDetailFragment.loadUri(contactLookupUri);
415    }
416
417    /**
418     * Handler for action bar actions.
419     */
420    @Override
421    public void onAction(Action action) {
422        switch (action) {
423            case START_SEARCH_MODE:
424                // Bring the contact list fragment to the front.
425                FragmentTransaction ft = getFragmentManager().beginTransaction();
426                ft.show(mContactsFragment);
427                ft.commit();
428                break;
429            case STOP_SEARCH_MODE:
430            case CHANGE_SEARCH_QUERY:
431                // Refresh the contact list fragment.
432                configureFragments(false /* from request */);
433                mListFragment.setQueryString(mActionBarAdapter.getQueryString(), true);
434                break;
435            default:
436                throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action);
437        }
438    }
439
440    private void configureListFragmentForRequest() {
441        Uri contactUri = mRequest.getContactUri();
442        if (contactUri != null) {
443            mListFragment.setSelectedContactUri(contactUri);
444        }
445
446        mListFragment.setSearchMode(mRequest.isSearchMode());
447        mListFragment.setQueryString(mRequest.getQueryString(), false);
448
449        if (mRequest.isDirectorySearchEnabled()) {
450            mListFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT);
451        } else {
452            mListFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
453        }
454
455        if (mContactListFilterController.isLoaded()) {
456            mListFragment.setFilter(mContactListFilterController.getFilter());
457        }
458    }
459
460    private void configureListFragment() {
461        mListFragment.setSearchMode(mSearchMode);
462
463        mListFragment.setVisibleScrollbarEnabled(!mSearchMode);
464        mListFragment.setVerticalScrollbarPosition(
465                mContactContentDisplayed
466                        ? View.SCROLLBAR_POSITION_LEFT
467                        : View.SCROLLBAR_POSITION_RIGHT);
468        mListFragment.setSelectionVisible(mContactContentDisplayed);
469        mListFragment.setQuickContactEnabled(!mContactContentDisplayed);
470    }
471
472    @Override
473    public void onProviderStatusChange() {
474        updateFragmentVisibility();
475    }
476
477    private void updateFragmentVisibility() {
478        int providerStatus = mProviderStatusLoader.getProviderStatus();
479        if (providerStatus == mProviderStatus) {
480            return;
481        }
482
483        mProviderStatus = providerStatus;
484
485        View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view);
486        View mainView = findViewById(R.id.main_view);
487
488        if (mProviderStatus == ProviderStatus.STATUS_NORMAL) {
489            contactsUnavailableView.setVisibility(View.GONE);
490            if (mainView != null) {
491                mainView.setVisibility(View.VISIBLE);
492            }
493            if (mListFragment != null) {
494                mListFragment.setEnabled(true);
495            }
496        } else {
497            if (mListFragment != null) {
498                mListFragment.setEnabled(false);
499            }
500            if (mContactsUnavailableFragment == null) {
501                mContactsUnavailableFragment = new ContactsUnavailableFragment();
502                mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader);
503                mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
504                        new ContactsUnavailableFragmentListener());
505                getFragmentManager().beginTransaction()
506                        .replace(R.id.contacts_unavailable_container, mContactsUnavailableFragment)
507                        .commit();
508            } else {
509                mContactsUnavailableFragment.update();
510            }
511            contactsUnavailableView.setVisibility(View.VISIBLE);
512            if (mainView != null) {
513                mainView.setVisibility(View.INVISIBLE);
514            }
515        }
516
517        invalidateOptionsMenu();
518    }
519
520    private final class ContactBrowserActionListener implements OnContactBrowserActionListener {
521
522        @Override
523        public void onSelectionChange() {
524            if (mContactContentDisplayed) {
525                setupContactDetailFragment(mListFragment.getSelectedContactUri());
526            }
527        }
528
529        @Override
530        public void onViewContactAction(Uri contactLookupUri) {
531            if (mContactContentDisplayed) {
532                setupContactDetailFragment(contactLookupUri);
533            } else {
534                startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri));
535            }
536        }
537
538        @Override
539        public void onCreateNewContactAction() {
540            Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
541            Bundle extras = getIntent().getExtras();
542            if (extras != null) {
543                intent.putExtras(extras);
544            }
545            startActivity(intent);
546        }
547
548        @Override
549        public void onEditContactAction(Uri contactLookupUri) {
550            Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
551            Bundle extras = getIntent().getExtras();
552            if (extras != null) {
553                intent.putExtras(extras);
554            }
555            startActivityForResult(intent, SUBACTIVITY_EDIT_CONTACT);
556        }
557
558        @Override
559        public void onAddToFavoritesAction(Uri contactUri) {
560            ContentValues values = new ContentValues(1);
561            values.put(Contacts.STARRED, 1);
562            getContentResolver().update(contactUri, values, null, null);
563        }
564
565        @Override
566        public void onRemoveFromFavoritesAction(Uri contactUri) {
567            ContentValues values = new ContentValues(1);
568            values.put(Contacts.STARRED, 0);
569            getContentResolver().update(contactUri, values, null, null);
570        }
571
572        @Override
573        public void onCallContactAction(Uri contactUri) {
574            getPhoneNumberCallInteraction().startInteraction(contactUri);
575        }
576
577        @Override
578        public void onSmsContactAction(Uri contactUri) {
579            getSendTextMessageInteraction().startInteraction(contactUri);
580        }
581
582        @Override
583        public void onDeleteContactAction(Uri contactUri) {
584            ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false);
585        }
586
587        @Override
588        public void onFinishAction() {
589            onBackPressed();
590        }
591
592        @Override
593        public void onInvalidSelection() {
594            ContactListFilter filter;
595            ContactListFilter currentFilter = mListFragment.getFilter();
596            if (currentFilter != null
597                    && currentFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
598                filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
599                mListFragment.setFilter(filter);
600            } else {
601                filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_SINGLE_CONTACT);
602                mListFragment.setFilter(filter, false);
603            }
604            mContactListFilterController.setContactListFilter(filter, true);
605        }
606    }
607
608    private class DetailFragmentListener implements ContactDetailFragment.Listener {
609        @Override
610        public void onContactNotFound() {
611            // Nothing needs to be done here
612        }
613
614        @Override
615        public void onEditRequested(Uri contactLookupUri) {
616            startActivityForResult(
617                    new Intent(Intent.ACTION_EDIT, contactLookupUri), SUBACTIVITY_EDIT_CONTACT);
618        }
619
620        @Override
621        public void onItemClicked(Intent intent) {
622            try {
623                startActivity(intent);
624            } catch (ActivityNotFoundException e) {
625                Log.e(TAG, "No activity found for intent: " + intent);
626            }
627        }
628
629        @Override
630        public void onDeleteRequested(Uri contactUri) {
631            ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false);
632        }
633
634        @Override
635        public void onCreateRawContactRequested(ArrayList<ContentValues> values, Account account) {
636            Toast.makeText(PeopleActivity.this, R.string.toast_making_personal_copy,
637                    Toast.LENGTH_LONG).show();
638            Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
639                    PeopleActivity.this, values, account,
640                    PeopleActivity.class, Intent.ACTION_VIEW);
641            startService(serviceIntent);
642        }
643    }
644
645    private class ContactsUnavailableFragmentListener
646            implements OnContactsUnavailableActionListener {
647
648        @Override
649        public void onCreateNewContactAction() {
650            startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
651        }
652
653        @Override
654        public void onAddAccountAction() {
655            Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
656            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
657            intent.putExtra(Settings.EXTRA_AUTHORITIES,
658                    new String[] { ContactsContract.AUTHORITY });
659            startActivity(intent);
660        }
661
662        @Override
663        public void onImportContactsFromFileAction() {
664            AccountSelectionUtil.doImportFromSdCard(PeopleActivity.this, null);
665        }
666
667        @Override
668        public void onFreeInternalStorageAction() {
669            startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
670        }
671    }
672
673    public void startActivityAndForwardResult(final Intent intent) {
674        intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
675
676        // Forward extras to the new activity
677        Bundle extras = getIntent().getExtras();
678        if (extras != null) {
679            intent.putExtras(extras);
680        }
681        startActivity(intent);
682        finish();
683    }
684
685    @Override
686    public boolean onCreatePanelMenu(int featureId, Menu menu) {
687        // No menu if contacts are unavailable
688        if (!areContactsAvailable()) {
689            return false;
690        }
691
692        return super.onCreatePanelMenu(featureId, menu);
693    }
694
695    @Override
696    public boolean onPreparePanel(int featureId, View view, Menu menu) {
697        // No menu if contacts are unavailable
698        if (!areContactsAvailable()) {
699            return false;
700        }
701
702        return super.onPreparePanel(featureId, view, menu);
703    }
704
705    @Override
706    public boolean onCreateOptionsMenu(Menu menu) {
707        if (!areContactsAvailable()) {
708            return false;
709        }
710        super.onCreateOptionsMenu(menu);
711
712        MenuInflater inflater = getMenuInflater();
713        inflater.inflate(R.menu.actions, menu);
714        // TODO: Figure out if R.menu.list or R.menu.search are necessary according to the overflow
715        // menus on the UX mocks.
716        MenuItem searchMenuItem = menu.findItem(R.id.menu_search);
717        if (searchMenuItem != null && searchMenuItem.getActionView() instanceof SearchView) {
718            SearchView searchView = (SearchView) searchMenuItem.getActionView();
719            searchView.setQueryHint(getString(R.string.hint_findContacts));
720            searchView.setIconifiedByDefault(false);
721
722            if (mActionBarAdapter != null) {
723                mActionBarAdapter.setSearchView(searchView);
724            }
725        }
726
727        // TODO: Can remove this as a custom view because the account selector is in the editor now.
728        // Change add contact button to button with a custom view
729        final MenuItem addContact = menu.findItem(R.id.menu_add);
730        addContact.setActionView(mAddContactImageView);
731        return true;
732    }
733
734    @Override
735    public void invalidateOptionsMenu() {
736        if (isOptionsMenuChanged()) {
737            super.invalidateOptionsMenu();
738        }
739    }
740
741    public boolean isOptionsMenuChanged() {
742        if (mOptionsMenuContactsAvailable != areContactsAvailable()) {
743            return true;
744        }
745
746        if (mOptionsMenuGroupActionsEnabled != areGroupActionsEnabled()) {
747            return true;
748        }
749
750        if (mListFragment != null && mListFragment.isOptionsMenuChanged()) {
751            return true;
752        }
753
754        if (mDetailFragment != null && mDetailFragment.isOptionsMenuChanged()) {
755            return true;
756        }
757
758        return false;
759    }
760
761    @Override
762    public boolean onPrepareOptionsMenu(Menu menu) {
763        mOptionsMenuContactsAvailable = areContactsAvailable();
764        if (!mOptionsMenuContactsAvailable) {
765            return false;
766        }
767
768        MenuItem settings = menu.findItem(R.id.menu_settings);
769        if (settings != null) {
770            settings.setVisible(!ContactsPreferenceActivity.isEmpty(this));
771        }
772
773        mOptionsMenuGroupActionsEnabled = areGroupActionsEnabled();
774
775        MenuItem renameGroup = menu.findItem(R.id.menu_rename_group);
776        if (renameGroup != null) {
777            renameGroup.setVisible(mOptionsMenuGroupActionsEnabled);
778        }
779
780        MenuItem deleteGroup = menu.findItem(R.id.menu_delete_group);
781        if (deleteGroup != null) {
782            deleteGroup.setVisible(mOptionsMenuGroupActionsEnabled);
783        }
784
785        return true;
786    }
787
788    private boolean areGroupActionsEnabled() {
789        boolean groupActionsEnabled = false;
790        if (mListFragment != null) {
791            ContactListFilter filter = mListFragment.getFilter();
792            if (filter != null
793                    && filter.filterType == ContactListFilter.FILTER_TYPE_GROUP
794                    && !filter.groupReadOnly) {
795                groupActionsEnabled = true;
796            }
797        }
798        return groupActionsEnabled;
799    }
800
801    @Override
802    public boolean onOptionsItemSelected(MenuItem item) {
803        switch (item.getItemId()) {
804            case R.id.menu_settings: {
805                final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
806                startActivity(intent);
807                return true;
808            }
809            case R.id.menu_contacts_filter: {
810                final Intent intent = new Intent(this, CustomContactListFilterActivity.class);
811                startActivityForResult(intent, SUBACTIVITY_CUSTOMIZE_FILTER);
812                return true;
813            }
814            case R.id.menu_search: {
815                onSearchRequested();
816                return true;
817            }
818            case R.id.menu_add: {
819                final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
820                startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
821                return true;
822            }
823            case R.id.menu_import_export: {
824                ImportExportDialogFragment.show(getFragmentManager());
825                return true;
826            }
827            case R.id.menu_accounts: {
828                final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
829                intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
830                    ContactsContract.AUTHORITY
831                });
832                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
833                startActivity(intent);
834                return true;
835            }
836            case R.id.menu_rename_group: {
837                ContactListFilter filter = mListFragment.getFilter();
838                GroupRenamingDialogFragment.show(getFragmentManager(), filter.groupId,
839                        filter.title);
840                return true;
841            }
842            case R.id.menu_delete_group: {
843                ContactListFilter filter = mListFragment.getFilter();
844                GroupDeletionDialogFragment.show(getFragmentManager(), filter.groupId,
845                        filter.title);
846                return true;
847            }
848        }
849        return false;
850    }
851
852    @Override
853    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
854            boolean globalSearch) {
855        if (mListFragment != null && mListFragment.isAdded() && !globalSearch) {
856            mListFragment.startSearch(initialQuery);
857        } else {
858            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
859        }
860    }
861
862    @Override
863    protected Dialog onCreateDialog(int id, Bundle bundle) {
864        if (DialogManager.isManagedId(id)) return mDialogManager.onCreateDialog(id, bundle);
865
866        Dialog dialog = getPhoneNumberCallInteraction().onCreateDialog(id, bundle);
867        if (dialog != null) return dialog;
868
869        dialog = getSendTextMessageInteraction().onCreateDialog(id, bundle);
870        if (dialog != null) return dialog;
871
872        return super.onCreateDialog(id, bundle);
873    }
874
875    @Override
876    protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
877        if (getPhoneNumberCallInteraction().onPrepareDialog(id, dialog, bundle)) {
878            return;
879        }
880
881        if (getSendTextMessageInteraction().onPrepareDialog(id, dialog, bundle)) {
882            return;
883        }
884
885        super.onPrepareDialog(id, dialog, bundle);
886    }
887
888    @Override
889    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
890        switch (requestCode) {
891            case SUBACTIVITY_CUSTOMIZE_FILTER: {
892                if (resultCode == Activity.RESULT_OK) {
893                    mContactListFilterController.selectCustomFilter();
894                }
895                break;
896            }
897
898            case SUBACTIVITY_EDIT_CONTACT:
899            case SUBACTIVITY_NEW_CONTACT: {
900                if (resultCode == RESULT_OK && mContactContentDisplayed) {
901                    mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
902                    mListFragment.reloadDataAndSetSelectedUri(data.getData());
903                }
904                break;
905            }
906
907            // TODO: Using the new startActivityWithResultFromFragment API this should not be needed
908            // anymore
909            case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER:
910                if (resultCode == RESULT_OK) {
911                    mListFragment.onPickerResult(data);
912                }
913
914// TODO fix or remove multipicker code
915//                else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) {
916//                    // Finish the activity if the sub activity was canceled as back key is used
917//                    // to confirm user selection in MODE_PICK_MULTIPLE_PHONES.
918//                    finish();
919//                }
920//                break;
921        }
922    }
923
924    @Override
925    public boolean onContextItemSelected(MenuItem item) {
926        ContextMenuAdapter menuAdapter = mListFragment.getContextMenuAdapter();
927        if (menuAdapter != null) {
928            return menuAdapter.onContextItemSelected(item);
929        }
930
931        return super.onContextItemSelected(item);
932    }
933
934    @Override
935    public boolean onKeyDown(int keyCode, KeyEvent event) {
936        // TODO move to the fragment
937        switch (keyCode) {
938//            case KeyEvent.KEYCODE_CALL: {
939//                if (callSelection()) {
940//                    return true;
941//                }
942//                break;
943//            }
944
945            case KeyEvent.KEYCODE_DEL: {
946                if (deleteSelection()) {
947                    return true;
948                }
949                break;
950            }
951            default: {
952                // Bring up the search UI if the user starts typing
953                final int unicodeChar = event.getUnicodeChar();
954                if (unicodeChar != 0 && !Character.isWhitespace(unicodeChar)) {
955                    String query = new String(new int[]{ unicodeChar }, 0, 1);
956                    if (!mActionBarAdapter.isSearchMode()) {
957                        mActionBarAdapter.setQueryString(query);
958                        mActionBarAdapter.setSearchMode(true);
959                        return true;
960                    } else if (!mRequest.isSearchMode()) {
961                        if (!mSearchInitiated) {
962                            mSearchInitiated = true;
963                            startSearch(query, false, null, false);
964                            return true;
965                        }
966                    }
967                }
968            }
969        }
970
971        return super.onKeyDown(keyCode, event);
972    }
973
974    @Override
975    public void onBackPressed() {
976        if (mSearchMode && mActionBarAdapter != null) {
977            mActionBarAdapter.setSearchMode(false);
978        } else {
979            super.onBackPressed();
980        }
981    }
982
983    private boolean deleteSelection() {
984        // TODO move to the fragment
985//        if (mActionCode == ContactsRequest.ACTION_DEFAULT) {
986//            final int position = mListView.getSelectedItemPosition();
987//            if (position != ListView.INVALID_POSITION) {
988//                Uri contactUri = getContactUri(position);
989//                if (contactUri != null) {
990//                    doContactDelete(contactUri);
991//                    return true;
992//                }
993//            }
994//        }
995        return false;
996    }
997
998    @Override
999    protected void onSaveInstanceState(Bundle outState) {
1000        super.onSaveInstanceState(outState);
1001        outState.putBoolean(KEY_SEARCH_MODE, mSearchMode);
1002        if (mActionBarAdapter != null) {
1003            mActionBarAdapter.onSaveInstanceState(outState);
1004        }
1005    }
1006
1007    @Override
1008    protected void onRestoreInstanceState(Bundle inState) {
1009        super.onRestoreInstanceState(inState);
1010        mSearchMode = inState.getBoolean(KEY_SEARCH_MODE);
1011        if (mActionBarAdapter != null) {
1012            mActionBarAdapter.onRestoreInstanceState(inState);
1013        }
1014    }
1015
1016    private PhoneNumberInteraction getPhoneNumberCallInteraction() {
1017        if (mPhoneNumberCallInteraction == null) {
1018            mPhoneNumberCallInteraction = new PhoneNumberInteraction(this, false, null);
1019        }
1020        return mPhoneNumberCallInteraction;
1021    }
1022
1023    private PhoneNumberInteraction getSendTextMessageInteraction() {
1024        if (mSendTextMessageInteraction == null) {
1025            mSendTextMessageInteraction = new PhoneNumberInteraction(this, true, null);
1026        }
1027        return mSendTextMessageInteraction;
1028    }
1029
1030    @Override
1031    public DialogManager getDialogManager() {
1032        return mDialogManager;
1033    }
1034
1035    // Visible for testing
1036    public ContactBrowseListFragment getListFragment() {
1037        return mListFragment;
1038    }
1039
1040    // Visible for testing
1041    public ContactDetailFragment getDetailFragment() {
1042        return mDetailFragment;
1043    }
1044}
1045