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