PeopleActivity.java revision c1eed6d4fe14599fb89644d460c45020057789be
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.activities.ActionBarAdapter.TabState;
24import com.android.contacts.detail.ContactDetailFragment;
25import com.android.contacts.detail.ContactDetailLayoutController;
26import com.android.contacts.detail.ContactDetailTabCarousel;
27import com.android.contacts.detail.ContactDetailUpdatesFragment;
28import com.android.contacts.detail.ContactLoaderFragment;
29import com.android.contacts.detail.ContactLoaderFragment.ContactLoaderFragmentListener;
30import com.android.contacts.group.GroupBrowseListFragment;
31import com.android.contacts.group.GroupBrowseListFragment.OnGroupBrowserActionListener;
32import com.android.contacts.group.GroupDetailFragment;
33import com.android.contacts.interactions.ContactDeletionInteraction;
34import com.android.contacts.interactions.ImportExportDialogFragment;
35import com.android.contacts.interactions.PhoneNumberInteraction;
36import com.android.contacts.list.AccountFilterActivity;
37import com.android.contacts.list.ContactBrowseListFragment;
38import com.android.contacts.list.ContactEntryListFragment;
39import com.android.contacts.list.ContactListFilter;
40import com.android.contacts.list.ContactListFilterController;
41import com.android.contacts.list.ContactTileAdapter.DisplayType;
42import com.android.contacts.list.ContactsIntentResolver;
43import com.android.contacts.list.ContactsRequest;
44import com.android.contacts.list.ContactsUnavailableFragment;
45import com.android.contacts.list.CustomContactListFilterActivity;
46import com.android.contacts.list.DefaultContactBrowseListFragment;
47import com.android.contacts.list.DirectoryListLoader;
48import com.android.contacts.list.OnContactBrowserActionListener;
49import com.android.contacts.list.OnContactsUnavailableActionListener;
50import com.android.contacts.list.ProviderStatusLoader;
51import com.android.contacts.list.ProviderStatusLoader.ProviderStatusListener;
52import com.android.contacts.list.StrequentContactListFragment;
53import com.android.contacts.model.AccountTypeManager;
54import com.android.contacts.preference.ContactsPreferenceActivity;
55import com.android.contacts.util.AccountSelectionUtil;
56import com.android.contacts.util.AccountsListAdapter;
57import com.android.contacts.util.DialogManager;
58import com.android.contacts.util.PhoneCapabilityTester;
59
60import android.accounts.Account;
61import android.app.Activity;
62import android.app.Fragment;
63import android.app.FragmentManager;
64import android.app.FragmentTransaction;
65import android.content.ActivityNotFoundException;
66import android.content.ContentValues;
67import android.content.Intent;
68import android.net.Uri;
69import android.os.Bundle;
70import android.os.Handler;
71import android.os.Parcelable;
72import android.provider.ContactsContract;
73import android.provider.ContactsContract.Contacts;
74import android.provider.ContactsContract.Intents;
75import android.provider.ContactsContract.ProviderStatus;
76import android.provider.Settings;
77import android.support.v13.app.FragmentPagerAdapter;
78import android.support.v4.view.PagerAdapter;
79import android.support.v4.view.ViewPager;
80import android.util.Log;
81import android.view.KeyEvent;
82import android.view.Menu;
83import android.view.MenuInflater;
84import android.view.MenuItem;
85import android.view.View;
86import android.view.View.OnClickListener;
87import android.widget.AdapterView;
88import android.widget.AdapterView.OnItemClickListener;
89import android.widget.ListPopupWindow;
90import android.widget.Toast;
91
92import java.util.ArrayList;
93
94/**
95 * Displays a list to browse contacts. For xlarge screens, this also displays a detail-pane on
96 * the right.
97 */
98public class PeopleActivity extends ContactsActivity
99        implements View.OnCreateContextMenuListener, ActionBarAdapter.Listener,
100        DialogManager.DialogShowingViewActivity,
101        ContactListFilterController.ContactListFilterListener, ProviderStatusListener {
102
103    private static final String TAG = "PeopleActivity";
104
105    private static final int SUBACTIVITY_NEW_CONTACT = 2;
106    private static final int SUBACTIVITY_EDIT_CONTACT = 3;
107    private static final int SUBACTIVITY_NEW_GROUP = 4;
108    private static final int SUBACTIVITY_EDIT_GROUP = 5;
109    private static final int SUBACTIVITY_ACCOUNT_FILTER = 6;
110    private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 7;
111
112    private static final String KEY_SEARCH_MODE = "searchMode";
113
114    private DialogManager mDialogManager = new DialogManager(this);
115
116    private ContactsIntentResolver mIntentResolver;
117    private ContactsRequest mRequest;
118
119    private ActionBarAdapter mActionBarAdapter;
120
121    private boolean mSearchMode;
122
123    private ContactDetailFragment mContactDetailFragment;
124    private ContactDetailUpdatesFragment mContactDetailUpdatesFragment;
125    private final ContactDetailFragmentListener mContactDetailFragmentListener =
126            new ContactDetailFragmentListener();
127
128    private ContactLoaderFragment mContactDetailLoaderFragment;
129    private final ContactDetailLoaderFragmentListener mContactDetailLoaderFragmentListener =
130            new ContactDetailLoaderFragmentListener();
131
132    private GroupDetailFragment mGroupDetailFragment;
133    private final GroupDetailFragmentListener mGroupDetailFragmentListener =
134            new GroupDetailFragmentListener();
135
136    private StrequentContactListFragment.Listener mFavoritesFragmentListener =
137            new StrequentContactListFragmentListener();
138
139    private boolean mSearchInitiated;
140
141    private ContactListFilterController mContactListFilterController;
142
143    private ContactsUnavailableFragment mContactsUnavailableFragment;
144    private ProviderStatusLoader mProviderStatusLoader;
145    private int mProviderStatus = -1;
146
147    private boolean mOptionsMenuContactsAvailable;
148
149    /**
150     * Showing a list of Contacts. Also used for showing search results in search mode.
151     */
152    private DefaultContactBrowseListFragment mAllFragment;
153    private StrequentContactListFragment mFavoritesFragment;
154    private StrequentContactListFragment mFrequentFragment;
155    private GroupBrowseListFragment mGroupsFragment;
156
157    private View mFavoritesView;
158    private View mBrowserView;
159    private View mDetailsView;
160
161    private View mAddGroupImageView;
162
163    /** ViewPager for swipe, used only on the phone (i.e. one-pane mode) */
164    private ViewPager mTabPager;
165    private TabPagerAdapter mTabPagerAdapter;
166
167    private ContactDetailLayoutController mContactDetailLayoutController;
168
169    private final Handler mHandler = new Handler();
170
171    public PeopleActivity() {
172        mIntentResolver = new ContactsIntentResolver(this);
173        mContactListFilterController = new ContactListFilterController(this);
174        mContactListFilterController.addListener(this);
175        mProviderStatusLoader = new ProviderStatusLoader(this);
176    }
177
178    public boolean areContactsAvailable() {
179        return mProviderStatus == ProviderStatus.STATUS_NORMAL;
180    }
181
182    /**
183     * Initialize fragments that are (or may not be) in the layout.
184     *
185     * For the fragments that are in the layout, we initialize them in
186     * {@link #configureContentView(boolean, Bundle)} after inflating the layout.
187     *
188     * However, there are special fragments which may not be in the layout, so we have to do the
189     * initialization here.
190     * The target fragments are:
191     * - {@link ContactDetailFragment} and {@link ContactDetailUpdatesFragment}:  They may not be
192     *   in the layout depending on the configuration.  (i.e. portrait)
193     * - {@link ContactsUnavailableFragment}: We always create it at runtime.
194     */
195    @Override
196    public void onAttachFragment(Fragment fragment) {
197        if (fragment instanceof ContactDetailFragment) {
198            mContactDetailFragment = (ContactDetailFragment) fragment;
199            mContactDetailFragment.setListener(mContactDetailFragmentListener);
200        } else if (fragment instanceof ContactDetailUpdatesFragment) {
201            mContactDetailUpdatesFragment = (ContactDetailUpdatesFragment) fragment;
202        } else if (fragment instanceof ContactsUnavailableFragment) {
203            mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment;
204            mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader);
205            mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
206                    new ContactsUnavailableFragmentListener());
207        }
208    }
209
210    @Override
211    protected void onCreate(Bundle savedState) {
212        super.onCreate(savedState);
213
214        configureContentView(true, savedState);
215    }
216
217    @Override
218    protected void onNewIntent(Intent intent) {
219        setIntent(intent);
220        configureContentView(false, null);
221    }
222
223    private void configureContentView(boolean createContentView, Bundle savedState) {
224        // Extract relevant information from the intent
225        mRequest = mIntentResolver.resolveIntent(getIntent());
226        if (!mRequest.isValid()) {
227            setResult(RESULT_CANCELED);
228            finish();
229            return;
230        }
231
232        Intent redirect = mRequest.getRedirectIntent();
233        if (redirect != null) {
234            // Need to start a different activity
235            startActivity(redirect);
236            finish();
237            return;
238        }
239
240        if (mRequest.getActionCode() == ContactsRequest.ACTION_VIEW_CONTACT
241                && !PhoneCapabilityTester.isUsingTwoPanes(this)) {
242            redirect = new Intent(this, ContactDetailActivity.class);
243            redirect.setAction(Intent.ACTION_VIEW);
244            redirect.setData(mRequest.getContactUri());
245            startActivity(redirect);
246            finish();
247            return;
248        }
249
250        if (createContentView) {
251            setContentView(R.layout.people_activity);
252
253            final FragmentManager fragmentManager = getFragmentManager();
254
255            // Hide all tabs (the current tab will later be reshown once a tab is selected)
256            final FragmentTransaction transaction = fragmentManager.beginTransaction();
257
258            // Prepare the fragments which are used both on 1-pane and on 2-pane.
259            if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
260                mFavoritesFragment = getFragment(R.id.favorites_fragment);
261                mAllFragment = getFragment(R.id.all_fragment);
262                mGroupsFragment = getFragment(R.id.groups_fragment);
263            } else {
264                mTabPager = getView(R.id.tab_pager);
265                mTabPagerAdapter = new TabPagerAdapter();
266                mTabPager.setAdapter(mTabPagerAdapter);
267                mTabPager.setOnPageChangeListener(new TabPagerListener());
268
269                final String FAVORITE_TAG = "tab-pager-favorite";
270                final String ALL_TAG = "tab-pager-all";
271                final String GROUPS_TAG = "tab-pager-groups";
272
273                // Create the fragments and add as children of the view pager.
274                // The pager adapter will only change the visibility; it'll never create/destroy
275                // fragments.
276                // However, if it's after screen rotation, the fragments have been re-created by
277                // the fragment manager, so first see if there're already the target fragments
278                // existing.
279                mFavoritesFragment = (StrequentContactListFragment)
280                        fragmentManager.findFragmentByTag(FAVORITE_TAG);
281                mAllFragment = (DefaultContactBrowseListFragment)
282                        fragmentManager.findFragmentByTag(ALL_TAG);
283                mGroupsFragment = (GroupBrowseListFragment)
284                        fragmentManager.findFragmentByTag(GROUPS_TAG);
285
286                if (mFavoritesFragment == null) {
287                    mFavoritesFragment = new StrequentContactListFragment();
288                    mAllFragment = new DefaultContactBrowseListFragment();
289                    mGroupsFragment = new GroupBrowseListFragment();
290
291                    transaction.add(R.id.tab_pager, mFavoritesFragment, FAVORITE_TAG);
292                    transaction.add(R.id.tab_pager, mAllFragment, ALL_TAG);
293                    transaction.add(R.id.tab_pager, mGroupsFragment, GROUPS_TAG);
294                }
295            }
296
297            mFavoritesFragment.setListener(mFavoritesFragmentListener);
298
299            mAllFragment.setOnContactListActionListener(new ContactBrowserActionListener());
300
301            mGroupsFragment.setListener(new GroupBrowserActionListener());
302
303            // Hide all fragments for now.  We adjust visibility when we get onSelectedTabChanged()
304            // from ActionBarAdapter.
305            transaction.hide(mFavoritesFragment);
306            transaction.hide(mAllFragment);
307            transaction.hide(mGroupsFragment);
308
309            if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
310                // Prepare 2-pane only fragments/views...
311
312                // Container views for fragments
313                mFavoritesView = getView(R.id.favorites_view);
314                mDetailsView = getView(R.id.details_view);
315                mBrowserView = getView(R.id.browse_view);
316
317                // 2-pane only fragments
318                mFrequentFragment = getFragment(R.id.frequent_fragment);
319                mFrequentFragment.setListener(mFavoritesFragmentListener);
320                mFrequentFragment.setDisplayType(DisplayType.FREQUENT_ONLY);
321                mFrequentFragment.setQuickContact(true);
322
323                mContactDetailLoaderFragment = getFragment(R.id.contact_detail_loader_fragment);
324                mContactDetailLoaderFragment.setListener(mContactDetailLoaderFragmentListener);
325
326                mGroupDetailFragment = getFragment(R.id.group_detail_fragment);
327                mGroupDetailFragment.setListener(mGroupDetailFragmentListener);
328                mGroupDetailFragment.setQuickContact(true);
329
330                transaction.hide(mContactDetailFragment);
331                transaction.hide(mGroupDetailFragment);
332            }
333            transaction.commit();
334            fragmentManager.executePendingTransactions();
335
336            // These operations below are only okay if the fragment is already created.
337            // Because we create the fragment dynamically on 1-pane, this has to be done after
338            // the fragment transaction is executed.
339            mFavoritesFragment.setQuickContact(PhoneCapabilityTester.isUsingTwoPanes(this));
340            mFavoritesFragment.setDisplayType(DisplayType.STARRED_ONLY);
341        }
342
343        setTitle(mRequest.getActivityTitle());
344        if (createContentView) {
345            mActionBarAdapter = new ActionBarAdapter(this, this, getActionBar());
346        }
347        mActionBarAdapter.initialize(savedState, mRequest);
348
349
350        ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
351        ContactDetailTabCarousel tabCarousel = (ContactDetailTabCarousel)
352                findViewById(R.id.tab_carousel);
353        mContactDetailLayoutController = new ContactDetailLayoutController(
354                getFragmentManager(), viewPager, tabCarousel,
355                mContactDetailFragmentListener);
356
357        if (createContentView) {
358            // TODO Is the createContentView test really necessary?
359            invalidateOptionsMenuIfNeeded();
360        }
361
362        configureFragments(savedState == null);
363    }
364
365    @Override
366    protected void onPause() {
367        mOptionsMenuContactsAvailable = false;
368
369        mProviderStatus = -1;
370        mProviderStatusLoader.setProviderStatusListener(null);
371        super.onPause();
372    }
373
374    @Override
375    protected void onResume() {
376        super.onResume();
377        mProviderStatusLoader.setProviderStatusListener(this);
378        showContactsUnavailableFragmentIfNecessary();
379
380        // Re-register the listener, which may have been cleared when onSaveInstanceState was
381        // called.  See also: onSaveInstanceState
382        mActionBarAdapter.setListener(this);
383    }
384
385    @Override
386    protected void onStart() {
387        mContactListFilterController.onStart();
388        super.onStart();
389    }
390
391    @Override
392    protected void onDestroy() {
393        // mActionBarAdapter will be null here when redirecting to another activity in
394        // configureContentView().
395        if (mActionBarAdapter != null) {
396            mActionBarAdapter.setListener(null);
397        }
398        super.onDestroy();
399    }
400
401    private void configureFragments(boolean fromRequest) {
402        if (fromRequest) {
403            ContactListFilter filter = null;
404            int actionCode = mRequest.getActionCode();
405            switch (actionCode) {
406                case ContactsRequest.ACTION_ALL_CONTACTS:
407                    filter = ContactListFilter.createFilterWithType(
408                            ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
409                    break;
410                case ContactsRequest.ACTION_CONTACTS_WITH_PHONES:
411                    filter = ContactListFilter.createFilterWithType(
412                            ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY);
413                    break;
414
415                // TODO: handle FREQUENT and STREQUENT according to the spec
416                case ContactsRequest.ACTION_FREQUENT:
417                case ContactsRequest.ACTION_STREQUENT:
418                    // For now they are treated the same as STARRED
419                case ContactsRequest.ACTION_STARRED:
420                    filter = ContactListFilter.createFilterWithType(
421                            ContactListFilter.FILTER_TYPE_STARRED);
422                    break;
423                case ContactsRequest.ACTION_VIEW_CONTACT:
424                    if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
425                        mActionBarAdapter.setCurrentTab(TabState.ALL);
426                    }
427            }
428
429            mSearchMode = mRequest.isSearchMode();
430            if (filter != null) {
431                mContactListFilterController.setContactListFilter(filter, false);
432                mSearchMode = false;
433            } else if (mRequest.getActionCode() == ContactsRequest.ACTION_ALL_CONTACTS) {
434                mContactListFilterController.setContactListFilter(
435                        ContactListFilter.createFilterWithType(
436                        ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), false);
437            }
438
439            if (mRequest.getContactUri() != null) {
440                mSearchMode = false;
441            }
442
443            mAllFragment.setContactsRequest(mRequest);
444            configureContactListFragmentForRequest();
445
446        } else {
447            mSearchMode = mActionBarAdapter.isSearchMode();
448        }
449
450        configureContactListFragment();
451        configureGroupListFragment();
452
453        invalidateOptionsMenuIfNeeded();
454    }
455
456    @Override
457    public void onContactListFilterChanged() {
458        if (mAllFragment == null || !mAllFragment.isAdded()) {
459            return;
460        }
461
462        mAllFragment.setFilter(mContactListFilterController.getFilter());
463
464        invalidateOptionsMenuIfNeeded();
465    }
466
467    private void setupContactDetailFragment(final Uri contactLookupUri) {
468        mContactDetailLoaderFragment.loadUri(contactLookupUri);
469        invalidateOptionsMenuIfNeeded();
470    }
471
472    private void setupGroupDetailFragment(Uri groupUri) {
473        mGroupDetailFragment.loadGroup(groupUri);
474        invalidateOptionsMenuIfNeeded();
475    }
476
477    /**
478     * Handler for action bar actions.
479     */
480    @Override
481    public void onAction(Action action) {
482        switch (action) {
483            case START_SEARCH_MODE:
484                clearSearch();
485                updateFragmentsVisibility();
486                invalidateOptionsMenu();
487                break;
488            case STOP_SEARCH_MODE:
489                clearSearch();
490                updateFragmentsVisibility();
491                invalidateOptionsMenu();
492                break;
493            case CHANGE_SEARCH_QUERY:
494                loadSearch(mActionBarAdapter.getQueryString());
495                break;
496            default:
497                throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action);
498        }
499    }
500
501    @Override
502    public void onSelectedTabChanged() {
503        updateFragmentsVisibility();
504    }
505
506    /**
507     * Updates the fragment/view visibility according to the current mode, such as
508     * {@link ActionBarAdapter#isSearchMode()} and {@link ActionBarAdapter#getCurrentTab()}.
509     */
510    private void updateFragmentsVisibility() {
511        TabState tab = mActionBarAdapter.getCurrentTab();
512
513        // We use ViewPager on 1-pane.
514        if (!PhoneCapabilityTester.isUsingTwoPanes(this)) {
515            if (mActionBarAdapter.isSearchMode()) {
516                mTabPagerAdapter.setSearchMode(true);
517            } else {
518                mTabPagerAdapter.setSearchMode(false);
519                int tabIndex = tab.ordinal();
520                if (mTabPager.getCurrentItem() != tabIndex) {
521                    mTabPager.setCurrentItem(tab.ordinal(), false /* no smooth scroll */);
522                }
523            }
524            invalidateOptionsMenu();
525            return;
526        }
527
528        // for the tablet...
529
530        // If in search mode, we use the all list + contact details to show the result.
531        if (mActionBarAdapter.isSearchMode()) {
532            tab = TabState.ALL;
533        }
534        switch (tab) {
535            case FAVORITES:
536                mFavoritesView.setVisibility(View.VISIBLE);
537                mBrowserView.setVisibility(View.GONE);
538                mDetailsView.setVisibility(View.GONE);
539                break;
540            case GROUPS:
541            case ALL:
542                mFavoritesView.setVisibility(View.GONE);
543                mBrowserView.setVisibility(View.VISIBLE);
544                mDetailsView.setVisibility(View.VISIBLE);
545                break;
546        }
547        FragmentManager fragmentManager = getFragmentManager();
548        FragmentTransaction ft = fragmentManager.beginTransaction();
549
550        switch (tab) {
551            case FAVORITES:
552                showFragment(ft, mFavoritesFragment);
553                showFragment(ft, mFrequentFragment);
554                hideFragment(ft, mAllFragment);
555                hideFragment(ft, mContactDetailFragment);
556                hideFragment(ft, mGroupsFragment);
557                hideFragment(ft, mGroupDetailFragment);
558                break;
559            case ALL:
560                hideFragment(ft, mFavoritesFragment);
561                hideFragment(ft, mFrequentFragment);
562                showFragment(ft, mAllFragment);
563                showFragment(ft, mContactDetailFragment);
564                hideFragment(ft, mGroupsFragment);
565                hideFragment(ft, mGroupDetailFragment);
566                break;
567            case GROUPS:
568                hideFragment(ft, mFavoritesFragment);
569                hideFragment(ft, mFrequentFragment);
570                hideFragment(ft, mAllFragment);
571                hideFragment(ft, mContactDetailFragment);
572                showFragment(ft, mGroupsFragment);
573                showFragment(ft, mGroupDetailFragment);
574                break;
575        }
576        if (!ft.isEmpty()) {
577            ft.commit();
578            fragmentManager.executePendingTransactions();
579        }
580        invalidateOptionsMenu();
581    }
582
583    private class TabPagerListener implements ViewPager.OnPageChangeListener {
584        @Override
585        public void onPageScrollStateChanged(int state) {
586        }
587
588        @Override
589        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
590        }
591
592        @Override
593        public void onPageSelected(int position) {
594            // Make sure not in the search mode, in which case position != TabState.ordinal().
595            if (!mTabPagerAdapter.isSearchMode()) {
596                mActionBarAdapter.setCurrentTab(TabState.fromInt(position), false);
597                invalidateOptionsMenu();
598            }
599        }
600    }
601
602    /**
603     * Adapter for the {@link ViewPager}.  Unlike {@link FragmentPagerAdapter},
604     * {@link #instantiateItem} returns existing fragments, and {@link #instantiateItem}/
605     * {@link #destroyItem} show/hide fragments instead of attaching/detaching.
606     *
607     * In search mode, we always show the "all" fragment, and disable the swipe.  We change the
608     * number of items to 1 to disable the swipe.
609     *
610     * TODO figure out a more straight way to disable swipe.
611     */
612    private class TabPagerAdapter extends PagerAdapter {
613        private final FragmentManager mFragmentManager;
614        private FragmentTransaction mCurTransaction = null;
615
616        private boolean mTabPagerAdapterSearchMode;
617
618        public TabPagerAdapter() {
619            mFragmentManager = getFragmentManager();
620        }
621
622        public boolean isSearchMode() {
623            return mTabPagerAdapterSearchMode;
624        }
625
626        public void setSearchMode(boolean searchMode) {
627            if (searchMode == mTabPagerAdapterSearchMode) {
628                return;
629            }
630            mTabPagerAdapterSearchMode = searchMode;
631            notifyDataSetChanged();
632        }
633
634        @Override
635        public int getCount() {
636            return mTabPagerAdapterSearchMode ? 1 : TabState.values().length;
637        }
638
639        /** Gets called when the number of items changes. */
640        @Override
641        public int getItemPosition(Object object) {
642            if (mTabPagerAdapterSearchMode) {
643                if (object == mAllFragment) {
644                    return 0; // Only 1 page in search mode
645                }
646            } else {
647                if (object == mFavoritesFragment) {
648                    return TabState.FAVORITES.ordinal();
649                }
650                if (object == mAllFragment) {
651                    return TabState.ALL.ordinal();
652                }
653                if (object == mGroupsFragment) {
654                    return TabState.GROUPS.ordinal();
655                }
656            }
657            return POSITION_NONE;
658        }
659
660        @Override
661        public void startUpdate(View container) {
662        }
663
664        private Fragment getFragment(int position) {
665            if (mTabPagerAdapterSearchMode) {
666                if (position == 0) {
667                    return mAllFragment;
668                }
669            } else {
670                if (position == TabState.FAVORITES.ordinal()) {
671                    return mFavoritesFragment;
672                } else if (position == TabState.ALL.ordinal()) {
673                    return mAllFragment;
674                } else if (position == TabState.GROUPS.ordinal()) {
675                    return mGroupsFragment;
676                }
677            }
678            throw new IllegalArgumentException("position: " + position);
679        }
680
681        @Override
682        public Object instantiateItem(View container, int position) {
683            if (mCurTransaction == null) {
684                mCurTransaction = mFragmentManager.beginTransaction();
685            }
686            Fragment f = getFragment(position);
687            mCurTransaction.show(f);
688            return f;
689        }
690
691        @Override
692        public void destroyItem(View container, int position, Object object) {
693            if (mCurTransaction == null) {
694                mCurTransaction = mFragmentManager.beginTransaction();
695            }
696            mCurTransaction.hide((Fragment) object);
697        }
698
699        @Override
700        public void finishUpdate(View container) {
701            if (mCurTransaction != null) {
702                mCurTransaction.commit();
703                mCurTransaction = null;
704                mFragmentManager.executePendingTransactions();
705            }
706        }
707
708        @Override
709        public boolean isViewFromObject(View view, Object object) {
710            return ((Fragment) object).getView() == view;
711        }
712
713        @Override
714        public Parcelable saveState() {
715            return null;
716        }
717
718        @Override
719        public void restoreState(Parcelable state, ClassLoader loader) {
720        }
721    }
722
723    private void clearSearch() {
724        loadSearch("");
725    }
726
727    private void loadSearch(String query) {
728        configureFragments(false /* from request */);
729        mAllFragment.setQueryString(query, true);
730    }
731
732    private void configureContactListFragmentForRequest() {
733        Uri contactUri = mRequest.getContactUri();
734        if (contactUri != null) {
735            mAllFragment.setSelectedContactUri(contactUri);
736        }
737
738        mAllFragment.setSearchMode(mRequest.isSearchMode());
739        mAllFragment.setQueryString(mRequest.getQueryString(), false);
740
741        if (mRequest.isDirectorySearchEnabled()) {
742            mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT);
743        } else {
744            mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
745        }
746
747        if (mContactListFilterController.isInitialized()) {
748            mAllFragment.setFilter(mContactListFilterController.getFilter());
749        }
750    }
751
752    private void configureContactListFragment() {
753        mAllFragment.setSearchMode(mSearchMode);
754
755        final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this);
756        mAllFragment.setVisibleScrollbarEnabled(!mSearchMode);
757        mAllFragment.setVerticalScrollbarPosition(
758                useTwoPane
759                        ? View.SCROLLBAR_POSITION_LEFT
760                        : View.SCROLLBAR_POSITION_RIGHT);
761        mAllFragment.setSelectionVisible(useTwoPane);
762        mAllFragment.setQuickContactEnabled(!useTwoPane);
763    }
764
765    private void configureGroupListFragment() {
766        final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this);
767        mGroupsFragment.setVerticalScrollbarPosition(
768                useTwoPane
769                        ? View.SCROLLBAR_POSITION_LEFT
770                        : View.SCROLLBAR_POSITION_RIGHT);
771        mGroupsFragment.setSelectionVisible(useTwoPane);
772    }
773
774    @Override
775    public void onProviderStatusChange() {
776        showContactsUnavailableFragmentIfNecessary();
777    }
778
779    private void showContactsUnavailableFragmentIfNecessary() {
780        int providerStatus = mProviderStatusLoader.getProviderStatus();
781        if (providerStatus == mProviderStatus) {
782            return;
783        }
784
785        mProviderStatus = providerStatus;
786
787        View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view);
788        View mainView = findViewById(R.id.main_view);
789
790        if (mProviderStatus == ProviderStatus.STATUS_NORMAL) {
791            contactsUnavailableView.setVisibility(View.GONE);
792            if (mainView != null) {
793                mainView.setVisibility(View.VISIBLE);
794            }
795            if (mAllFragment != null) {
796                mAllFragment.setEnabled(true);
797            }
798        } else {
799            if (mAllFragment != null) {
800                mAllFragment.setEnabled(false);
801            }
802            if (mContactsUnavailableFragment == null) {
803                mContactsUnavailableFragment = new ContactsUnavailableFragment();
804                mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader);
805                mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
806                        new ContactsUnavailableFragmentListener());
807                getFragmentManager().beginTransaction()
808                        .replace(R.id.contacts_unavailable_container, mContactsUnavailableFragment)
809                        .commit();
810            } else {
811                mContactsUnavailableFragment.update();
812            }
813            contactsUnavailableView.setVisibility(View.VISIBLE);
814            if (mainView != null) {
815                mainView.setVisibility(View.INVISIBLE);
816            }
817        }
818
819        invalidateOptionsMenuIfNeeded();
820    }
821
822    private final class ContactBrowserActionListener implements OnContactBrowserActionListener {
823
824        @Override
825        public void onSelectionChange() {
826            if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
827                setupContactDetailFragment(mAllFragment.getSelectedContactUri());
828            }
829        }
830
831        @Override
832        public void onViewContactAction(Uri contactLookupUri) {
833            if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
834                setupContactDetailFragment(contactLookupUri);
835            } else {
836                startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri));
837            }
838        }
839
840        @Override
841        public void onCreateNewContactAction() {
842            Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
843            Bundle extras = getIntent().getExtras();
844            if (extras != null) {
845                intent.putExtras(extras);
846            }
847            startActivity(intent);
848        }
849
850        @Override
851        public void onEditContactAction(Uri contactLookupUri) {
852            Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
853            Bundle extras = getIntent().getExtras();
854            if (extras != null) {
855                intent.putExtras(extras);
856            }
857            startActivityForResult(intent, SUBACTIVITY_EDIT_CONTACT);
858        }
859
860        @Override
861        public void onAddToFavoritesAction(Uri contactUri) {
862            ContentValues values = new ContentValues(1);
863            values.put(Contacts.STARRED, 1);
864            getContentResolver().update(contactUri, values, null, null);
865        }
866
867        @Override
868        public void onRemoveFromFavoritesAction(Uri contactUri) {
869            ContentValues values = new ContentValues(1);
870            values.put(Contacts.STARRED, 0);
871            getContentResolver().update(contactUri, values, null, null);
872        }
873
874        @Override
875        public void onCallContactAction(Uri contactUri) {
876            PhoneNumberInteraction.startInteractionForPhoneCall(PeopleActivity.this, contactUri);
877        }
878
879        @Override
880        public void onSmsContactAction(Uri contactUri) {
881            PhoneNumberInteraction.startInteractionForTextMessage(PeopleActivity.this, contactUri);
882        }
883
884        @Override
885        public void onDeleteContactAction(Uri contactUri) {
886            ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false);
887        }
888
889        @Override
890        public void onFinishAction() {
891            onBackPressed();
892        }
893
894        @Override
895        public void onInvalidSelection() {
896            ContactListFilter filter;
897            ContactListFilter currentFilter = mAllFragment.getFilter();
898            if (currentFilter != null
899                    && currentFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
900                filter = ContactListFilter.createFilterWithType(
901                        ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
902                mAllFragment.setFilter(filter);
903            } else {
904                filter = ContactListFilter.createFilterWithType(
905                        ContactListFilter.FILTER_TYPE_SINGLE_CONTACT);
906                mAllFragment.setFilter(filter, false);
907            }
908            mContactListFilterController.setContactListFilter(filter, true);
909        }
910    }
911
912    private class ContactDetailLoaderFragmentListener implements ContactLoaderFragmentListener {
913        @Override
914        public void onContactNotFound() {
915            // Nothing needs to be done here
916        }
917
918        @Override
919        public void onDetailsLoaded(final ContactLoader.Result result) {
920            if (result == null) {
921                return;
922            }
923            // Since {@link FragmentTransaction}s cannot be done in the onLoadFinished() of the
924            // {@link LoaderCallbacks}, then post this {@link Runnable} to the {@link Handler}
925            // on the main thread to execute later.
926            mHandler.post(new Runnable() {
927                @Override
928                public void run() {
929                    if (!mContactDetailLayoutController.isInitialized()) {
930                        mContactDetailLayoutController.setContactDetailFragment(
931                                mContactDetailFragment);
932                        mContactDetailLayoutController.setContactDetailUpdatesFragment(
933                                mContactDetailUpdatesFragment);
934                        mContactDetailLayoutController.initialize();
935                    }
936                    mContactDetailLayoutController.setContactData(result);
937                }
938            });
939        }
940
941        @Override
942        public void onEditRequested(Uri contactLookupUri) {
943            startActivityForResult(
944                    new Intent(Intent.ACTION_EDIT, contactLookupUri), SUBACTIVITY_EDIT_CONTACT);
945        }
946
947        @Override
948        public void onDeleteRequested(Uri contactUri) {
949            ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false);
950        }
951    }
952
953    public class ContactDetailFragmentListener implements ContactDetailFragment.Listener {
954        @Override
955        public void onItemClicked(Intent intent) {
956            try {
957                startActivity(intent);
958            } catch (ActivityNotFoundException e) {
959                Log.e(TAG, "No activity found for intent: " + intent);
960            }
961        }
962
963        @Override
964        public void onCreateRawContactRequested(ArrayList<ContentValues> values, Account account) {
965            Toast.makeText(PeopleActivity.this, R.string.toast_making_personal_copy,
966                    Toast.LENGTH_LONG).show();
967            Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
968                    PeopleActivity.this, values, account,
969                    PeopleActivity.class, Intent.ACTION_VIEW);
970            startService(serviceIntent);
971        }
972    }
973
974    private class ContactsUnavailableFragmentListener
975            implements OnContactsUnavailableActionListener {
976
977        @Override
978        public void onCreateNewContactAction() {
979            startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
980        }
981
982        @Override
983        public void onAddAccountAction() {
984            Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
985            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
986            intent.putExtra(Settings.EXTRA_AUTHORITIES,
987                    new String[] { ContactsContract.AUTHORITY });
988            startActivity(intent);
989        }
990
991        @Override
992        public void onImportContactsFromFileAction() {
993            AccountSelectionUtil.doImportFromSdCard(PeopleActivity.this, null);
994        }
995
996        @Override
997        public void onFreeInternalStorageAction() {
998            startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
999        }
1000    }
1001
1002    private final class StrequentContactListFragmentListener
1003            implements StrequentContactListFragment.Listener {
1004        @Override
1005        public void onContactSelected(Uri contactUri) {
1006            if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
1007                setupContactDetailFragment(contactUri);
1008            } else {
1009                startActivity(new Intent(Intent.ACTION_VIEW, contactUri));
1010            }
1011        }
1012    }
1013
1014    private final class GroupBrowserActionListener implements OnGroupBrowserActionListener {
1015
1016        @Override
1017        public void onViewGroupAction(Uri groupUri) {
1018            if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
1019                setupGroupDetailFragment(groupUri);
1020            } else {
1021                Intent intent = new Intent(PeopleActivity.this, GroupDetailActivity.class);
1022                intent.setData(groupUri);
1023                startActivity(intent);
1024            }
1025        }
1026    }
1027
1028    private class GroupDetailFragmentListener implements GroupDetailFragment.Listener {
1029        @Override
1030        public void onGroupSizeUpdated(String size) {
1031            // Nothing needs to be done here because the size will be displayed in the detail
1032            // fragment
1033        }
1034
1035        @Override
1036        public void onGroupTitleUpdated(String title) {
1037            // Nothing needs to be done here because the title will be displayed in the detail
1038            // fragment
1039        }
1040
1041        @Override
1042        public void onEditRequested(Uri groupUri) {
1043            final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class);
1044            intent.setData(groupUri);
1045            intent.setAction(Intent.ACTION_EDIT);
1046            startActivityForResult(intent, SUBACTIVITY_EDIT_GROUP);
1047        }
1048
1049        @Override
1050        public void onContactSelected(Uri contactUri) {
1051            // Nothing needs to be done here because either quickcontact will be displayed
1052            // or activity will take care of selection
1053        }
1054    }
1055
1056    public void startActivityAndForwardResult(final Intent intent) {
1057        intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
1058
1059        // Forward extras to the new activity
1060        Bundle extras = getIntent().getExtras();
1061        if (extras != null) {
1062            intent.putExtras(extras);
1063        }
1064        startActivity(intent);
1065        finish();
1066    }
1067
1068    @Override
1069    public boolean onCreateOptionsMenu(Menu menu) {
1070//      STOPSHIP Un-comment it once b/5027071 is fixed.
1071//        if (!areContactsAvailable()) {
1072//            If contacts aren't available, hide all menu items.
1073//            return false;
1074//        }
1075        super.onCreateOptionsMenu(menu);
1076
1077        MenuInflater inflater = getMenuInflater();
1078        inflater.inflate(R.menu.actions, menu);
1079
1080        // On narrow screens we specify a NEW group button in the {@link ActionBar}, so that
1081        // it can be in the overflow menu. On wide screens, we use a custom view because we need
1082        // its location for anchoring the account-selector popup.
1083        final MenuItem addGroup = menu.findItem(R.id.menu_custom_add_group);
1084        if (addGroup != null) {
1085            mAddGroupImageView = getLayoutInflater().inflate(
1086                    R.layout.add_group_menu_item, null, false);
1087            View item = mAddGroupImageView.findViewById(R.id.menu_item);
1088            item.setOnClickListener(new OnClickListener() {
1089                @Override
1090                public void onClick(View v) {
1091                    createNewGroupWithAccountDisambiguation();
1092                }
1093            });
1094            addGroup.setActionView(mAddGroupImageView);
1095        }
1096        return true;
1097    }
1098
1099    private void invalidateOptionsMenuIfNeeded() {
1100        if (isOptionsMenuChanged()) {
1101            invalidateOptionsMenu();
1102        }
1103    }
1104
1105    public boolean isOptionsMenuChanged() {
1106        if (mOptionsMenuContactsAvailable != areContactsAvailable()) {
1107            return true;
1108        }
1109
1110        if (mAllFragment != null && mAllFragment.isOptionsMenuChanged()) {
1111            return true;
1112        }
1113
1114        if (mContactDetailLoaderFragment != null &&
1115                mContactDetailLoaderFragment.isOptionsMenuChanged()) {
1116            return true;
1117        }
1118
1119        if (mGroupDetailFragment != null && mGroupDetailFragment.isOptionsMenuChanged()) {
1120            return true;
1121        }
1122
1123        return false;
1124    }
1125
1126    @Override
1127    public boolean onPrepareOptionsMenu(Menu menu) {
1128        mOptionsMenuContactsAvailable = areContactsAvailable();
1129        if (!mOptionsMenuContactsAvailable) {
1130            // STOPSHIP Remove makeAllMenuItemsVisible()when STOPSHIP in onCreateOptionsMenu() is
1131            // fixed.
1132            makeAllMenuItemsVisible(menu, false);
1133            return false;
1134        }
1135        makeAllMenuItemsVisible(menu, true);
1136
1137        final MenuItem searchMenu = menu.findItem(R.id.menu_search);
1138        final MenuItem addContactMenu = menu.findItem(R.id.menu_add_contact);
1139        MenuItem addGroupMenu = menu.findItem(R.id.menu_add_group);
1140        if (addGroupMenu == null) {
1141            addGroupMenu = menu.findItem(R.id.menu_custom_add_group);
1142        }
1143
1144        if (mActionBarAdapter.isSearchMode()) {
1145            addContactMenu.setVisible(false);
1146            addGroupMenu.setVisible(false);
1147        } else {
1148            switch (mActionBarAdapter.getCurrentTab()) {
1149                case FAVORITES:
1150                    // TODO: Fall through until we determine what the menu items should be for
1151                    // this tab
1152                case ALL:
1153                    addContactMenu.setVisible(true);
1154                    addGroupMenu.setVisible(false);
1155                    break;
1156                case GROUPS:
1157                    addContactMenu.setVisible(false);
1158                    addGroupMenu.setVisible(true);
1159                    break;
1160            }
1161        }
1162
1163        if (searchMenu != null) {
1164            // Don't show the search menu in search mode.
1165            searchMenu.setVisible(!mActionBarAdapter.isSearchMode());
1166        }
1167
1168        MenuItem settings = menu.findItem(R.id.menu_settings);
1169        if (settings != null) {
1170            settings.setVisible(!ContactsPreferenceActivity.isEmpty(this));
1171        }
1172
1173        return true;
1174    }
1175
1176    private void makeAllMenuItemsVisible(Menu menu, boolean visible) {
1177        final int itemCount = menu.size();
1178        for (int i = 0; i < itemCount; i++) {
1179            menu.getItem(i).setVisible(visible);
1180        }
1181    }
1182
1183    @Override
1184    public boolean onOptionsItemSelected(MenuItem item) {
1185        switch (item.getItemId()) {
1186            case android.R.id.home: {
1187                // The home icon on the action bar is pressed
1188                if (mActionBarAdapter.isUpShowing()) {
1189                    // "UP" icon press -- should be treated as "back".
1190                    onBackPressed();
1191                }
1192                return true;
1193            }
1194            case R.id.menu_settings: {
1195                final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
1196                startActivity(intent);
1197                return true;
1198            }
1199            case R.id.menu_contacts_filter: {
1200                final Intent intent = new Intent(this, AccountFilterActivity.class);
1201                startActivityForResult(intent, SUBACTIVITY_ACCOUNT_FILTER);
1202                return true;
1203            }
1204            case R.id.menu_search: {
1205                onSearchRequested();
1206                return true;
1207            }
1208            case R.id.menu_add_contact: {
1209                final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
1210                startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
1211                return true;
1212            }
1213            case R.id.menu_add_group: {
1214                createNewGroupWithAccountDisambiguation();
1215                return true;
1216            }
1217            case R.id.menu_import_export: {
1218                ImportExportDialogFragment.show(getFragmentManager());
1219                return true;
1220            }
1221            case R.id.menu_accounts: {
1222                final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
1223                intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
1224                    ContactsContract.AUTHORITY
1225                });
1226                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
1227                startActivity(intent);
1228                return true;
1229            }
1230        }
1231        return false;
1232    }
1233
1234    private void createNewGroupWithAccountDisambiguation() {
1235        final ArrayList<Account> accounts =
1236                AccountTypeManager.getInstance(this).getAccounts(true);
1237        if (accounts.size() <= 1 || mAddGroupImageView == null) {
1238            // No account to choose or no control to anchor the popup-menu to
1239            // ==> just go straight to the editor which will disambig if necessary
1240            final Intent intent = new Intent(this, GroupEditorActivity.class);
1241            intent.setAction(Intent.ACTION_INSERT);
1242            startActivityForResult(intent, SUBACTIVITY_NEW_GROUP);
1243            return;
1244        }
1245
1246        final ListPopupWindow popup = new ListPopupWindow(this, null);
1247        popup.setWidth(getResources().getDimensionPixelSize(R.dimen.account_selector_popup_width));
1248        popup.setAnchorView(mAddGroupImageView);
1249        // Create a list adapter with all writeable accounts (assume that the writeable accounts all
1250        // allow group creation).
1251        final AccountsListAdapter adapter = new AccountsListAdapter(this, true);
1252        popup.setAdapter(adapter);
1253        popup.setOnItemClickListener(new OnItemClickListener() {
1254            @Override
1255            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1256                popup.dismiss();
1257                final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class);
1258                intent.setAction(Intent.ACTION_INSERT);
1259                intent.putExtra(Intents.Insert.ACCOUNT, adapter.getItem(position));
1260                startActivityForResult(intent, SUBACTIVITY_NEW_GROUP);
1261            }
1262        });
1263        popup.setModal(true);
1264        popup.show();
1265    }
1266
1267    @Override
1268    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
1269            boolean globalSearch) {
1270        if (mAllFragment != null && mAllFragment.isAdded() && !globalSearch) {
1271            mAllFragment.startSearch(initialQuery);
1272        } else {
1273            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
1274        }
1275    }
1276
1277    @Override
1278    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1279        switch (requestCode) {
1280            case SUBACTIVITY_ACCOUNT_FILTER: {
1281                if (resultCode == Activity.RESULT_OK) {
1282                    ContactListFilter filter = (ContactListFilter) data.getParcelableExtra(
1283                            AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER);
1284                    if (filter == null) {
1285                        return;
1286                    }
1287                    // If this is a custom filter, launch the activity to customize the display list
1288                    if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
1289                        final Intent intent = new Intent(this,
1290                                CustomContactListFilterActivity.class);
1291                        startActivityForResult(intent, SUBACTIVITY_CUSTOMIZE_FILTER);
1292                    } else {
1293                        mContactListFilterController.setContactListFilter(filter, true);
1294                    }
1295                }
1296                break;
1297            }
1298            case SUBACTIVITY_CUSTOMIZE_FILTER: {
1299                if (resultCode == Activity.RESULT_OK) {
1300                    mContactListFilterController.selectCustomFilter();
1301                }
1302                break;
1303            }
1304            case SUBACTIVITY_EDIT_CONTACT:
1305            case SUBACTIVITY_NEW_CONTACT: {
1306                if (resultCode == RESULT_OK && PhoneCapabilityTester.isUsingTwoPanes(this)) {
1307                    mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
1308                    mAllFragment.reloadDataAndSetSelectedUri(data.getData());
1309                }
1310                break;
1311            }
1312
1313            case SUBACTIVITY_NEW_GROUP:
1314            case SUBACTIVITY_EDIT_GROUP: {
1315                if (resultCode == RESULT_OK && PhoneCapabilityTester.isUsingTwoPanes(this)) {
1316                    mRequest.setActionCode(ContactsRequest.ACTION_GROUP);
1317                    mGroupsFragment.setSelectedUri(data.getData());
1318                }
1319                break;
1320            }
1321
1322            // TODO: Using the new startActivityWithResultFromFragment API this should not be needed
1323            // anymore
1324            case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER:
1325                if (resultCode == RESULT_OK) {
1326                    mAllFragment.onPickerResult(data);
1327                }
1328
1329// TODO fix or remove multipicker code
1330//                else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) {
1331//                    // Finish the activity if the sub activity was canceled as back key is used
1332//                    // to confirm user selection in MODE_PICK_MULTIPLE_PHONES.
1333//                    finish();
1334//                }
1335//                break;
1336        }
1337    }
1338
1339    @Override
1340    public boolean onKeyDown(int keyCode, KeyEvent event) {
1341        // TODO move to the fragment
1342        switch (keyCode) {
1343//            case KeyEvent.KEYCODE_CALL: {
1344//                if (callSelection()) {
1345//                    return true;
1346//                }
1347//                break;
1348//            }
1349
1350            case KeyEvent.KEYCODE_DEL: {
1351                if (deleteSelection()) {
1352                    return true;
1353                }
1354                break;
1355            }
1356            default: {
1357                // Bring up the search UI if the user starts typing
1358                final int unicodeChar = event.getUnicodeChar();
1359                if (unicodeChar != 0 && !Character.isWhitespace(unicodeChar)) {
1360                    String query = new String(new int[]{ unicodeChar }, 0, 1);
1361                    if (!mActionBarAdapter.isSearchMode()) {
1362                        mActionBarAdapter.setQueryString(query);
1363                        mActionBarAdapter.setSearchMode(true);
1364                        return true;
1365                    } else if (!mRequest.isSearchMode()) {
1366                        if (!mSearchInitiated) {
1367                            mSearchInitiated = true;
1368                            startSearch(query, false, null, false);
1369                            return true;
1370                        }
1371                    }
1372                }
1373            }
1374        }
1375
1376        return super.onKeyDown(keyCode, event);
1377    }
1378
1379    @Override
1380    public void onBackPressed() {
1381        if (mSearchMode && mActionBarAdapter != null) {
1382            mActionBarAdapter.setSearchMode(false);
1383        } else {
1384            super.onBackPressed();
1385        }
1386    }
1387
1388    private boolean deleteSelection() {
1389        // TODO move to the fragment
1390//        if (mActionCode == ContactsRequest.ACTION_DEFAULT) {
1391//            final int position = mListView.getSelectedItemPosition();
1392//            if (position != ListView.INVALID_POSITION) {
1393//                Uri contactUri = getContactUri(position);
1394//                if (contactUri != null) {
1395//                    doContactDelete(contactUri);
1396//                    return true;
1397//                }
1398//            }
1399//        }
1400        return false;
1401    }
1402
1403    @Override
1404    protected void onSaveInstanceState(Bundle outState) {
1405        super.onSaveInstanceState(outState);
1406        outState.putBoolean(KEY_SEARCH_MODE, mSearchMode);
1407        mActionBarAdapter.onSaveInstanceState(outState);
1408        if (mContactDetailLayoutController != null) {
1409            mContactDetailLayoutController.onSaveInstanceState(outState);
1410        }
1411
1412        // Clear the listener to make sure we don't get callbacks after onSaveInstanceState,
1413        // in order to avoid doing fragment transactions after it.
1414        // TODO Figure out a better way to deal with the issue.
1415        mActionBarAdapter.setListener(null);
1416    }
1417
1418    @Override
1419    protected void onRestoreInstanceState(Bundle inState) {
1420        super.onRestoreInstanceState(inState);
1421        mSearchMode = inState.getBoolean(KEY_SEARCH_MODE);
1422        if (mContactDetailLayoutController != null) {
1423            mContactDetailLayoutController.onRestoreInstanceState(inState);
1424        }
1425    }
1426
1427    @Override
1428    public DialogManager getDialogManager() {
1429        return mDialogManager;
1430    }
1431
1432    // Visible for testing
1433    public ContactBrowseListFragment getListFragment() {
1434        return mAllFragment;
1435    }
1436
1437    // Visible for testing
1438    public ContactDetailFragment getDetailFragment() {
1439        return mContactDetailFragment;
1440    }
1441}
1442