SpeedDialFragment.java revision 4ed7db87e6c0e71b2f656e77de938f3579bc36bf
16b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee/*
2dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * Copyright (C) 2013 The Android Open Source Project
36b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee *
46b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * Licensed under the Apache License, Version 2.0 (the "License");
56b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * you may not use this file except in compliance with the License.
66b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * You may obtain a copy of the License at
76b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee *
86b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee *      http://www.apache.org/licenses/LICENSE-2.0
96b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee *
106b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * Unless required by applicable law or agreed to in writing, software
116b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * distributed under the License is distributed on an "AS IS" BASIS,
126b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * See the License for the specific language governing permissions and
146b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * limitations under the License.
156b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee */
166b049128c51b90e17ae14856d98130a22d3a5433Yorke Leepackage com.android.dialer.list;
176b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
185b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wangimport android.animation.Animator;
195b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wangimport android.animation.AnimatorSet;
204fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chenimport android.animation.ArgbEvaluator;
215b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wangimport android.animation.ObjectAnimator;
224fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chenimport android.animation.ValueAnimator;
236b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.app.Activity;
246b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.app.Fragment;
256b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.app.LoaderManager;
2621299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Leeimport android.content.Context;
276b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.content.CursorLoader;
286b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.content.Loader;
2921299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Leeimport android.content.SharedPreferences;
306b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.database.Cursor;
316b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.graphics.Rect;
326b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.net.Uri;
336b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.os.Bundle;
34c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Chengimport android.provider.CallLog;
356b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.util.Log;
366b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.view.LayoutInflater;
376b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.view.View;
38c36684277aa45085999284bfe71cb8be71b3a464Yorke Leeimport android.view.ViewTreeObserver;
396b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.view.View.OnClickListener;
406b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.view.ViewGroup;
416b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.widget.AbsListView;
426b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.widget.AdapterView;
436b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.widget.AdapterView.OnItemClickListener;
444911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wangimport android.widget.ImageView;
456b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.widget.ListView;
466b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
476b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport com.android.contacts.common.ContactPhotoManager;
486b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport com.android.contacts.common.ContactTileLoaderFactory;
49dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.contacts.common.GeoUtil;
50c36684277aa45085999284bfe71cb8be71b3a464Yorke Leeimport com.android.contacts.common.list.ContactEntry;
516b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport com.android.contacts.common.list.ContactTileView;
5221299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Leeimport com.android.dialer.DialtactsActivity;
536b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport com.android.dialer.R;
5421299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Leeimport com.android.dialer.calllog.CallLogQuery;
55dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.dialer.calllog.ContactInfoHelper;
56fb585079cc4c522c27f6dd6bf03fd296535960f3Yorke Leeimport com.android.dialer.calllog.CallLogAdapter;
57dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.dialer.calllog.CallLogQueryHandler;
58c36684277aa45085999284bfe71cb8be71b3a464Yorke Leeimport com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
59704acc087ce359295475a46695c2821c55778344Chiao Chengimport com.android.dialerbind.ObjectFactory;
60c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
61c36684277aa45085999284bfe71cb8be71b3a464Yorke Leeimport java.util.ArrayList;
62c36684277aa45085999284bfe71cb8be71b3a464Yorke Leeimport java.util.HashMap;
636b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
646b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee/**
656b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * Fragment for Phone UI's favorite screen.
666b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee *
676b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * This fragment contains three kinds of contacts in one screen: "starred", "frequent", and "all"
686b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * contacts. To show them at once, this merges results from {@link com.android.contacts.common.list.ContactTileAdapter} and
696b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * {@link com.android.contacts.common.list.PhoneNumberListAdapter} into one unified list using {@link PhoneFavoriteMergedAdapter}.
706b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee * A contact filter header is also inserted between those adapters' results.
716b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee */
728898cd0577d41cfa4e6acf785c9e904bd71fbdfdYorke Leepublic class PhoneFavoriteFragment extends Fragment implements OnItemClickListener,
73c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        CallLogQueryHandler.Listener, CallLogAdapter.CallFetcher,
74c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        PhoneFavoritesTileAdapter.OnDataSetChangedForAnimationListener {
75c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
764ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee    /**
774ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     * By default, the animation code assumes that all items in a list view are of the same height
784ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     * when animating new list items into view (e.g. from the bottom of the screen into view).
794ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     * This can cause incorrect translation offsets when a item that is larger or smaller than
804ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     * other list item is removed from the list. This key is used to provide the actual height
814ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     * of the removed object so that the actual translation appears correct to the user.
824ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     */
834ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee    private static final long KEY_REMOVED_ITEM_HEIGHT = Long.MAX_VALUE;
844ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee
858898cd0577d41cfa4e6acf785c9e904bd71fbdfdYorke Lee    private static final String TAG = PhoneFavoriteFragment.class.getSimpleName();
863ed80a357d780310777178ad589d7771d4f7bf2dYorke Lee    private static final boolean DEBUG = false;
87c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
884911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang    private int mAnimationDuration;
896b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
906b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    /**
916b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     * Used with LoaderManager.
926b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     */
936b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private static int LOADER_ID_CONTACT_TILE = 1;
94c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng    private static int MISSED_CALL_LOADER = 2;
956b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
9621299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee    private static final String KEY_LAST_DISMISSED_CALL_SHORTCUT_DATE =
9721299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee            "key_last_dismissed_call_shortcut_date";
9821299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee
998dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee    public interface OnShowAllContactsListener {
1008dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee        public void onShowAllContacts();
1018dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee    }
1028dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee
1036b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    public interface Listener {
1046b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onContactSelected(Uri contactUri);
1056b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onCallNumberDirectly(String phoneNumber);
1066b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
1076b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
108c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng    private class MissedCallLogLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
109c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng
110c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng        @Override
111c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
112c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng            final Uri uri = CallLog.Calls.CONTENT_URI;
113c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng            final String[] projection = new String[] {CallLog.Calls.TYPE};
114c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng            final String selection = CallLog.Calls.TYPE + " = " + CallLog.Calls.MISSED_TYPE +
115c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng                    " AND " + CallLog.Calls.IS_READ + " = 0";
116c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng            return new CursorLoader(getActivity(), uri, projection, selection, null, null);
117c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng        }
118c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng
119c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng        @Override
120c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng        public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor data) {
121c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng            mCallLogAdapter.setMissedCalls(data);
122c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng        }
123c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng
124c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng        @Override
125c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng        public void onLoaderReset(Loader<Cursor> cursorLoader) {
126c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng        }
127c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng    }
128c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng
1296b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
1306b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1316b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public CursorLoader onCreateLoader(int id, Bundle args) {
1326b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onCreateLoader.");
1336b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity());
1346b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1356b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1366b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1376b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
1386b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoadFinished");
1396b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            mContactTileAdapter.setContactCursor(data);
1404fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen            setEmptyViewVisibility(mContactTileAdapter.getCount() == 0);
1416b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1426b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1436b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1446b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onLoaderReset(Loader<Cursor> loader) {
1456b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoaderReset. ");
1466b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1476b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
1486b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1496b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private class ContactTileAdapterListener implements ContactTileView.Listener {
1506b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1516b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onContactSelected(Uri contactUri, Rect targetRect) {
1526b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            if (mListener != null) {
1536b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee                mListener.onContactSelected(contactUri);
1546b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            }
1556b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1566b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1576b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1586b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onCallNumberDirectly(String phoneNumber) {
1596b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            if (mListener != null) {
1606b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee                mListener.onCallNumberDirectly(phoneNumber);
1616b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            }
1626b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1636b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1646b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1656b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public int getApproximateTileWidth() {
1666b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            return getView().getWidth() / mContactTileAdapter.getColumnCount();
1676b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1686b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
1696b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1706b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private class ScrollListener implements ListView.OnScrollListener {
1716b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1726b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onScroll(AbsListView view,
1736b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee                int firstVisibleItem, int visibleItemCount, int totalItemCount) {
1746b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1756b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1766b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1776b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onScrollStateChanged(AbsListView view, int scrollState) {
178dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
1796b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1806b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
1816b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1826b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private Listener mListener;
183dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
184dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private OnListFragmentScrolledListener mActivityScrollListener;
1858dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee    private OnShowAllContactsListener mShowAllContactsListener;
1868898cd0577d41cfa4e6acf785c9e904bd71fbdfdYorke Lee    private PhoneFavoriteMergedAdapter mAdapter;
187dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private PhoneFavoritesTileAdapter mContactTileAdapter;
1886b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
189fb585079cc4c522c27f6dd6bf03fd296535960f3Yorke Lee    private CallLogAdapter mCallLogAdapter;
190dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private CallLogQueryHandler mCallLogQueryHandler;
191dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
1924fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen    private View mParentView;
1934fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen
194bf5b298b1a5752a56315131a22434185198370a8Hongwei Wang    private PhoneFavoriteListView mListView;
19518e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee
1966091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen    private View mShowAllContactsButton;
1974fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen    private View mShowAllContactsInEmptyViewButton;
1986b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
199c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    private final HashMap<Long, Integer> mItemIdTopMap = new HashMap<Long, Integer>();
200c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    private final HashMap<Long, Integer> mItemIdLeftMap = new HashMap<Long, Integer>();
201c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
2026b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    /**
20318e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee     * Layout used when there are no favorites.
2046b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     */
20518e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee    private View mEmptyView;
2066b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
20721299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee    /**
20821299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee     * Call shortcuts older than this date (persisted in shared preferences) will not show up in
20921299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee     * at the top of the screen
21021299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee     */
21121299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee    private long mLastCallShortcutDate = 0;
21221299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee
21321299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee    /**
21421299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee     * The date of the current call shortcut that is showing on screen.
21521299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee     */
21621299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee    private long mCurrentCallShortcutDate = 0;
21721299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee
2186b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private final ContactTileView.Listener mContactTileAdapterListener =
2196b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            new ContactTileAdapterListener();
2206b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener =
2216b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            new ContactTileLoaderListener();
2226b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private final ScrollListener mScrollListener = new ScrollListener();
2236b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2246b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
2256b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    public void onAttach(Activity activity) {
2266b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        if (DEBUG) Log.d(TAG, "onAttach()");
2276b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        super.onAttach(activity);
2286b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2296b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // Construct two base adapters which will become part of PhoneFavoriteMergedAdapter.
2306b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // We don't construct the resultant adapter at this moment since it requires LayoutInflater
2316b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // that will be available on onCreateView().
232dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mContactTileAdapter = new PhoneFavoritesTileAdapter(activity, mContactTileAdapterListener,
233c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                this,
234dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                getResources().getInteger(R.integer.contact_tile_column_count_in_favorites_new),
235dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                1);
2366b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
2376b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
2386b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2396b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
2406b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    public void onCreate(Bundle savedState) {
2416b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        if (DEBUG) Log.d(TAG, "onCreate()");
2426b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        super.onCreate(savedState);
2436b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2444911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang        mAnimationDuration = getResources().getInteger(R.integer.fade_duration);
245dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mCallLogQueryHandler = new CallLogQueryHandler(getActivity().getContentResolver(),
246dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                this, 1);
247dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        final String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
248704acc087ce359295475a46695c2821c55778344Chiao Cheng        mCallLogAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this,
249704acc087ce359295475a46695c2821c55778344Chiao Cheng                new ContactInfoHelper(getActivity(), currentCountryIso), true, false);
2506b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        setHasOptionsMenu(true);
2516b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
2526b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2536b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
254dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void onResume() {
255dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        super.onResume();
25621299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee        final SharedPreferences prefs = getActivity().getSharedPreferences(
25721299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee                DialtactsActivity.SHARED_PREFS_NAME, Context.MODE_PRIVATE);
25821299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee
25921299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee        mLastCallShortcutDate = prefs.getLong(KEY_LAST_DISMISSED_CALL_SHORTCUT_DATE, 0);
26021299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee
2614dc32bdf10bff18acbc1964d289354556950d2ebYorke Lee        fetchCalls();
262dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mCallLogAdapter.setLoading(true);
26311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE).forceLoad();
2646b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
2656b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2666b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
2676b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    public View onCreateView(LayoutInflater inflater, ViewGroup container,
2686b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            Bundle savedInstanceState) {
2694fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        mParentView = inflater.inflate(R.layout.phone_favorites_fragment, container, false);
2706b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2714fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        mListView = (PhoneFavoriteListView) mParentView.findViewById(R.id.contact_tile_list);
2726b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setItemsCanFocus(true);
2736b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setOnItemClickListener(this);
2746b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setVerticalScrollBarEnabled(false);
2756b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
2766b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
27711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mListView.setOnItemSwipeListener(mContactTileAdapter);
2787639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang        mListView.setOnDragDropListener(mContactTileAdapter);
2796b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2804911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang        final ImageView dragShadowOverlay =
2814fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen                (ImageView) mParentView.findViewById(R.id.contact_tile_drag_shadow_overlay);
2824911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang        mListView.setDragShadowOverlay(dragShadowOverlay);
2834911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang
2844fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        mEmptyView = mParentView.findViewById(R.id.phone_no_favorites_view);
2854fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen
2864fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        mShowAllContactsInEmptyViewButton = mParentView.findViewById(
2874fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen                R.id.show_all_contact_button_in_nofav);
2884fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        mShowAllContactsInEmptyViewButton.setOnClickListener(new OnClickListener() {
2894fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen            @Override
2904fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen            public void onClick(View view) {
2914fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen                showAllContacts();
2924fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen            }
2934fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        });
29418e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee
2956091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen        mShowAllContactsButton = inflater.inflate(R.layout.show_all_contact_button, mListView,
2966091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen                false);
2976091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen        mShowAllContactsButton.setOnClickListener(new OnClickListener() {
2986091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen            @Override
2996091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen            public void onClick(View view) {
3006091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen                showAllContacts();
3016091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen            }
3026091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen        });
3036b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
30421299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee        mAdapter = new PhoneFavoriteMergedAdapter(getActivity(), this, mContactTileAdapter,
30518e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee                mCallLogAdapter, mShowAllContactsButton);
3066b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
3076b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setAdapter(mAdapter);
3086b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
3096b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setOnScrollListener(mScrollListener);
3106b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setFastScrollEnabled(false);
3116b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setFastScrollAlwaysVisible(false);
3126b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
3134fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        return mParentView;
3146b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
3156b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
316f420a5344995b2c450379159f679809690bc9880Yorke Lee    public boolean hasFrequents() {
317f420a5344995b2c450379159f679809690bc9880Yorke Lee        if (mContactTileAdapter == null) return false;
3186b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        return mContactTileAdapter.getNumFrequents() > 0;
3196b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
3206b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
3214fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen    /* package */ void setEmptyViewVisibility(final boolean visible) {
3224fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        final int previousVisibility = mEmptyView.getVisibility();
3234fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        final int newVisibility = visible ? View.VISIBLE : View.GONE;
3244fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen
3254fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        if (previousVisibility != newVisibility) {
326fe3fbd24097c51290b189f2788059c4083dd90a1Christine Chen            mEmptyView.setVisibility(newVisibility);
3274fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        }
3284fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen    }
3294fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen
3306b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
3316b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    public void onStart() {
3326b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        super.onStart();
3336b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
334dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        final Activity activity = getActivity();
335dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
336dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        try {
337dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            mActivityScrollListener = (OnListFragmentScrolledListener) activity;
338dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        } catch (ClassCastException e) {
339dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            throw new ClassCastException(activity.toString()
340dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    + " must implement OnListFragmentScrolledListener");
341dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
3426b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
3438dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee        try {
3448dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee            mShowAllContactsListener = (OnShowAllContactsListener) activity;
3458dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee        } catch (ClassCastException e) {
3468dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee            throw new ClassCastException(activity.toString()
3478dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee                    + " must implement OnShowAllContactsListener");
3488dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee        }
3498dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee
3506b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // Use initLoader() instead of restartLoader() to refraining unnecessary reload.
3516b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
3526b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // be called, on which we'll check if "all" contacts should be reloaded again or not.
3536b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, mContactTileLoaderListener);
354c5b6a693d4773214ff8d3fcfa43d0a29581bb886Chiao Cheng        getLoaderManager().initLoader(MISSED_CALL_LOADER, null, new MissedCallLogLoaderListener());
3556b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
3566b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
3576b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    /**
3586b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     * {@inheritDoc}
3596b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     *
3606b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     * This is only effective for elements provided by {@link #mContactTileAdapter}.
3616b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     * {@link #mContactTileAdapter} has its own logic for click events.
3626b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     */
3636b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
3646b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
3656b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        final int contactTileAdapterCount = mContactTileAdapter.getCount();
3666b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        if (position <= contactTileAdapterCount) {
3676b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            Log.e(TAG, "onItemClick() event for unexpected position. "
3686b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee                    + "The position " + position + " is before \"all\" section. Ignored.");
3696b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
3706b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
3716b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
3726b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    /**
3736091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen     * Gets called when user click on the show all contacts button.
3746b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     */
3756091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen    private void showAllContacts() {
3768dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee        mShowAllContactsListener.onShowAllContacts();
3776b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
3786b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
379dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void setListener(Listener listener) {
380dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mListener = listener;
3816b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
3826b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
383dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
384dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void onVoicemailStatusFetched(Cursor statusCursor) {
3854dc32bdf10bff18acbc1964d289354556950d2ebYorke Lee        // no-op
3866b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
3876b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
388dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
389dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void onCallsFetched(Cursor cursor) {
3904ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee        animateListView();
391dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mCallLogAdapter.setLoading(false);
39221299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee
39321299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee        // Save the date of the most recent call log item
39421299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee        if (cursor != null && cursor.moveToFirst()) {
39521299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee            mCurrentCallShortcutDate = cursor.getLong(CallLogQuery.DATE);
39621299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee        }
39721299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee
398dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mCallLogAdapter.changeCursor(cursor);
399dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mAdapter.notifyDataSetChanged();
4006b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
4016b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
402dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
403dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void fetchCalls() {
40421299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee        mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL, mLastCallShortcutDate);
4056b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
40611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
40711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    @Override
40811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public void onPause() {
40911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        // If there are any pending contact entries that are to be removed, remove them
41011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mContactTileAdapter.removePendingContactEntry();
4113b9d09c50b6ed52cbfe6d87cee06ecef5116d799Yorke Lee        // Wipe the cache to refresh the call shortcut item. This is not that expensive because
4123b9d09c50b6ed52cbfe6d87cee06ecef5116d799Yorke Lee        // it only contains one item.
4133b9d09c50b6ed52cbfe6d87cee06ecef5116d799Yorke Lee        mCallLogAdapter.invalidateCache();
41411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        super.onPause();
41511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
416c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
417c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    /**
418c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * Saves the current view offsets into memory
419c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     */
420c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    @SuppressWarnings("unchecked")
4214ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee    private void saveOffsets(int removedItemHeight) {
422c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        final int firstVisiblePosition = mListView.getFirstVisiblePosition();
423c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        if (DEBUG) {
424c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            Log.d(TAG, "Child count : " + mListView.getChildCount());
425c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
426c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        for (int i = 0; i < mListView.getChildCount(); i++) {
427c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final View child = mListView.getChildAt(i);
428c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final int position = firstVisiblePosition + i;
429c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final long itemId = mAdapter.getItemId(position);
430c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final int itemViewType = mAdapter.getItemViewType(position);
431c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            if (itemViewType == PhoneFavoritesTileAdapter.ViewTypes.TOP) {
432c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                // This is a tiled row, so save horizontal offsets instead
433c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                saveHorizontalOffsets((ContactTileRow) child, (ArrayList<ContactEntry>)
4344ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee                        mAdapter.getItem(position));
435c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
436c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            if (DEBUG) {
437c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                Log.d(TAG, "Saving itemId: " + itemId + " for listview child " + i + " Top: "
438c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                        + child.getTop());
439c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
440c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            mItemIdTopMap.put(itemId, child.getTop());
441c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
4424ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee
4434ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee        mItemIdTopMap.put(KEY_REMOVED_ITEM_HEIGHT, removedItemHeight);
444c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
445c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
4464ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee    private void saveHorizontalOffsets(ContactTileRow row, ArrayList<ContactEntry> list) {
447c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        for (int i = 0; i < list.size(); i++) {
448c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final View child = row.getChildAt(i);
449c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final ContactEntry entry = list.get(i);
450c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final long itemId = mContactTileAdapter.getAdjustedItemId(entry.id);
451c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            if (DEBUG) {
452c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                Log.d(TAG, "Saving itemId: " + itemId + " for tileview child " + i + " Left: "
453c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                        + child.getTop());
454c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
455c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            mItemIdLeftMap.put(itemId, child.getLeft());
456c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
457c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
458c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
459c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    /*
460c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * Performs a animations for a row of tiles
461c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     */
462c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    private void performHorizontalAnimations(ContactTileRow row, ArrayList<ContactEntry> list,
463c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            long[] idsInPlace) {
464c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        if (mItemIdLeftMap.isEmpty()) {
465c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            return;
466c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
4675b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang        final AnimatorSet animSet = new AnimatorSet();
4685b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang        final ArrayList<Animator> animators = new ArrayList<Animator>();
469c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        for (int i = 0; i < list.size(); i++) {
470c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final View child = row.getChildAt(i);
471c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final ContactEntry entry = list.get(i);
472c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final long itemId = mContactTileAdapter.getAdjustedItemId(entry.id);
473c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
4744911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang            if (containsId(idsInPlace, itemId)) {
4755b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                animators.add(ObjectAnimator.ofFloat(
4765b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        child, "alpha", 0.0f, 1.0f));
4774911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang                break;
4785b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang            } else {
4795b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                Integer startLeft = mItemIdLeftMap.get(itemId);
4805b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                int left = child.getLeft();
4815b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                if (startLeft != null) {
4825b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                    if (startLeft != left) {
4835b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        int delta = startLeft - left;
4845b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        if (DEBUG) {
4855b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                            Log.d(TAG, "Found itemId: " + itemId + " for tileview child " + i +
4865b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                                    " Left: " + left +
4875b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                                    " Delta: " + delta);
4885b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        }
4895b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        animators.add(ObjectAnimator.ofFloat(
4905b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                                child, "translationX", delta, 0.0f));
4915b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                    }
4925b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                } else {
4935b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                    // In case the last square row is pushed up from the non-square section.
4945b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                    animators.add(ObjectAnimator.ofFloat(
4955b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                            child, "translationX", left, 0.0f));
4965b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                }
497c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
498c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
4995b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang        if (animators.size() > 0) {
5005b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang            animSet.setDuration(mAnimationDuration).playTogether(animators);
5015b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang            animSet.start();
5025b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang        }
503c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
504c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
505c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    /*
506c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * Performs animations for the list view. If the list item is a row of tiles, horizontal
507c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * animations will be performed instead.
508c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     */
509c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    private void animateListView(final long... idsInPlace) {
51020d682f9a586da495f180bfb6a445520d027c74dHongwei Wang        if (mItemIdTopMap.isEmpty()) {
511c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            // Don't do animations if the database is being queried for the first time and
512c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            // the previous item offsets have not been cached, or the user hasn't done anything
513c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            // (dragging, swiping etc) that requires an animation.
514c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            return;
515c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
5164ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee
5174ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee        final int removedItemHeight = mItemIdTopMap.get(KEY_REMOVED_ITEM_HEIGHT);
5184ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee
519c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        final ViewTreeObserver observer = mListView.getViewTreeObserver();
520c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
521c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            @SuppressWarnings("unchecked")
522c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            @Override
523c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            public boolean onPreDraw() {
524c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                observer.removeOnPreDrawListener(this);
525c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                final int firstVisiblePosition = mListView.getFirstVisiblePosition();
5265b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                final AnimatorSet animSet = new AnimatorSet();
5275b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                final ArrayList<Animator> animators = new ArrayList<Animator>();
528c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                for (int i = 0; i < mListView.getChildCount(); i++) {
529c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    final View child = mListView.getChildAt(i);
530c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    int position = firstVisiblePosition + i;
531c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    final int itemViewType = mAdapter.getItemViewType(position);
532c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    if (itemViewType == PhoneFavoritesTileAdapter.ViewTypes.TOP) {
533c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                        // This is a tiled row, so perform horizontal animations instead
534c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                        performHorizontalAnimations((ContactTileRow) child, (
535c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                                ArrayList<ContactEntry>) mAdapter.getItem(position), idsInPlace);
536c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    }
537c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
538c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    final long itemId = mAdapter.getItemId(position);
539c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
5404911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang                    if (containsId(idsInPlace, itemId)) {
5415b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        animators.add(ObjectAnimator.ofFloat(
5425b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                                child, "alpha", 0.0f, 1.0f));
5435b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        break;
544f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                    } else {
545f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        Integer startTop = mItemIdTopMap.get(itemId);
546f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        final int top = child.getTop();
547f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        int delta = 0;
548f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        if (startTop != null) {
549f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                            if (startTop != top) {
550f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                                delta = startTop - top;
551f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                            }
552f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        } else if (!mItemIdLeftMap.containsKey(itemId)) {
553f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                            // Animate new views along with the others. The catch is that they did
554f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                            // not exist in the start state, so we must calculate their starting
555f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                            // position based on neighboring views.
5564ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee
5574ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee                            final int itemHeight;
5584ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee                            if (removedItemHeight == 0) {
5594ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee                                itemHeight = child.getHeight() + mListView.getDividerHeight();
5604ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee                            } else {
5614ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee                                itemHeight = removedItemHeight;
5624ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee                            }
5634ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee                            startTop = top + (i > 0 ? itemHeight : -itemHeight);
564f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                            delta = startTop - top;
5655b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        } else {
56620d682f9a586da495f180bfb6a445520d027c74dHongwei Wang                            // In case the first non-square row is pushed down
5675b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                            // from the square section.
5685b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                            animators.add(ObjectAnimator.ofFloat(
56920d682f9a586da495f180bfb6a445520d027c74dHongwei Wang                                    child, "alpha", 0.0f, 1.0f));
5705b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        }
5715b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        if (DEBUG) {
5725b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                            Log.d(TAG, "Found itemId: " + itemId + " for listview child " + i +
5735b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                                    " Top: " + top +
5745b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                                    " Delta: " + delta);
575f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        }
576f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira
577f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        if (delta != 0) {
5785b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                            animators.add(ObjectAnimator.ofFloat(
5795b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                                    child, "translationY", delta, 0.0f));
580f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        }
581c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    }
582c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                }
5835b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang
5845b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                if (animators.size() > 0) {
5855b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                    animSet.setDuration(mAnimationDuration).playTogether(animators);
5865b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                    animSet.start();
5875b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                }
5885b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang
589c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                mItemIdTopMap.clear();
590c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                mItemIdLeftMap.clear();
591c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                return true;
592c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
593c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        });
594c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
595c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
596c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    private boolean containsId(long[] ids, long target) {
597c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        // Linear search on array is fine because this is typically only 0-1 elements long
598c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        for (int i = 0; i < ids.length; i++) {
599c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            if (ids[i] == target) {
600c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                return true;
601c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
602c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
603c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        return false;
604c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
605c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
606c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    @Override
607c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    public void onDataSetChangedForAnimation(long... idsInPlace) {
608c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        animateListView(idsInPlace);
609c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
610c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
611c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    @Override
612c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    public void cacheOffsetsForDatasetChange() {
6134ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee        saveOffsets(0);
614c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
61521299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee
6164ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee    public void dismissShortcut(int height) {
6174ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee        saveOffsets(height);
61821299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee        mLastCallShortcutDate = mCurrentCallShortcutDate;
61921299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee        final SharedPreferences prefs = getActivity().getSharedPreferences(
62021299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee                DialtactsActivity.SHARED_PREFS_NAME, Context.MODE_PRIVATE);
62121299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee        prefs.edit().putLong(KEY_LAST_DISMISSED_CALL_SHORTCUT_DATE, mLastCallShortcutDate)
62221299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee                .apply();
62321299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee        fetchCalls();
62421299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee    }
6256b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee}
626