PhoneFavoriteFragment.java revision 90f40d0162e4cee5b94bf3203c059e27f6472fab
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 com.android.contacts.ContactPhotoManager;
199b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport com.android.contacts.ContactTileLoaderFactory;
209b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport com.android.contacts.R;
217c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawaimport com.android.contacts.interactions.ImportExportDialogFragment;
229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport com.android.contacts.preference.ContactsPreferences;
2334b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawaimport com.android.contacts.util.AccountFilterUtil;
249b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
259b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.app.Activity;
269b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.app.Fragment;
279b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.app.LoaderManager;
289b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.content.CursorLoader;
299b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.content.Intent;
309b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.content.Loader;
319b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.database.Cursor;
327a700cf798a19a8452b8671f643a05336f63f611Katherine Kuanimport android.graphics.Rect;
339b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.net.Uri;
349b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.os.Bundle;
3590f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawaimport android.os.Handler;
3690f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawaimport android.os.Message;
377c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawaimport android.provider.ContactsContract;
389b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.provider.ContactsContract.Directory;
397c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawaimport android.provider.Settings;
409b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.util.Log;
419b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.view.LayoutInflater;
427c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawaimport android.view.Menu;
437c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawaimport android.view.MenuInflater;
447c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawaimport android.view.MenuItem;
459b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.view.View;
469b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.view.View.OnClickListener;
479b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.view.ViewGroup;
481dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawaimport android.widget.AbsListView;
499b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.widget.AdapterView;
509b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.widget.AdapterView.OnItemClickListener;
5134b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawaimport android.widget.FrameLayout;
529b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.widget.ListView;
539b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawaimport android.widget.TextView;
549b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
559b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa/**
569b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * Fragment for Phone UI's favorite screen.
579b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa *
589b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * This fragment contains three kinds of contacts in one screen: "starred", "frequent", and "all"
599b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * contacts. To show them at once, this merges results from {@link ContactTileAdapter} and
609b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * {@link PhoneNumberListAdapter} into one unified list using {@link PhoneFavoriteMergedAdapter}.
619b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa * A contact filter header is also inserted between those adapters' results.
629b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa */
63091034c2d4c3ea69fc63c2e9d0956a109a13e08eDaisuke Miyakawapublic class PhoneFavoriteFragment extends Fragment implements OnItemClickListener {
649b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private static final String TAG = PhoneFavoriteFragment.class.getSimpleName();
659b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private static final boolean DEBUG = false;
669b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
679b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    /**
689b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * Used with LoaderManager.
699b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     */
709b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private static int LOADER_ID_CONTACT_TILE = 1;
719b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private static int LOADER_ID_ALL_CONTACTS = 2;
729b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
739b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private static final String KEY_FILTER = "filter";
749b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
7534b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
7634b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa
779b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public interface Listener {
789b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onContactSelected(Uri contactUri);
794e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus        public void onCallNumberDirectly(String phoneNumber);
809b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
819b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
829b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
839b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
849b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public CursorLoader onCreateLoader(int id, Bundle args) {
859b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onCreateLoader.");
869b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity());
879b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
889b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
899b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
909b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
919b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoadFinished");
929b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mContactTileAdapter.setContactCursor(data);
939b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
949b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (mAllContactsForceReload) {
959b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                mAllContactsAdapter.onDataReload();
969b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                // Use restartLoader() to make LoaderManager to load the section again.
979b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                getLoaderManager().restartLoader(
989b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                        LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
999b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            } else if (!mAllContactsLoaderStarted) {
1009b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                // Load "all" contacts if not loaded yet.
1019b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                getLoaderManager().initLoader(
1029b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                        LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
1039b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            }
1049b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsForceReload = false;
1059b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsLoaderStarted = true;
1069b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1079b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            // Show the filter header with "loading" state.
1089b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            updateFilterHeaderView();
10934b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            mAccountFilterHeader.setVisibility(View.VISIBLE);
1109b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1119b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1129b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1139b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onLoaderReset(Loader<Cursor> loader) {
1149b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoaderReset. ");
1159b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1169b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
1179b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1189b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private class AllContactsLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
1199b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1209b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
1219b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onCreateLoader");
1229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            CursorLoader loader = new CursorLoader(getActivity(), null, null, null, null, null);
1239b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsAdapter.configureLoader(loader, Directory.DEFAULT);
1249b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            return loader;
1259b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1269b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1279b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1289b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
1299b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onLoadFinished");
1309b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsAdapter.changeCursor(0, data);
1319b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            updateFilterHeaderView();
13290f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa            mHandler.removeMessages(MESSAGE_SHOW_LOADING_EFFECT);
13390f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa            mLoadingView.setVisibility(View.VISIBLE);
1349b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1359b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1369b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1379b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onLoaderReset(Loader<Cursor> loader) {
1389b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onLoaderReset. ");
1399b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1409b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
1419b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1424e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus    private class ContactTileAdapterListener implements ContactTileView.Listener {
1439b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1447a700cf798a19a8452b8671f643a05336f63f611Katherine Kuan        public void onContactSelected(Uri contactUri, Rect targetRect) {
1459b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (mListener != null) {
1469b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                mListener.onContactSelected(contactUri);
1479b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            }
1489b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1494e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus
1504e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus        @Override
1514e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus        public void onCallNumberDirectly(String phoneNumber) {
1524e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus            if (mListener != null) {
1534e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus                mListener.onCallNumberDirectly(phoneNumber);
1544e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus            }
1554e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus        }
1562f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann
1572f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann        @Override
1582f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann        public int getApproximateTileWidth() {
1592f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann            return getView().getWidth() / mContactTileAdapter.getColumnCount();
1602f77c85543e6c497e63cd0e216b70e58c1d13ec3Daniel Lehmann        }
1619b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
1629b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1639b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private class FilterHeaderClickListener implements OnClickListener {
1649b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1659b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onClick(View view) {
16634b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            AccountFilterUtil.startAccountFilterActivityForResult(
167b6553457d0c3e84eefa0a853090b680090a7b2f9Josh Gargus                    PhoneFavoriteFragment.this,
168b6553457d0c3e84eefa0a853090b680090a7b2f9Josh Gargus                    REQUEST_CODE_ACCOUNT_FILTER,
169b6553457d0c3e84eefa0a853090b680090a7b2f9Josh Gargus                    mFilter);
1709b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1719b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
1729b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1739b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private class ContactsPreferenceChangeListener
1749b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            implements ContactsPreferences.ChangeListener {
1759b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        @Override
1769b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        public void onChange() {
1779b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (loadContactsPreferences()) {
1789b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                requestReloadAllContacts();
1799b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            }
1809b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
1819b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
1829b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
1831dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa    private class ScrollListener implements ListView.OnScrollListener {
1841dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        private boolean mShouldShowFastScroller;
1851dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        @Override
1861dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        public void onScroll(AbsListView view,
1871dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa                int firstVisibleItem, int visibleItemCount, int totalItemCount) {
1881dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa            // FastScroller should be visible only when the user is seeing "all" contacts section.
1891dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa            final boolean shouldShow = mAdapter.shouldShowFirstScroller(firstVisibleItem);
1901dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa            if (shouldShow != mShouldShowFastScroller) {
191532db253b803d98bfbbc457c5b559834cdb3b326Daisuke Miyakawa                mListView.setVerticalScrollBarEnabled(shouldShow);
1921dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa                mListView.setFastScrollEnabled(shouldShow);
1931dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa                mListView.setFastScrollAlwaysVisible(shouldShow);
1941dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa                mShouldShowFastScroller = shouldShow;
1951dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa            }
1961dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        }
1971dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa
1981dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        @Override
1991dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        public void onScrollStateChanged(AbsListView view, int scrollState) {
2001dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        }
2011dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa    }
2021dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa
20390f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    private static final int MESSAGE_SHOW_LOADING_EFFECT = 1;
20490f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    private static final int LOADING_EFFECT_DELAY = 500;  // ms
20590f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    private final Handler mHandler = new Handler() {
20690f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        @Override
20790f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        public void handleMessage(Message msg) {
20890f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa            switch (msg.what) {
20990f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa                case MESSAGE_SHOW_LOADING_EFFECT:
21090f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa                    mLoadingView.setVisibility(View.VISIBLE);
21190f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa                    break;
21290f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa            }
21390f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        }
21490f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    };
21590f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa
2169b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private Listener mListener;
2179b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private PhoneFavoriteMergedAdapter mAdapter;
2189b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private ContactTileAdapter mContactTileAdapter;
2199b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private PhoneNumberListAdapter mAllContactsAdapter;
2209b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
2219b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    /**
2229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * true when the loader for {@link PhoneNumberListAdapter} has started already.
2239b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     */
2249b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private boolean mAllContactsLoaderStarted;
2259b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    /**
2269b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * true when the loader for {@link PhoneNumberListAdapter} must reload "all" contacts again.
2279b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * It typically happens when {@link ContactsPreferences} has changed its settings
2289b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * (display order and sort order)
2299b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     */
2309b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private boolean mAllContactsForceReload;
2319b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
2329b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private ContactsPreferences mContactsPrefs;
2339b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private ContactListFilter mFilter;
2349b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
2359b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private TextView mEmptyView;
2369b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private ListView mListView;
23734b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    /**
23834b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa     * Layout containing {@link #mAccountFilterHeader}. Used to limit area being "pressed".
23934b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa     */
24034b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    private FrameLayout mAccountFilterHeaderContainer;
24134b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    private View mAccountFilterHeader;
2429b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
24390f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    /**
24490f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa     * Layout used when contacts load is slower than expected and thus "loading" view should be
24590f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa     * shown.
24690f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa     */
24790f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa    private View mLoadingView;
24890f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa
2494e05a29c9b05c8cb74972aa5b9fe55fe35d7f45dJosh Gargus    private final ContactTileView.Listener mContactTileAdapterListener =
2509b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            new ContactTileAdapterListener();
2519b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener =
2529b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            new ContactTileLoaderListener();
2539b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private final LoaderManager.LoaderCallbacks<Cursor> mAllContactsLoaderListener =
2549b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            new AllContactsLoaderListener();
2559b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private final OnClickListener mFilterHeaderClickListener = new FilterHeaderClickListener();
2569b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private final ContactsPreferenceChangeListener mContactsPreferenceChangeListener =
2579b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            new ContactsPreferenceChangeListener();
2581dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa    private final ScrollListener mScrollListener = new ScrollListener();
2599b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
2609b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
2610da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa    public void onAttach(Activity activity) {
2620da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        if (DEBUG) Log.d(TAG, "onAttach()");
2630da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        super.onAttach(activity);
2640da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
2650da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mContactsPrefs = new ContactsPreferences(activity);
2660da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
2670da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Construct two base adapters which will become part of PhoneFavoriteMergedAdapter.
2680da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // We don't construct the resultant adapter at this moment since it requires LayoutInflater
2690da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // that will be available on onCreateView().
2700da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
2710da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mContactTileAdapter = new ContactTileAdapter(activity, mContactTileAdapterListener,
2720da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa                getResources().getInteger(R.integer.contact_tile_column_count),
2730da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa                ContactTileAdapter.DisplayType.STREQUENT_PHONE_ONLY);
2740da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
2750da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
2760da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Setup the "all" adapter manually. See also the setup logic in ContactEntryListFragment.
2770da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter = new PhoneNumberListAdapter(activity);
2780da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setDisplayPhotos(true);
2790da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setQuickContactEnabled(true);
2800da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setSearchMode(false);
2810da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setIncludeProfile(false);
2820da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setSelectionVisible(false);
2830da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setDarkTheme(true);
2840da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
2850da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Disable directory header.
2860da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setHasHeader(0, false);
2870da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Show A-Z section index.
2880da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setSectionHeaderDisplayEnabled(true);
2890da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Disable pinned header. It doesn't work with this fragment.
2900da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setPinnedPartitionHeadersEnabled(false);
2910da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Put photos on left for consistency with "frequent" contacts section.
2920da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setPhotoPosition(ContactListItemView.PhotoPosition.LEFT);
2930da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
2940da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Use Callable.CONTENT_URI which will include not only phone numbers but also SIP
2950da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // addresses.
2960da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setUseCallableUri(true);
2970da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
2980da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setContactNameDisplayOrder(mContactsPrefs.getDisplayOrder());
2990da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAllContactsAdapter.setSortOrder(mContactsPrefs.getSortOrder());
3000da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa    }
3010da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
3020da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa    @Override
3039b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void onCreate(Bundle savedState) {
3040da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        if (DEBUG) Log.d(TAG, "onCreate()");
3059b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        super.onCreate(savedState);
3069b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (savedState != null) {
3079b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mFilter = savedState.getParcelable(KEY_FILTER);
3080da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
3090da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa            if (mFilter != null) {
3100da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa                mAllContactsAdapter.setFilter(mFilter);
3110da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa            }
3129b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
3137c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa        setHasOptionsMenu(true);
3149b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
3159b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3169b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
3179b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void onSaveInstanceState(Bundle outState) {
3189b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        super.onSaveInstanceState(outState);
3199b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        outState.putParcelable(KEY_FILTER, mFilter);
3209b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
3219b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
3235db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa    public View onCreateView(LayoutInflater inflater, ViewGroup container,
3245db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa            Bundle savedInstanceState) {
325902170690532fceb9a3e13ad00f6aff00f95b10fDaisuke Miyakawa        final View listLayout = inflater.inflate(
326902170690532fceb9a3e13ad00f6aff00f95b10fDaisuke Miyakawa                R.layout.phone_contact_tile_list, container, false);
3275db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa
3285db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView = (ListView) listLayout.findViewById(R.id.contact_tile_list);
3295db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setItemsCanFocus(true);
3305db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setOnItemClickListener(this);
331532db253b803d98bfbbc457c5b559834cdb3b326Daisuke Miyakawa        mListView.setVerticalScrollBarEnabled(false);
3325db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
3335db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
3349b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3350da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Create the account filter header but keep it hidden until "all" contacts are loaded.
3360da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAccountFilterHeaderContainer = new FrameLayout(getActivity(), null);
3370da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAccountFilterHeader = inflater.inflate(R.layout.account_filter_header_for_phone_favorite,
3380da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa                mListView, false);
3390da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
3400da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAccountFilterHeaderContainer.addView(mAccountFilterHeader);
34190f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa
34290f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        mLoadingView = inflater.inflate(R.layout.phone_loading_contacts, mListView, false);
3430da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa
3440da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        mAdapter = new PhoneFavoriteMergedAdapter(getActivity(),
34590f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa                mContactTileAdapter, mAccountFilterHeaderContainer, mAllContactsAdapter,
34690f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa                mLoadingView);
3475db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa
3485db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setAdapter(mAdapter);
3495db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa
3501dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        mListView.setOnScrollListener(mScrollListener);
3511dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        mListView.setFastScrollEnabled(false);
3521dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa        mListView.setFastScrollAlwaysVisible(false);
3531dff6b60c28fa34e477b84c95c4bbe6c9b482416Daisuke Miyakawa
3545db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mEmptyView = (TextView) listLayout.findViewById(R.id.contact_tile_list_empty);
3555db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mEmptyView.setText(getString(R.string.listTotalAllContactsZero));
3565db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        mListView.setEmptyView(mEmptyView);
3579b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3589b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        updateFilterHeaderView();
3595db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa
3605db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        return listLayout;
3619b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
3629b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3639b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
3647c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
3657c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa        super.onCreateOptionsMenu(menu, inflater);
366091034c2d4c3ea69fc63c2e9d0956a109a13e08eDaisuke Miyakawa        inflater.inflate(R.menu.phone_favorite_options, menu);
3677c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    }
3687c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa
3697c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    @Override
3707c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    public boolean onOptionsItemSelected(MenuItem item) {
3717c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa        switch (item.getItemId()) {
3727c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa            case R.id.menu_import_export:
373e05f8efc11a383b8299e3a16b79a5d27531251d4Josh Gargus                // We hard-code the "contactsAreAvailable" argument because doing it properly would
374e05f8efc11a383b8299e3a16b79a5d27531251d4Josh Gargus                // involve querying a {@link ProviderStatusLoader}, which we don't want to do right
375091034c2d4c3ea69fc63c2e9d0956a109a13e08eDaisuke Miyakawa                // now in Dialtacts for (potential) performance reasons.  Compare with how it is
376e05f8efc11a383b8299e3a16b79a5d27531251d4Josh Gargus                // done in {@link PeopleActivity}.
377e05f8efc11a383b8299e3a16b79a5d27531251d4Josh Gargus                ImportExportDialogFragment.show(getFragmentManager(), true);
3787c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                return true;
3797c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa            case R.id.menu_accounts:
3807c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
3817c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
3827c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                    ContactsContract.AUTHORITY
3837c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                });
3847c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
3857c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                startActivity(intent);
3867c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa                return true;
3877c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa        }
3887c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa        return false;
3897c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    }
3907c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa
3917c30236364c71618da4b266a61a77e9711aa23f1Daisuke Miyakawa    @Override
3929b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void onStart() {
3939b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        super.onStart();
3949b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3959b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        mContactsPrefs.registerChangeListener(mContactsPreferenceChangeListener);
3969b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
3979b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        // If ContactsPreferences has changed, we need to reload "all" contacts with the new
3989b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        // settings. If mAllContactsFoarceReload is already true, it should be kept.
3999b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (loadContactsPreferences()) {
4009b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsForceReload = true;
4019b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
4029b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4030da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        // Use initLoader() instead of restartLoader() to refraining unnecessary reload.
4049b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        // This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
4059b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        // be called, on which we'll check if "all" contacts should be reloaded again or not.
4069b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, mContactTileLoaderListener);
40790f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa
40890f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        // Delay showing "loading" view until certain amount of time so that users won't see
40990f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        // instant flash of the view when the contacts load is fast enough.
41090f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        // This will be kept shown until both tile and all sections are loaded.
41190f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        mLoadingView.setVisibility(View.INVISIBLE);
41290f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_LOADING_EFFECT, LOADING_EFFECT_DELAY);
4139b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
4149b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4159b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
4169b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void onStop() {
4179b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        super.onStop();
4189b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        mContactsPrefs.unregisterChangeListener();
4199b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
4209b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4219b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    /**
4229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * {@inheritDoc}
4239b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     *
4249b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * This is only effective for elements provided by {@link #mContactTileAdapter}.
4259b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * {@link #mContactTileAdapter} has its own logic for click events.
4269b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     */
4279b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    @Override
4289b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
4299b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        final int contactTileAdapterCount = mContactTileAdapter.getCount();
4309b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (position <= contactTileAdapterCount) {
4319b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            Log.e(TAG, "onItemClick() event for unexpected position. "
4329b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                    + "The position " + position + " is before \"all\" section. Ignored.");
4339b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        } else {
4349b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            final int localPosition = position - mContactTileAdapter.getCount() - 1;
4359b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            if (mListener != null) {
4369b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                mListener.onContactSelected(mAllContactsAdapter.getDataUri(localPosition));
4379b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            }
4389b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
4399b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
4409b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
44134b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    @Override
44234b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    public void onActivityResult(int requestCode, int resultCode, Intent data) {
44334b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa        if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
44434b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            if (getActivity() != null) {
44534b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa                AccountFilterUtil.handleAccountFilterResult(
44634b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa                        ContactListFilterController.getInstance(getActivity()), resultCode, data);
44734b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            } else {
44834b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa                Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
44934b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            }
45034b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa        }
45134b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa    }
45234b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa
4539b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private boolean loadContactsPreferences() {
4549b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (mContactsPrefs == null || mAllContactsAdapter == null) {
4559b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            return false;
4569b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
4579b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4589b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        boolean changed = false;
4590da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        final int currentDisplayOrder = mContactsPrefs.getDisplayOrder();
4600da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        if (mAllContactsAdapter.getContactNameDisplayOrder() != currentDisplayOrder) {
4610da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa            mAllContactsAdapter.setContactNameDisplayOrder(currentDisplayOrder);
4629b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            changed = true;
4639b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
4649b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4650da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        final int currentSortOrder = mContactsPrefs.getSortOrder();
4660da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa        if (mAllContactsAdapter.getSortOrder() != currentSortOrder) {
4670da88d6359b1b8c7f4998d7f58a467ad65d40f84Daisuke Miyakawa            mAllContactsAdapter.setSortOrder(currentSortOrder);
4689b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            changed = true;
4699b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
4709b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4719b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        return changed;
4729b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
4739b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4749b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    /**
4759b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * Requests to reload "all" contacts. If the section is already loaded, this method will
4769b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * force reloading it now. If the section isn't loaded yet, the actual load may be done later
4779b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     * (on {@link #onStart()}.
4789b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa     */
4799b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private void requestReloadAllContacts() {
4809b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (DEBUG) {
4819b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            Log.d(TAG, "requestReloadAllContacts()"
4829b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                    + " mAllContactsAdapter: " + mAllContactsAdapter
4839b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                    + ", mAllContactsLoaderStarted: " + mAllContactsLoaderStarted);
4849b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
4859b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4869b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (mAllContactsAdapter == null || !mAllContactsLoaderStarted) {
4879b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            // Remember this request until next load on onStart().
4889b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            mAllContactsForceReload = true;
4899b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            return;
4909b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
4919b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4929b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (DEBUG) Log.d(TAG, "Reload \"all\" contacts now.");
4939b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4949b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        mAllContactsAdapter.onDataReload();
4959b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        // Use restartLoader() to make LoaderManager to load the section again.
4969b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        getLoaderManager().restartLoader(LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
4979b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
4989b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
4999b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    private void updateFilterHeaderView() {
5009b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        final ContactListFilter filter = getFilter();
50134b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa        if (mAccountFilterHeader == null || mAllContactsAdapter == null || filter == null) {
50234b901ca7781cf7fdd4f6275ef98ae73c95749c2Daisuke Miyakawa            return;
5039b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
50490f40d0162e4cee5b94bf3203c059e27f6472fabDaisuke Miyakawa        AccountFilterUtil.updateAccountFilterTitleForPhone(mAccountFilterHeader, filter, true);
5059b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
5069b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5079b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public ContactListFilter getFilter() {
5089b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        return mFilter;
5099b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
5109b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5119b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void setFilter(ContactListFilter filter) {
5129b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if ((mFilter == null && filter == null) || (mFilter != null && mFilter.equals(filter))) {
5139b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            return;
5149b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
5159b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5169b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        if (DEBUG) {
5179b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa            Log.d(TAG, "setFilter(). old filter (" + mFilter
5189b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa                    + ") will be replaced with new filter (" + filter + ")");
5199b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        }
5209b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5219b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        mFilter = filter;
5229b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5235db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        if (mAllContactsAdapter != null) {
5245db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa            mAllContactsAdapter.setFilter(mFilter);
5255db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa            requestReloadAllContacts();
5265db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa            updateFilterHeaderView();
5275db4f136163675105813900b1f3f7da48bde97b6Daisuke Miyakawa        }
5289b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
5299b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa
5309b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    public void setListener(Listener listener) {
5319b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa        mListener = listener;
5329b9104874ec0680ac60cb0527d127fc1c34bdb7eDaisuke Miyakawa    }
533aabc1e81c2d43827fe2ad0bdde6851bdfb431924Josh Gargus}
534