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
1838019af70eb1ca084d36291390bbc54dc81027deYorke Leeimport static android.Manifest.permission.READ_CONTACTS;
1938019af70eb1ca084d36291390bbc54dc81027deYorke Lee
205b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wangimport android.animation.Animator;
215b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wangimport android.animation.AnimatorSet;
225b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wangimport android.animation.ObjectAnimator;
236b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.app.Activity;
24beab3bbc2c977a48e3affc3e61cc2fdbb22c4ae2Brian Attwellimport android.app.Fragment;
256b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.app.LoaderManager;
266b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.content.CursorLoader;
276b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.content.Loader;
287e1a68556c419c4b9b1eda9247e80262615fa809Yorke Leeimport android.content.pm.PackageManager;
296b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.database.Cursor;
306b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.graphics.Rect;
316b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.net.Uri;
326b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.os.Bundle;
33f5554ceb3c152949b4b6278b7f5e1522148a574cYorke Leeimport android.os.Trace;
346b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.util.Log;
356b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.view.LayoutInflater;
366b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.view.View;
376b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.view.ViewGroup;
38765734c1d602c9a6d166d653b3684e6408b771c4Yorke Leeimport android.view.ViewTreeObserver;
399b08f53ac993cffa249b73d3a2cc9ed5a9e71158Yorke Leeimport android.view.animation.AnimationUtils;
409b08f53ac993cffa249b73d3a2cc9ed5a9e71158Yorke Leeimport android.view.animation.LayoutAnimationController;
416b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.widget.AbsListView;
426b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.widget.AdapterView;
436b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.widget.AdapterView.OnItemClickListener;
44ae6302b51b1f03ddd4865539b071ba2a661b977fYorke Leeimport android.widget.FrameLayout;
45ae6302b51b1f03ddd4865539b071ba2a661b977fYorke Leeimport android.widget.FrameLayout.LayoutParams;
464911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wangimport android.widget.ImageView;
476b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport android.widget.ListView;
486b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
496b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport com.android.contacts.common.ContactPhotoManager;
506b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport com.android.contacts.common.ContactTileLoaderFactory;
516b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport com.android.contacts.common.list.ContactTileView;
52e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Leeimport com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
53c16ea5ad67bf454158b364dec2f26c95a879c350Yorke Leeimport com.android.contacts.common.util.PermissionsUtil;
546b049128c51b90e17ae14856d98130a22d3a5433Yorke Leeimport com.android.dialer.R;
550a4327e6bfeedc23ddefb4df9f6e8041ebc87b59Andrew Leeimport com.android.dialer.util.DialerUtils;
5638019af70eb1ca084d36291390bbc54dc81027deYorke Leeimport com.android.dialer.widget.EmptyContentView;
57c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
58c36684277aa45085999284bfe71cb8be71b3a464Yorke Leeimport java.util.ArrayList;
59c36684277aa45085999284bfe71cb8be71b3a464Yorke Leeimport java.util.HashMap;
606b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
616b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee/**
62d999b71452baebec789f6f58f1955d30e834826dYorke Lee * This fragment displays the user's favorite/frequent contacts in a grid.
636b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee */
64beab3bbc2c977a48e3affc3e61cc2fdbb22c4ae2Brian Attwellpublic class SpeedDialFragment extends Fragment implements OnItemClickListener,
6538019af70eb1ca084d36291390bbc54dc81027deYorke Lee        PhoneFavoritesTileAdapter.OnDataSetChangedForAnimationListener,
6638019af70eb1ca084d36291390bbc54dc81027deYorke Lee        EmptyContentView.OnEmptyViewActionButtonClickedListener {
67c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
687e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee    private static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 1;
697e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee
704ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee    /**
714ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     * By default, the animation code assumes that all items in a list view are of the same height
724ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     * when animating new list items into view (e.g. from the bottom of the screen into view).
734ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     * This can cause incorrect translation offsets when a item that is larger or smaller than
744ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     * other list item is removed from the list. This key is used to provide the actual height
754ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     * of the removed object so that the actual translation appears correct to the user.
764ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee     */
774ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee    private static final long KEY_REMOVED_ITEM_HEIGHT = Long.MAX_VALUE;
784ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee
79f5554ceb3c152949b4b6278b7f5e1522148a574cYorke Lee    private static final String TAG = "SpeedDialFragment";
803ed80a357d780310777178ad589d7771d4f7bf2dYorke Lee    private static final boolean DEBUG = false;
81c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
824911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang    private int mAnimationDuration;
836b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
846b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    /**
856b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     * Used with LoaderManager.
866b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     */
876b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private static int LOADER_ID_CONTACT_TILE = 1;
8821299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee
893cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee    public interface HostInterface {
903cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee        public void setDragDropController(DragDropController controller);
9138019af70eb1ca084d36291390bbc54dc81027deYorke Lee        public void showAllContactsTab();
923cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee    }
933cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee
946b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
956b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
966b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public CursorLoader onCreateLoader(int id, Bundle args) {
976b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onCreateLoader.");
986b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity());
996b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1006b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1016b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1026b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
1036b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoadFinished");
1046b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            mContactTileAdapter.setContactCursor(data);
1054fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen            setEmptyViewVisibility(mContactTileAdapter.getCount() == 0);
1066b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1076b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1086b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1096b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onLoaderReset(Loader<Cursor> loader) {
1106b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoaderReset. ");
1116b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1126b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
1136b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1146b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private class ContactTileAdapterListener implements ContactTileView.Listener {
1156b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1166b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onContactSelected(Uri contactUri, Rect targetRect) {
117e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee            if (mPhoneNumberPickerActionListener != null) {
118e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee                mPhoneNumberPickerActionListener.onPickPhoneNumberAction(contactUri);
1196b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            }
1206b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1216b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1226b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1236b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onCallNumberDirectly(String phoneNumber) {
124e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee            if (mPhoneNumberPickerActionListener != null) {
125e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee                mPhoneNumberPickerActionListener.onCallNumberDirectly(phoneNumber);
1266b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            }
1276b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1286b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1296b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1306b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public int getApproximateTileWidth() {
131efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee            return getView().getWidth();
1326b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1336b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
1346b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1356b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private class ScrollListener implements ListView.OnScrollListener {
1366b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1376b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onScroll(AbsListView view,
1386b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee                int firstVisibleItem, int visibleItemCount, int totalItemCount) {
13986e21f733049560e77b92f17a58474312d72ec3eYorke Lee            if (mActivityScrollListener != null) {
14086e21f733049560e77b92f17a58474312d72ec3eYorke Lee                mActivityScrollListener.onListFragmentScroll(firstVisibleItem, visibleItemCount,
14186e21f733049560e77b92f17a58474312d72ec3eYorke Lee                    totalItemCount);
14286e21f733049560e77b92f17a58474312d72ec3eYorke Lee            }
1436b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1446b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1456b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        @Override
1466b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        public void onScrollStateChanged(AbsListView view, int scrollState) {
147dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
1486b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
1496b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
1506b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
151e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee    private OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener;
152dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
153dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private OnListFragmentScrolledListener mActivityScrollListener;
154dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private PhoneFavoritesTileAdapter mContactTileAdapter;
1556b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1564fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen    private View mParentView;
1574fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen
158bf5b298b1a5752a56315131a22434185198370a8Hongwei Wang    private PhoneFavoriteListView mListView;
15918e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee
1603eb8ab2ab09a9e742cde17e36381a6dde058c4beYorke Lee    private View mContactTileFrame;
1616b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
162c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    private final HashMap<Long, Integer> mItemIdTopMap = new HashMap<Long, Integer>();
163c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    private final HashMap<Long, Integer> mItemIdLeftMap = new HashMap<Long, Integer>();
164c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
1656b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    /**
16618e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee     * Layout used when there are no favorites.
1676b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     */
16838019af70eb1ca084d36291390bbc54dc81027deYorke Lee    private EmptyContentView mEmptyView;
1696b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1706b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private final ContactTileView.Listener mContactTileAdapterListener =
1716b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            new ContactTileAdapterListener();
1726b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener =
1736b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            new ContactTileLoaderListener();
1746b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    private final ScrollListener mScrollListener = new ScrollListener();
1756b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1766b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
1776b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    public void onAttach(Activity activity) {
1786b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        if (DEBUG) Log.d(TAG, "onAttach()");
1796b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        super.onAttach(activity);
1806b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1816b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // Construct two base adapters which will become part of PhoneFavoriteMergedAdapter.
1826b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // We don't construct the resultant adapter at this moment since it requires LayoutInflater
1836b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // that will be available on onCreateView().
184dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mContactTileAdapter = new PhoneFavoritesTileAdapter(activity, mContactTileAdapterListener,
185efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                this);
1866b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
1876b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
1886b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1896b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
1906b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    public void onCreate(Bundle savedState) {
1916b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        if (DEBUG) Log.d(TAG, "onCreate()");
192f5554ceb3c152949b4b6278b7f5e1522148a574cYorke Lee        Trace.beginSection(TAG + " onCreate");
1936b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        super.onCreate(savedState);
1946b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1954911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang        mAnimationDuration = getResources().getInteger(R.integer.fade_duration);
196f5554ceb3c152949b4b6278b7f5e1522148a574cYorke Lee        Trace.endSection();
1976b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
1986b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
1996b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
200dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void onResume() {
201f5554ceb3c152949b4b6278b7f5e1522148a574cYorke Lee        Trace.beginSection(TAG + " onResume");
202dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        super.onResume();
20321299c354c4178dd8f89eef42872cbd62cd4aaf0Yorke Lee
204c16ea5ad67bf454158b364dec2f26c95a879c350Yorke Lee        if (PermissionsUtil.hasContactsPermissions(getActivity())) {
2052a613022dbb278f0f7dd94cf49885cde9d36100cYorke Lee            if (getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE) == null) {
2062a613022dbb278f0f7dd94cf49885cde9d36100cYorke Lee                getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null,
2072a613022dbb278f0f7dd94cf49885cde9d36100cYorke Lee                        mContactTileLoaderListener);
20838019af70eb1ca084d36291390bbc54dc81027deYorke Lee
2092a613022dbb278f0f7dd94cf49885cde9d36100cYorke Lee            } else {
2102a613022dbb278f0f7dd94cf49885cde9d36100cYorke Lee                getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE).forceLoad();
2112a613022dbb278f0f7dd94cf49885cde9d36100cYorke Lee            }
21238019af70eb1ca084d36291390bbc54dc81027deYorke Lee
21338019af70eb1ca084d36291390bbc54dc81027deYorke Lee            mEmptyView.setDescription(R.string.speed_dial_empty);
21438019af70eb1ca084d36291390bbc54dc81027deYorke Lee            mEmptyView.setActionLabel(R.string.speed_dial_empty_add_favorite_action);
21538019af70eb1ca084d36291390bbc54dc81027deYorke Lee        } else {
21638019af70eb1ca084d36291390bbc54dc81027deYorke Lee            mEmptyView.setDescription(R.string.permission_no_speeddial);
21738019af70eb1ca084d36291390bbc54dc81027deYorke Lee            mEmptyView.setActionLabel(R.string.permission_single_turn_on);
218c16ea5ad67bf454158b364dec2f26c95a879c350Yorke Lee        }
219f5554ceb3c152949b4b6278b7f5e1522148a574cYorke Lee        Trace.endSection();
2206b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
2216b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2226b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
2236b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    public View onCreateView(LayoutInflater inflater, ViewGroup container,
2246b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            Bundle savedInstanceState) {
225f5554ceb3c152949b4b6278b7f5e1522148a574cYorke Lee        Trace.beginSection(TAG + " onCreateView");
22639f773e8a64ec1b770b2679871b7c44cac7243d2Andrew Lee        mParentView = inflater.inflate(R.layout.speed_dial_fragment, container, false);
2276b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2284fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        mListView = (PhoneFavoriteListView) mParentView.findViewById(R.id.contact_tile_list);
2296b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setOnItemClickListener(this);
2306b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setVerticalScrollBarEnabled(false);
2316b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
2326b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
2333cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee        mListView.getDragDropController().addOnDragDropListener(mContactTileAdapter);
2346b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2354911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang        final ImageView dragShadowOverlay =
236f66cc0403faab0a31ccdb2c78e0dad5be11218f5Yorke Lee                (ImageView) getActivity().findViewById(R.id.contact_tile_drag_shadow_overlay);
2374911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang        mListView.setDragShadowOverlay(dragShadowOverlay);
2384911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang
23938019af70eb1ca084d36291390bbc54dc81027deYorke Lee        mEmptyView = (EmptyContentView) mParentView.findViewById(R.id.empty_list_view);
24038019af70eb1ca084d36291390bbc54dc81027deYorke Lee        mEmptyView.setImage(R.drawable.empty_speed_dial);
24138019af70eb1ca084d36291390bbc54dc81027deYorke Lee        mEmptyView.setActionClickedListener(this);
2424fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen
2433eb8ab2ab09a9e742cde17e36381a6dde058c4beYorke Lee        mContactTileFrame = mParentView.findViewById(R.id.contact_tile_frame);
2443eb8ab2ab09a9e742cde17e36381a6dde058c4beYorke Lee
2459b08f53ac993cffa249b73d3a2cc9ed5a9e71158Yorke Lee        final LayoutAnimationController controller = new LayoutAnimationController(
2469b08f53ac993cffa249b73d3a2cc9ed5a9e71158Yorke Lee                AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in));
2479b08f53ac993cffa249b73d3a2cc9ed5a9e71158Yorke Lee        controller.setDelay(0);
2489b08f53ac993cffa249b73d3a2cc9ed5a9e71158Yorke Lee        mListView.setLayoutAnimation(controller);
249efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        mListView.setAdapter(mContactTileAdapter);
2506b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2516b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setOnScrollListener(mScrollListener);
2526b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setFastScrollEnabled(false);
2536b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        mListView.setFastScrollAlwaysVisible(false);
254f5554ceb3c152949b4b6278b7f5e1522148a574cYorke Lee        Trace.endSection();
2554fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        return mParentView;
2566b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
2576b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
258f420a5344995b2c450379159f679809690bc9880Yorke Lee    public boolean hasFrequents() {
259f420a5344995b2c450379159f679809690bc9880Yorke Lee        if (mContactTileAdapter == null) return false;
2606b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        return mContactTileAdapter.getNumFrequents() > 0;
2616b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
2626b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2634fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen    /* package */ void setEmptyViewVisibility(final boolean visible) {
2644fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        final int previousVisibility = mEmptyView.getVisibility();
2650bbd606a3d1da11a38fdc127f4067b06f71174f8Ihab Awad        final int emptyViewVisibility = visible ? View.VISIBLE : View.GONE;
2660bbd606a3d1da11a38fdc127f4067b06f71174f8Ihab Awad        final int listViewVisibility = visible ? View.GONE : View.VISIBLE;
2674fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen
2680bbd606a3d1da11a38fdc127f4067b06f71174f8Ihab Awad        if (previousVisibility != emptyViewVisibility) {
269ae6302b51b1f03ddd4865539b071ba2a661b977fYorke Lee            final FrameLayout.LayoutParams params = (LayoutParams) mContactTileFrame
2703eb8ab2ab09a9e742cde17e36381a6dde058c4beYorke Lee                    .getLayoutParams();
2713eb8ab2ab09a9e742cde17e36381a6dde058c4beYorke Lee            params.height = visible ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT;
2723eb8ab2ab09a9e742cde17e36381a6dde058c4beYorke Lee            mContactTileFrame.setLayoutParams(params);
2730bbd606a3d1da11a38fdc127f4067b06f71174f8Ihab Awad            mEmptyView.setVisibility(emptyViewVisibility);
2740bbd606a3d1da11a38fdc127f4067b06f71174f8Ihab Awad            mListView.setVisibility(listViewVisibility);
2754fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        }
2764fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen    }
2774fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen
2786b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
2796b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    public void onStart() {
2806b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        super.onStart();
2816b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
282dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        final Activity activity = getActivity();
283dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
284dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        try {
285dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            mActivityScrollListener = (OnListFragmentScrolledListener) activity;
286dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        } catch (ClassCastException e) {
287dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            throw new ClassCastException(activity.toString()
288dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    + " must implement OnListFragmentScrolledListener");
289dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
2906b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
2918dd6200bef2eb07486f4c7e288941cfcfed35e79Yorke Lee        try {
2923cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee            OnDragDropListener listener = (OnDragDropListener) activity;
2933cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee            mListView.getDragDropController().addOnDragDropListener(listener);
2943cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee            ((HostInterface) activity).setDragDropController(mListView.getDragDropController());
2953cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee        } catch (ClassCastException e) {
2963cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee            throw new ClassCastException(activity.toString()
2973cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee                    + " must implement OnDragDropListener and HostInterface");
2983cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee        }
2993cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee
300e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee        try {
301e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee            mPhoneNumberPickerActionListener = (OnPhoneNumberPickerActionListener) activity;
302e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee        } catch (ClassCastException e) {
303e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee            throw new ClassCastException(activity.toString()
304e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee                    + " must implement PhoneFavoritesFragment.listener");
305e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee        }
306e00c9fe163d19ee380b922e3fcbe736216d78cccYorke Lee
3076b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // Use initLoader() instead of restartLoader() to refraining unnecessary reload.
3086b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
3096b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        // be called, on which we'll check if "all" contacts should be reloaded again or not.
310c16ea5ad67bf454158b364dec2f26c95a879c350Yorke Lee        if (PermissionsUtil.hasContactsPermissions(activity)) {
311c16ea5ad67bf454158b364dec2f26c95a879c350Yorke Lee            getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, mContactTileLoaderListener);
312c16ea5ad67bf454158b364dec2f26c95a879c350Yorke Lee        } else {
313c16ea5ad67bf454158b364dec2f26c95a879c350Yorke Lee            setEmptyViewVisibility(true);
314c16ea5ad67bf454158b364dec2f26c95a879c350Yorke Lee        }
3156b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
3166b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
3176b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    /**
3186b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     * {@inheritDoc}
3196b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     *
3206b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     * This is only effective for elements provided by {@link #mContactTileAdapter}.
3216b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     * {@link #mContactTileAdapter} has its own logic for click events.
3226b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee     */
3236b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    @Override
3246b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
3256b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        final int contactTileAdapterCount = mContactTileAdapter.getCount();
3266b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        if (position <= contactTileAdapterCount) {
3276b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee            Log.e(TAG, "onItemClick() event for unexpected position. "
3286b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee                    + "The position " + position + " is before \"all\" section. Ignored.");
3296b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee        }
3306b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee    }
3316b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee
332c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    /**
333765734c1d602c9a6d166d653b3684e6408b771c4Yorke Lee     * Cache the current view offsets into memory. Once a relayout of views in the ListView
334765734c1d602c9a6d166d653b3684e6408b771c4Yorke Lee     * has happened due to a dataset change, the cached offsets are used to create animations
335765734c1d602c9a6d166d653b3684e6408b771c4Yorke Lee     * that slide views from their previous positions to their new ones, to give the appearance
336765734c1d602c9a6d166d653b3684e6408b771c4Yorke Lee     * that the views are sliding into their new positions.
337c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     */
3384ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee    private void saveOffsets(int removedItemHeight) {
339c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        final int firstVisiblePosition = mListView.getFirstVisiblePosition();
340c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        if (DEBUG) {
341c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            Log.d(TAG, "Child count : " + mListView.getChildCount());
342c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
343c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        for (int i = 0; i < mListView.getChildCount(); i++) {
344c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final View child = mListView.getChildAt(i);
345c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final int position = firstVisiblePosition + i;
346e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee            // Since we are getting the position from mListView and then querying
347e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee            // mContactTileAdapter, its very possible that things are out of sync
348e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee            // and we might index out of bounds.  Let's make sure that this doesn't happen.
349e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee            if (!mContactTileAdapter.isIndexInBound(position)) {
350e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee                continue;
351e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee            }
352efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee            final long itemId = mContactTileAdapter.getItemId(position);
353c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            if (DEBUG) {
354c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                Log.d(TAG, "Saving itemId: " + itemId + " for listview child " + i + " Top: "
355c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                        + child.getTop());
356c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
357c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            mItemIdTopMap.put(itemId, child.getTop());
358c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            mItemIdLeftMap.put(itemId, child.getLeft());
359c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
360efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        mItemIdTopMap.put(KEY_REMOVED_ITEM_HEIGHT, removedItemHeight);
361c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
362c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
363c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    /*
364efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee     * Performs animations for the gridView
365c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     */
366efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee    private void animateGridView(final long... idsInPlace) {
36720d682f9a586da495f180bfb6a445520d027c74dHongwei Wang        if (mItemIdTopMap.isEmpty()) {
368c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            // Don't do animations if the database is being queried for the first time and
369c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            // the previous item offsets have not been cached, or the user hasn't done anything
370c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            // (dragging, swiping etc) that requires an animation.
371c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            return;
372c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
3734ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee
374c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        final ViewTreeObserver observer = mListView.getViewTreeObserver();
375c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
376c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            @SuppressWarnings("unchecked")
377c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            @Override
378c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            public boolean onPreDraw() {
379c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                observer.removeOnPreDrawListener(this);
380c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                final int firstVisiblePosition = mListView.getFirstVisiblePosition();
3815b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                final AnimatorSet animSet = new AnimatorSet();
3825b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                final ArrayList<Animator> animators = new ArrayList<Animator>();
383c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                for (int i = 0; i < mListView.getChildCount(); i++) {
384c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    final View child = mListView.getChildAt(i);
385c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    int position = firstVisiblePosition + i;
386c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
387e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee                    // Since we are getting the position from mListView and then querying
388e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee                    // mContactTileAdapter, its very possible that things are out of sync
389e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee                    // and we might index out of bounds.  Let's make sure that this doesn't happen.
390e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee                    if (!mContactTileAdapter.isIndexInBound(position)) {
391e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee                        continue;
392e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee                    }
393e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee
394efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                    final long itemId = mContactTileAdapter.getItemId(position);
395c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
3964911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang                    if (containsId(idsInPlace, itemId)) {
3975b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        animators.add(ObjectAnimator.ofFloat(
3985b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                                child, "alpha", 0.0f, 1.0f));
3995b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        break;
400f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                    } else {
401f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        Integer startTop = mItemIdTopMap.get(itemId);
402efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                        Integer startLeft = mItemIdLeftMap.get(itemId);
403f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        final int top = child.getTop();
404efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                        final int left = child.getLeft();
405efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                        int deltaX = 0;
406efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                        int deltaY = 0;
407efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee
408efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                        if (startLeft != null) {
409efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                            if (startLeft != left) {
410efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                                deltaX = startLeft - left;
411efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                                animators.add(ObjectAnimator.ofFloat(
412efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                                        child, "translationX", deltaX, 0.0f));
413efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                            }
414efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                        }
415efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee
416f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        if (startTop != null) {
417f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                            if (startTop != top) {
418efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                                deltaY = startTop - top;
419efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                                animators.add(ObjectAnimator.ofFloat(
420efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                                        child, "translationY", deltaY, 0.0f));
4214ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee                            }
4225b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        }
423efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee
4245b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                        if (DEBUG) {
4255b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                            Log.d(TAG, "Found itemId: " + itemId + " for listview child " + i +
4265b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                                    " Top: " + top +
427efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                                    " Delta: " + deltaY);
428f59250951cf47d81fda125e9bab324d66ed75214Mindy Pereira                        }
429c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    }
430c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                }
4315b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang
4325b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                if (animators.size() > 0) {
4335b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                    animSet.setDuration(mAnimationDuration).playTogether(animators);
4345b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                    animSet.start();
4355b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang                }
4365b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang
437c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                mItemIdTopMap.clear();
438c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                mItemIdLeftMap.clear();
439c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                return true;
440c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
441c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        });
442c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
443c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
444c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    private boolean containsId(long[] ids, long target) {
445c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        // Linear search on array is fine because this is typically only 0-1 elements long
446c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        for (int i = 0; i < ids.length; i++) {
447c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            if (ids[i] == target) {
448c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                return true;
449c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
450c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
451c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        return false;
452c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
453c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
454c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    @Override
455c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    public void onDataSetChangedForAnimation(long... idsInPlace) {
456efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        animateGridView(idsInPlace);
457c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
458c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
459c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    @Override
460c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    public void cacheOffsetsForDatasetChange() {
4614ed7db87e6c0e71b2f656e77de938f3579bc36bfYorke Lee        saveOffsets(0);
462c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
463764f652b451d27282cfaf73407d31c9522e6cb0eBrian Attwell
464764f652b451d27282cfaf73407d31c9522e6cb0eBrian Attwell    public AbsListView getListView() {
465764f652b451d27282cfaf73407d31c9522e6cb0eBrian Attwell        return mListView;
466764f652b451d27282cfaf73407d31c9522e6cb0eBrian Attwell    }
46738019af70eb1ca084d36291390bbc54dc81027deYorke Lee
46838019af70eb1ca084d36291390bbc54dc81027deYorke Lee    @Override
469575ae388961252a771488c357e425fca191594b2Yorke Lee    public void onEmptyViewActionButtonClicked() {
47038019af70eb1ca084d36291390bbc54dc81027deYorke Lee        final Activity activity = getActivity();
47138019af70eb1ca084d36291390bbc54dc81027deYorke Lee        if (activity == null) {
47238019af70eb1ca084d36291390bbc54dc81027deYorke Lee            return;
47338019af70eb1ca084d36291390bbc54dc81027deYorke Lee        }
47438019af70eb1ca084d36291390bbc54dc81027deYorke Lee
47538019af70eb1ca084d36291390bbc54dc81027deYorke Lee        if (!PermissionsUtil.hasPermission(activity, READ_CONTACTS)) {
4767e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee            requestPermissions(new String[] {READ_CONTACTS}, READ_CONTACTS_PERMISSION_REQUEST_CODE);
47738019af70eb1ca084d36291390bbc54dc81027deYorke Lee        } else {
47838019af70eb1ca084d36291390bbc54dc81027deYorke Lee            // Switch tabs
47938019af70eb1ca084d36291390bbc54dc81027deYorke Lee            ((HostInterface) activity).showAllContactsTab();
48038019af70eb1ca084d36291390bbc54dc81027deYorke Lee        }
48138019af70eb1ca084d36291390bbc54dc81027deYorke Lee    }
4827e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee
4837e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee    @Override
4847e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee    public void onRequestPermissionsResult(int requestCode, String[] permissions,
4857e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee            int[] grantResults) {
4867e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee        if (requestCode == READ_CONTACTS_PERMISSION_REQUEST_CODE) {
4877e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee            if (grantResults.length == 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
4887e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee                PermissionsUtil.notifyPermissionGranted(getActivity(), READ_CONTACTS);
4897e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee            }
4907e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee        }
4917e1a68556c419c4b9b1eda9247e80262615fa809Yorke Lee    }
4926b049128c51b90e17ae14856d98130a22d3a5433Yorke Lee}
493