19b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa/*
29b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * Copyright (C) 2011 The Android Open Source Project
39b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa *
49b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License");
59b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * you may not use this file except in compliance with the License.
69b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * You may obtain a copy of the License at
79b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa *
89b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa *      http://www.apache.org/licenses/LICENSE-2.0
99b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa *
109b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * Unless required by applicable law or agreed to in writing, software
119b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS,
129b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * See the License for the specific language governing permissions and
149b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * limitations under the License.
159b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa */
169b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawapackage com.android.contacts.list;
179b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
189b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.app.Activity;
199b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.app.Fragment;
209b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.app.LoaderManager;
219b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.content.CursorLoader;
229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.content.Intent;
239b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.content.Loader;
249b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.database.Cursor;
257a700cf798a19a8452b8671f643a05336f63f611Katherine Kuanimport android.graphics.Rect;
269b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.net.Uri;
279b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.os.Bundle;
2890f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawaimport android.os.Handler;
2990f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawaimport android.os.Message;
307c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawaimport android.provider.ContactsContract;
319b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.provider.ContactsContract.Directory;
327c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawaimport android.provider.Settings;
339b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.util.Log;
349b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.view.LayoutInflater;
357c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawaimport android.view.Menu;
367c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawaimport android.view.MenuInflater;
377c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawaimport android.view.MenuItem;
389b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.view.View;
399b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.view.View.OnClickListener;
409b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.view.ViewGroup;
411dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawaimport android.widget.AbsListView;
429b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.widget.AdapterView;
439b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.widget.AdapterView.OnItemClickListener;
4434b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawaimport android.widget.FrameLayout;
459b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.widget.ListView;
469b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.widget.TextView;
479b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
48e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.ContactPhotoManager;
49e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.ContactTileLoaderFactory;
50e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.R;
51e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.dialog.ClearFrequentsDialog;
52e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.interactions.ImportExportDialogFragment;
53e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.preference.ContactsPreferences;
54e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.util.AccountFilterUtil;
55e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng
569b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa/**
579b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * Fragment for Phone UI's favorite screen.
589b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa *
599b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * This fragment contains three kinds of contacts in one screen: "starred", "frequent", and "all"
609b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * contacts. To show them at once, this merges results from {@link ContactTileAdapter} and
619b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * {@link PhoneNumberListAdapter} into one unified list using {@link PhoneFavoriteMergedAdapter}.
629b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * A contact filter header is also inserted between those adapters' results.
639b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa */
64091034c2d4c3ea69fc63c2e9d0956a109a13e08eDaisuke Miyakawapublic class PhoneFavoriteFragment extends Fragment implements OnItemClickListener {
659b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private static final String TAG = PhoneFavoriteFragment.class.getSimpleName();
669b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private static final boolean DEBUG = false;
679b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
689b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    /**
699b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * Used with LoaderManager.
709b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     */
719b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private static int LOADER_ID_CONTACT_TILE = 1;
729b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private static int LOADER_ID_ALL_CONTACTS = 2;
739b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
749b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private static final String KEY_FILTER = "filter";
759b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
7634b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
7734b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa
789b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public interface Listener {
799b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onContactSelected(Uri contactUri);
804e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus        public void onCallNumberDirectly(String phoneNumber);
819b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
829b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
839b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
849b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
859b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public CursorLoader onCreateLoader(int id, Bundle args) {
869b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onCreateLoader.");
879b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity());
889b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
899b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
909b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
919b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
929b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoadFinished");
939b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mContactTileAdapter.setContactCursor(data);
949b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
959b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (mAllContactsForceReload) {
969b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                mAllContactsAdapter.onDataReload();
979b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                // Use restartLoader() to make LoaderManager to load the section again.
989b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                getLoaderManager().restartLoader(
999b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                        LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
1009b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            } else if (!mAllContactsLoaderStarted) {
1019b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                // Load "all" contacts if not loaded yet.
1029b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                getLoaderManager().initLoader(
1039b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                        LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
1049b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            }
1059b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsForceReload = false;
1069b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsLoaderStarted = true;
1079b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1089b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            // Show the filter header with "loading" state.
1099b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            updateFilterHeaderView();
11034b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            mAccountFilterHeader.setVisibility(View.VISIBLE);
1112fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu
1122fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu            // invalidate the options menu if needed
1132fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu            invalidateOptionsMenuIfNeeded();
1149b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1159b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1169b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1179b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onLoaderReset(Loader<Cursor> loader) {
1189b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoaderReset. ");
1199b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1209b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
1219b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private class AllContactsLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
1239b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1249b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
1259b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onCreateLoader");
1269b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            CursorLoader loader = new CursorLoader(getActivity(), null, null, null, null, null);
1279b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsAdapter.configureLoader(loader, Directory.DEFAULT);
1289b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            return loader;
1299b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1309b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1319b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1329b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
1339b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onLoadFinished");
1349b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsAdapter.changeCursor(0, data);
1359b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            updateFilterHeaderView();
13690f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa            mHandler.removeMessages(MESSAGE_SHOW_LOADING_EFFECT);
13790f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa            mLoadingView.setVisibility(View.VISIBLE);
1389b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1399b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1409b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1419b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onLoaderReset(Loader<Cursor> loader) {
1429b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onLoaderReset. ");
1439b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1449b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
1459b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1464e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus    private class ContactTileAdapterListener implements ContactTileView.Listener {
1479b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1487a700cf798a19a8452b8671f643a05336f63f611Katherine Kuan        public void onContactSelected(Uri contactUri, Rect targetRect) {
1499b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (mListener != null) {
1509b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                mListener.onContactSelected(contactUri);
1519b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            }
1529b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1534e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus
1544e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus        @Override
1554e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus        public void onCallNumberDirectly(String phoneNumber) {
1564e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus            if (mListener != null) {
1574e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus                mListener.onCallNumberDirectly(phoneNumber);
1584e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus            }
1594e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus        }
1602f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann
1612f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann        @Override
1622f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann        public int getApproximateTileWidth() {
1632f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann            return getView().getWidth() / mContactTileAdapter.getColumnCount();
1642f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann        }
1659b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
1669b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1679b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private class FilterHeaderClickListener implements OnClickListener {
1689b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1699b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onClick(View view) {
17034b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            AccountFilterUtil.startAccountFilterActivityForResult(
171b6553457d0c3e84eefa0a853090b680090a7b2f9Josh Gargus                    PhoneFavoriteFragment.this,
172b6553457d0c3e84eefa0a853090b680090a7b2f9Josh Gargus                    REQUEST_CODE_ACCOUNT_FILTER,
173b6553457d0c3e84eefa0a853090b680090a7b2f9Josh Gargus                    mFilter);
1749b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1759b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
1769b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1779b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private class ContactsPreferenceChangeListener
1789b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            implements ContactsPreferences.ChangeListener {
1799b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1809b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onChange() {
1819b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (loadContactsPreferences()) {
1829b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                requestReloadAllContacts();
1839b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            }
1849b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1859b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
1869b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1871dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa    private class ScrollListener implements ListView.OnScrollListener {
1881dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        private boolean mShouldShowFastScroller;
1891dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        @Override
1901dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        public void onScroll(AbsListView view,
1911dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa                int firstVisibleItem, int visibleItemCount, int totalItemCount) {
1921dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa            // FastScroller should be visible only when the user is seeing "all" contacts section.
1931dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa            final boolean shouldShow = mAdapter.shouldShowFirstScroller(firstVisibleItem);
1941dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa            if (shouldShow != mShouldShowFastScroller) {
195532db253b803d98bfbbc457c5b559834cdb3b326Daisuke Miyakawa                mListView.setVerticalScrollBarEnabled(shouldShow);
1961dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa                mListView.setFastScrollEnabled(shouldShow);
1971dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa                mListView.setFastScrollAlwaysVisible(shouldShow);
1981dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa                mShouldShowFastScroller = shouldShow;
1991dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa            }
2001dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        }
2011dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa
2021dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        @Override
2031dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        public void onScrollStateChanged(AbsListView view, int scrollState) {
2041dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        }
2051dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa    }
2061dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa
20790f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    private static final int MESSAGE_SHOW_LOADING_EFFECT = 1;
20890f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    private static final int LOADING_EFFECT_DELAY = 500;  // ms
20990f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    private final Handler mHandler = new Handler() {
21090f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        @Override
21190f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        public void handleMessage(Message msg) {
21290f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa            switch (msg.what) {
21390f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa                case MESSAGE_SHOW_LOADING_EFFECT:
21490f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa                    mLoadingView.setVisibility(View.VISIBLE);
21590f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa                    break;
21690f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa            }
21790f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        }
21890f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    };
21990f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa
2209b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private Listener mListener;
2219b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private PhoneFavoriteMergedAdapter mAdapter;
2229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private ContactTileAdapter mContactTileAdapter;
2239b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private PhoneNumberListAdapter mAllContactsAdapter;
2249b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
2259b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    /**
2269b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * true when the loader for {@link PhoneNumberListAdapter} has started already.
2279b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     */
2289b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private boolean mAllContactsLoaderStarted;
2299b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    /**
2309b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * true when the loader for {@link PhoneNumberListAdapter} must reload "all" contacts again.
2319b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * It typically happens when {@link ContactsPreferences} has changed its settings
2329b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * (display order and sort order)
2339b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     */
2349b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private boolean mAllContactsForceReload;
2359b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
2369b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private ContactsPreferences mContactsPrefs;
2379b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private ContactListFilter mFilter;
2389b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
2399b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private TextView mEmptyView;
2409b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private ListView mListView;
24134b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    /**
24234b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa     * Layout containing {@link #mAccountFilterHeader}. Used to limit area being "pressed".
24334b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa     */
24434b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    private FrameLayout mAccountFilterHeaderContainer;
24534b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    private View mAccountFilterHeader;
2469b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
24790f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    /**
24890f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa     * Layout used when contacts load is slower than expected and thus "loading" view should be
24990f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa     * shown.
25090f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa     */
25190f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    private View mLoadingView;
25290f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa
2534e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus    private final ContactTileView.Listener mContactTileAdapterListener =
2549b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            new ContactTileAdapterListener();
2559b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener =
2569b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            new ContactTileLoaderListener();
2579b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private final LoaderManager.LoaderCallbacks<Cursor> mAllContactsLoaderListener =
2589b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            new AllContactsLoaderListener();
2599b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private final OnClickListener mFilterHeaderClickListener = new FilterHeaderClickListener();
2609b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private final ContactsPreferenceChangeListener mContactsPreferenceChangeListener =
2619b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            new ContactsPreferenceChangeListener();
2621dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa    private final ScrollListener mScrollListener = new ScrollListener();
2639b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
2642fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu    private boolean mOptionsMenuHasFrequents;
2652fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu
2669b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
2670da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa    public void onAttach(Activity activity) {
2680da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        if (DEBUG) Log.d(TAG, "onAttach()");
2690da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        super.onAttach(activity);
2700da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
2710da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mContactsPrefs = new ContactsPreferences(activity);
2720da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
2730da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Construct two base adapters which will become part of PhoneFavoriteMergedAdapter.
2740da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // We don't construct the resultant adapter at this moment since it requires LayoutInflater
2750da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // that will be available on onCreateView().
2760da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
2770da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mContactTileAdapter = new ContactTileAdapter(activity, mContactTileAdapterListener,
2782871d61514f014926d991e8b037828c67438e522Maurice Chu                getResources().getInteger(R.integer.contact_tile_column_count_in_favorites),
2790da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa                ContactTileAdapter.DisplayType.STREQUENT_PHONE_ONLY);
2800da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
2810da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
2820da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Setup the "all" adapter manually. See also the setup logic in ContactEntryListFragment.
2830da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter = new PhoneNumberListAdapter(activity);
2840da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setDisplayPhotos(true);
2850da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setQuickContactEnabled(true);
2860da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setSearchMode(false);
2870da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setIncludeProfile(false);
2880da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setSelectionVisible(false);
2890da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setDarkTheme(true);
2900da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
2910da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Disable directory header.
2920da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setHasHeader(0, false);
2930da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Show A-Z section index.
2940da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setSectionHeaderDisplayEnabled(true);
2950da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Disable pinned header. It doesn't work with this fragment.
2960da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setPinnedPartitionHeadersEnabled(false);
2970da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Put photos on left for consistency with "frequent" contacts section.
2980da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setPhotoPosition(ContactListItemView.PhotoPosition.LEFT);
2990da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
3000da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Use Callable.CONTENT_URI which will include not only phone numbers but also SIP
3010da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // addresses.
3020da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setUseCallableUri(true);
3030da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
3040da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setContactNameDisplayOrder(mContactsPrefs.getDisplayOrder());
3050da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setSortOrder(mContactsPrefs.getSortOrder());
3060da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa    }
3070da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
3080da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa    @Override
3099b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void onCreate(Bundle savedState) {
3100da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        if (DEBUG) Log.d(TAG, "onCreate()");
3119b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        super.onCreate(savedState);
3129b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (savedState != null) {
3139b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mFilter = savedState.getParcelable(KEY_FILTER);
3140da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
3150da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa            if (mFilter != null) {
3160da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa                mAllContactsAdapter.setFilter(mFilter);
3170da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa            }
3189b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
3197c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa        setHasOptionsMenu(true);
3209b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
3219b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
3239b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void onSaveInstanceState(Bundle outState) {
3249b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        super.onSaveInstanceState(outState);
3259b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        outState.putParcelable(KEY_FILTER, mFilter);
3269b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
3279b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3289b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
3295db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa    public View onCreateView(LayoutInflater inflater, ViewGroup container,
3305db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa            Bundle savedInstanceState) {
331902170690532fceb9a3e13ad00f6aff00f95b10fDaisuke Miyakawa        final View listLayout = inflater.inflate(
332902170690532fceb9a3e13ad00f6aff00f95b10fDaisuke Miyakawa                R.layout.phone_contact_tile_list, container, false);
3335db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa
3345db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView = (ListView) listLayout.findViewById(R.id.contact_tile_list);
3355db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setItemsCanFocus(true);
3365db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setOnItemClickListener(this);
337532db253b803d98bfbbc457c5b559834cdb3b326Daisuke Miyakawa        mListView.setVerticalScrollBarEnabled(false);
3385db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
3395db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
3409b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3410da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Create the account filter header but keep it hidden until "all" contacts are loaded.
3420da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAccountFilterHeaderContainer = new FrameLayout(getActivity(), null);
3430da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAccountFilterHeader = inflater.inflate(R.layout.account_filter_header_for_phone_favorite,
3440da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa                mListView, false);
3450da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
3460da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAccountFilterHeaderContainer.addView(mAccountFilterHeader);
34790f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa
34890f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        mLoadingView = inflater.inflate(R.layout.phone_loading_contacts, mListView, false);
3490da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
3500da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAdapter = new PhoneFavoriteMergedAdapter(getActivity(),
35190f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa                mContactTileAdapter, mAccountFilterHeaderContainer, mAllContactsAdapter,
35290f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa                mLoadingView);
3535db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa
3545db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setAdapter(mAdapter);
3555db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa
3561dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        mListView.setOnScrollListener(mScrollListener);
3571dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        mListView.setFastScrollEnabled(false);
3581dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        mListView.setFastScrollAlwaysVisible(false);
3591dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa
3605db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mEmptyView = (TextView) listLayout.findViewById(R.id.contact_tile_list_empty);
3615db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mEmptyView.setText(getString(R.string.listTotalAllContactsZero));
3625db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setEmptyView(mEmptyView);
3639b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3649b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        updateFilterHeaderView();
3655db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa
3665db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        return listLayout;
3679b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
3689b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3692fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu    private boolean isOptionsMenuChanged() {
3702fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu        return mOptionsMenuHasFrequents != hasFrequents();
3712fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu    }
3722fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu
3732fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu    private void invalidateOptionsMenuIfNeeded() {
3742fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu        if (isOptionsMenuChanged()) {
3752fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu            getActivity().invalidateOptionsMenu();
3762fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu        }
3772fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu    }
3782fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu
3799b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
3807c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
3817c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa        super.onCreateOptionsMenu(menu, inflater);
382091034c2d4c3ea69fc63c2e9d0956a109a13e08eDaisuke Miyakawa        inflater.inflate(R.menu.phone_favorite_options, menu);
3837c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    }
3847c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa
3857c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    @Override
3862fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu    public void onPrepareOptionsMenu(Menu menu) {
3872fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu        final MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
3882fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu        mOptionsMenuHasFrequents = hasFrequents();
3892fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu        clearFrequents.setVisible(mOptionsMenuHasFrequents);
3902fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu    }
3912fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu
3922fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu    private boolean hasFrequents() {
3932fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu        return mContactTileAdapter.getNumFrequents() > 0;
3942fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu    }
3952fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu
3962fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu    @Override
3977c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    public boolean onOptionsItemSelected(MenuItem item) {
3987c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa        switch (item.getItemId()) {
3997c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa            case R.id.menu_import_export:
400e05f8efc11a383b8299e3a16b79a5d27531251d4Josh Gargus                // We hard-code the "contactsAreAvailable" argument because doing it properly would
401e05f8efc11a383b8299e3a16b79a5d27531251d4Josh Gargus                // involve querying a {@link ProviderStatusLoader}, which we don't want to do right
402091034c2d4c3ea69fc63c2e9d0956a109a13e08eDaisuke Miyakawa                // now in Dialtacts for (potential) performance reasons.  Compare with how it is
403e05f8efc11a383b8299e3a16b79a5d27531251d4Josh Gargus                // done in {@link PeopleActivity}.
404e05f8efc11a383b8299e3a16b79a5d27531251d4Josh Gargus                ImportExportDialogFragment.show(getFragmentManager(), true);
4057c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                return true;
4067c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa            case R.id.menu_accounts:
4077c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
4087c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
4097c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                    ContactsContract.AUTHORITY
4107c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                });
4117c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
4127c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                startActivity(intent);
4137c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                return true;
4142fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu            case R.id.menu_clear_frequents:
4152fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu                ClearFrequentsDialog.show(getFragmentManager());
4162fc4de31fe2d6a261d3eeaff69ea944a5383003eMaurice Chu                return true;
4177c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa        }
4187c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa        return false;
4197c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    }
4207c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa
4217c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    @Override
4229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void onStart() {
4239b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        super.onStart();
4249b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4259b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        mContactsPrefs.registerChangeListener(mContactsPreferenceChangeListener);
4269b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4279b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        // If ContactsPreferences has changed, we need to reload "all" contacts with the new
4289b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        // settings. If mAllContactsFoarceReload is already true, it should be kept.
4299b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (loadContactsPreferences()) {
4309b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsForceReload = true;
4319b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
4329b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4330da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Use initLoader() instead of restartLoader() to refraining unnecessary reload.
4349b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        // This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
4359b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        // be called, on which we'll check if "all" contacts should be reloaded again or not.
4369b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, mContactTileLoaderListener);
43790f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa
43890f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        // Delay showing "loading" view until certain amount of time so that users won't see
43990f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        // instant flash of the view when the contacts load is fast enough.
44090f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        // This will be kept shown until both tile and all sections are loaded.
44190f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        mLoadingView.setVisibility(View.INVISIBLE);
44290f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_LOADING_EFFECT, LOADING_EFFECT_DELAY);
4439b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
4449b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4459b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
4469b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void onStop() {
4479b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        super.onStop();
4489b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        mContactsPrefs.unregisterChangeListener();
4499b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
4509b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4519b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    /**
4529b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * {@inheritDoc}
4539b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     *
4549b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * This is only effective for elements provided by {@link #mContactTileAdapter}.
4559b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * {@link #mContactTileAdapter} has its own logic for click events.
4569b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     */
4579b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
4589b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
4599b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        final int contactTileAdapterCount = mContactTileAdapter.getCount();
4609b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (position <= contactTileAdapterCount) {
4619b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            Log.e(TAG, "onItemClick() event for unexpected position. "
4629b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                    + "The position " + position + " is before \"all\" section. Ignored.");
4639b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        } else {
4649b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            final int localPosition = position - mContactTileAdapter.getCount() - 1;
4659b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (mListener != null) {
4669b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                mListener.onContactSelected(mAllContactsAdapter.getDataUri(localPosition));
4679b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            }
4689b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
4699b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
4709b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
47134b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    @Override
47234b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    public void onActivityResult(int requestCode, int resultCode, Intent data) {
47334b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa        if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
47434b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            if (getActivity() != null) {
47534b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa                AccountFilterUtil.handleAccountFilterResult(
47634b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa                        ContactListFilterController.getInstance(getActivity()), resultCode, data);
47734b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            } else {
47834b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa                Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
47934b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            }
48034b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa        }
48134b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    }
48234b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa
4839b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private boolean loadContactsPreferences() {
4849b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (mContactsPrefs == null || mAllContactsAdapter == null) {
4859b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            return false;
4869b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
4879b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4889b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        boolean changed = false;
4890da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        final int currentDisplayOrder = mContactsPrefs.getDisplayOrder();
4900da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        if (mAllContactsAdapter.getContactNameDisplayOrder() != currentDisplayOrder) {
4910da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa            mAllContactsAdapter.setContactNameDisplayOrder(currentDisplayOrder);
4929b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            changed = true;
4939b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
4949b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4950da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        final int currentSortOrder = mContactsPrefs.getSortOrder();
4960da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        if (mAllContactsAdapter.getSortOrder() != currentSortOrder) {
4970da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa            mAllContactsAdapter.setSortOrder(currentSortOrder);
4989b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            changed = true;
4999b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
5009b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5019b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        return changed;
5029b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
5039b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5049b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    /**
5059b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * Requests to reload "all" contacts. If the section is already loaded, this method will
5069b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * force reloading it now. If the section isn't loaded yet, the actual load may be done later
5079b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * (on {@link #onStart()}.
5089b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     */
5099b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private void requestReloadAllContacts() {
5109b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (DEBUG) {
5119b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            Log.d(TAG, "requestReloadAllContacts()"
5129b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                    + " mAllContactsAdapter: " + mAllContactsAdapter
5139b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                    + ", mAllContactsLoaderStarted: " + mAllContactsLoaderStarted);
5149b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
5159b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5169b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (mAllContactsAdapter == null || !mAllContactsLoaderStarted) {
5179b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            // Remember this request until next load on onStart().
5189b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsForceReload = true;
5199b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            return;
5209b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
5219b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (DEBUG) Log.d(TAG, "Reload \"all\" contacts now.");
5239b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5249b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        mAllContactsAdapter.onDataReload();
5259b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        // Use restartLoader() to make LoaderManager to load the section again.
5269b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        getLoaderManager().restartLoader(LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
5279b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
5289b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5299b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private void updateFilterHeaderView() {
5309b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        final ContactListFilter filter = getFilter();
53134b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa        if (mAccountFilterHeader == null || mAllContactsAdapter == null || filter == null) {
53234b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            return;
5339b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
53490f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        AccountFilterUtil.updateAccountFilterTitleForPhone(mAccountFilterHeader, filter, true);
5359b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
5369b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5379b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public ContactListFilter getFilter() {
5389b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        return mFilter;
5399b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
5409b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5419b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void setFilter(ContactListFilter filter) {
5429b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if ((mFilter == null && filter == null) || (mFilter != null && mFilter.equals(filter))) {
5439b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            return;
5449b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
5459b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5469b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (DEBUG) {
5479b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            Log.d(TAG, "setFilter(). old filter (" + mFilter
5489b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                    + ") will be replaced with new filter (" + filter + ")");
5499b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
5509b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5519b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        mFilter = filter;
5529b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5535db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        if (mAllContactsAdapter != null) {
5545db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa            mAllContactsAdapter.setFilter(mFilter);
5555db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa            requestReloadAllContacts();
5565db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa            updateFilterHeaderView();
5575db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        }
5589b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
5599b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5609b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void setListener(Listener listener) {
5619b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        mListener = listener;
5629b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
563aabc1e81c2d43827fe2ad0bdde6851bdfb431924Josh Gargus}
564