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