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