PhoneFavoritesTileAdapter.java revision d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672
1dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee/*
2dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * Copyright (C) 2013 The Android Open Source Project
3dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee *
4dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * Licensed under the Apache License, Version 2.0 (the "License");
5dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * you may not use this file except in compliance with the License.
6dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * You may obtain a copy of the License at
7dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee *
8dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee *      http://www.apache.org/licenses/LICENSE-2.0
9dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee *
10dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * Unless required by applicable law or agreed to in writing, software
11dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * distributed under the License is distributed on an "AS IS" BASIS,
12dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * See the License for the specific language governing permissions and
14dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * limitations under the License.
15dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee */
16dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leepackage com.android.dialer.list;
17dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
183ce60186f646b461635b16d7d9a806ddc0b61bacYorke Leeimport com.google.common.annotations.VisibleForTesting;
193ce60186f646b461635b16d7d9a806ddc0b61bacYorke Leeimport com.google.common.collect.ComparisonChain;
203ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee
21dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.content.ContentUris;
2255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport android.content.ContentValues;
23dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.content.Context;
24dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.content.res.Resources;
25dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.database.Cursor;
26dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.net.Uri;
27dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Phone;
28dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.provider.ContactsContract.Contacts;
2955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport android.provider.ContactsContract.PinnedPositions;
304e741192ec40278d9189b46cf2ae6bc547d9071dYorke Leeimport android.text.TextUtils;
31dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.util.Log;
324e741192ec40278d9189b46cf2ae6bc547d9071dYorke Leeimport android.util.LongSparseArray;
3311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.view.MotionEvent;
34dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.view.View;
3511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.view.ViewConfiguration;
36dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.view.ViewGroup;
37dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.widget.BaseAdapter;
38dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.widget.FrameLayout;
39dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
40dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.contacts.common.ContactPhotoManager;
41dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.contacts.common.ContactTileLoaderFactory;
42dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.contacts.common.R;
43dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.contacts.common.list.ContactEntry;
4455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport com.android.contacts.common.list.ContactTileAdapter.DisplayType;
45dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.contacts.common.list.ContactTileView;
4611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport com.android.dialer.list.SwipeHelper.OnItemGestureListener;
4711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport com.android.dialer.list.SwipeHelper.SwipeHelperCallback;
4855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
49dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport java.util.ArrayList;
5055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport java.util.Comparator;
5155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport java.util.LinkedList;
5255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport java.util.List;
5355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport java.util.PriorityQueue;
54dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
55dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee/**
56dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * Also allows for a configurable number of columns as well as a maximum row of tiled contacts.
57dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee *
58dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * This adapter has been rewritten to only support a maximum of one row for favorites.
59dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee *
60dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee */
6111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leepublic class PhoneFavoritesTileAdapter extends BaseAdapter implements
62d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee        SwipeHelper.OnItemGestureListener, OnDragDropListener {
63316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private static final String TAG = PhoneFavoritesTileAdapter.class.getSimpleName();
64316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private static final boolean DEBUG = false;
65dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
663ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee    public static final int NO_ROW_LIMIT = -1;
673ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee
683ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee    public static final int ROW_LIMIT_DEFAULT = NO_ROW_LIMIT;
69dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
70dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private ContactTileView.Listener mListener;
71c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    private OnDataSetChangedForAnimationListener mDataSetChangedListener;
72c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
73dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private Context mContext;
74dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private Resources mResources;
75316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
76316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** Contact data stored in cache. This is used to populate the associated view. */
77316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    protected ArrayList<ContactEntry> mContactEntries = null;
78316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** Back up of the temporarily removed Contact during dragging. */
79316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private ContactEntry mDraggedEntry = null;
80316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** Position of the temporarily removed contact in the cache. */
81316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private int mDraggedEntryIndex = -1;
82316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** New position of the temporarily removed contact in the cache. */
83316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private int mDropEntryIndex = -1;
8472418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    /** New position of the temporarily entered contact in the cache. */
8572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    private int mDragEnteredEntryIndex = -1;
86316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** Position of the contact pending removal. */
87316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private int mPotentialRemoveEntryIndex = -1;
88c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    private long mIdToKeepInPlace = -1;
89316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
905a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee    private boolean mAwaitingRemove = false;
9161fe032af12ad13bba18eb7a807112a59e1e4776Yorke Lee    private boolean mDelayCursorUpdates = false;
925a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee
93dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private ContactPhotoManager mPhotoManager;
94dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mNumFrequents;
956a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee    protected int mNumStarred;
96dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
97dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mColumnCount;
986a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee    private int mMaxTiledRows = ROW_LIMIT_DEFAULT;
99dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private int mStarredIndex;
100dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
101dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mIdIndex;
102dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mLookupIndex;
103dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mPhotoUriIndex;
104dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mNameIndex;
105dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mPresenceIndex;
106dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mStatusIndex;
107dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
108dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private int mPhoneNumberIndex;
109dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private int mPhoneNumberTypeIndex;
110dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private int mPhoneNumberLabelIndex;
11143f543900efac8edc10159851184da8cfca3d81aYorke Lee    private int mIsDefaultNumberIndex;
11255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    protected int mPinnedIndex;
113791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee    protected int mContactIdIndex;
114dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
115dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private final int mPaddingInPixels;
116dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
117316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** Indicates whether a drag is in process. */
118316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private boolean mInDragging = false;
119316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
12072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    public static final int PIN_LIMIT = 20;
12155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
122d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang    /**
123d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     * The soft limit on how many contact tiles to show.
124d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     * NOTE This soft limit would not restrict the number of starred contacts to show, rather
125d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     * 1. If the count of starred contacts is less than this limit, show 20 tiles total.
126d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     * 2. If the count of starred contacts is more than or equal to this limit,
127d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     * show all starred tiles and no frequents.
128d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     */
129d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang    private static final int TILES_SOFT_LIMIT = 20;
130d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang
13155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    final Comparator<ContactEntry> mContactEntryComparator = new Comparator<ContactEntry>() {
13255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        @Override
13355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        public int compare(ContactEntry lhs, ContactEntry rhs) {
13455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            return ComparisonChain.start()
13555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                    .compare(lhs.pinned, rhs.pinned)
13655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                    .compare(lhs.name, rhs.name)
13755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                    .result();
13855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        }
13955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    };
14055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
141c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    public interface OnDataSetChangedForAnimationListener {
142c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        public void onDataSetChangedForAnimation(long... idsInPlace);
143c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        public void cacheOffsetsForDatasetChange();
144c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    };
145dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
146316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public PhoneFavoritesTileAdapter(Context context, ContactTileView.Listener listener,
147c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            OnDataSetChangedForAnimationListener dataSetChangedListener,
148316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            int numCols, int maxTiledRows) {
149c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        mDataSetChangedListener = dataSetChangedListener;
150dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mListener = listener;
151dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mContext = context;
152dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mResources = context.getResources();
153dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mColumnCount = numCols;
154dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mNumFrequents = 0;
155dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mMaxTiledRows = maxTiledRows;
156316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mContactEntries = new ArrayList<ContactEntry>();
157dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        // Converting padding in dips to padding in pixels
158dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPaddingInPixels = mContext.getResources()
1593ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee                .getDimensionPixelSize(R.dimen.contact_tile_divider_width);
160316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
161dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        bindColumnIndices();
162dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
163dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
164dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void setPhotoLoader(ContactPhotoManager photoLoader) {
165dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPhotoManager = photoLoader;
166dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
167dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
168dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void setMaxRowCount(int maxRows) {
169dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mMaxTiledRows = maxRows;
170dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
171dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
172dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void setColumnCount(int columnCount) {
173dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mColumnCount = columnCount;
174dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
175dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
176dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
177316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Indicates whether a drag is in process.
178316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     *
179316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * @param inDragging Boolean variable indicating whether there is a drag in process.
180316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
181316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public void setInDragging(boolean inDragging) {
18261fe032af12ad13bba18eb7a807112a59e1e4776Yorke Lee        mDelayCursorUpdates = inDragging;
183316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mInDragging = inDragging;
184316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
185316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
186316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** Gets whether the drag is in process. */
187316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public boolean getInDragging() {
188316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        return mInDragging;
189316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
190316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
191316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
192dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Sets the column indices for expected {@link Cursor}
193dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * based on {@link DisplayType}.
194dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
195dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected void bindColumnIndices() {
196dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mIdIndex = ContactTileLoaderFactory.CONTACT_ID;
197dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mLookupIndex = ContactTileLoaderFactory.LOOKUP_KEY;
198dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPhotoUriIndex = ContactTileLoaderFactory.PHOTO_URI;
199dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mNameIndex = ContactTileLoaderFactory.DISPLAY_NAME;
200dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mStarredIndex = ContactTileLoaderFactory.STARRED;
201dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPresenceIndex = ContactTileLoaderFactory.CONTACT_PRESENCE;
202dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mStatusIndex = ContactTileLoaderFactory.CONTACT_STATUS;
203dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
204dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPhoneNumberIndex = ContactTileLoaderFactory.PHONE_NUMBER;
205dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPhoneNumberTypeIndex = ContactTileLoaderFactory.PHONE_NUMBER_TYPE;
206dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPhoneNumberLabelIndex = ContactTileLoaderFactory.PHONE_NUMBER_LABEL;
20743f543900efac8edc10159851184da8cfca3d81aYorke Lee        mIsDefaultNumberIndex = ContactTileLoaderFactory.IS_DEFAULT_NUMBER;
20855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        mPinnedIndex = ContactTileLoaderFactory.PINNED;
209791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee        mContactIdIndex = ContactTileLoaderFactory.CONTACT_ID_FOR_DATA;
210dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
211dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
212dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
213dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Gets the number of frequents from the passed in cursor.
214dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     *
215dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * This methods is needed so the GroupMemberTileAdapter can override this.
216dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     *
217dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * @param cursor The cursor to get number of frequents from.
218dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
219dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected void saveNumFrequentsFromCursor(Cursor cursor) {
2206a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee        mNumFrequents = cursor.getCount() - mNumStarred;
221dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
222dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
223dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
224dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Creates {@link ContactTileView}s for each item in {@link Cursor}.
225dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     *
226dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Else use {@link ContactTileLoaderFactory}
227dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
228dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void setContactCursor(Cursor cursor) {
22961fe032af12ad13bba18eb7a807112a59e1e4776Yorke Lee        if (!mDelayCursorUpdates && cursor != null && !cursor.isClosed()) {
230316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            mNumStarred = getNumStarredContacts(cursor);
231c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            if (mAwaitingRemove) {
232c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                mDataSetChangedListener.cacheOffsetsForDatasetChange();
233c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
234c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
235316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            saveNumFrequentsFromCursor(cursor);
236316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            saveCursorToCache(cursor);
237316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            // cause a refresh of any views that rely on this data
238316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            notifyDataSetChanged();
239c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            // about to start redraw
240c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            if (mIdToKeepInPlace != -1) {
241c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                mDataSetChangedListener.onDataSetChangedForAnimation(mIdToKeepInPlace);
242c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            } else {
243c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                mDataSetChangedListener.onDataSetChangedForAnimation();
244c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
245c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            mIdToKeepInPlace = -1;
246316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
247316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
248dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
249316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
250316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Saves the cursor data to the cache, to speed up UI changes.
251316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     *
252316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * @param cursor Returned cursor with data to populate the view.
253316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
254316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private void saveCursorToCache(Cursor cursor) {
255316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mContactEntries.clear();
25655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
257443d758d53d1f7ebb12e8ed39fc6195c9d5ed18dYorke Lee        cursor.moveToPosition(-1);
25855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
2594e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee        final LongSparseArray<Object> duplicates = new LongSparseArray<Object>(cursor.getCount());
260443d758d53d1f7ebb12e8ed39fc6195c9d5ed18dYorke Lee
261d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang        // Track the length of {@link #mContactEntries} and compare to {@link #TILES_SOFT_LIMIT}.
262d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang        int counter = 0;
263d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang
2644e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee        while (cursor.moveToNext()) {
2654e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
2664e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final int starred = cursor.getInt(mStarredIndex);
2674e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final long id;
26855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
269791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            // We display a maximum of TILES_SOFT_LIMIT contacts, or the total number of starred
270791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            // whichever is greater.
271791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            if (starred < 1 && counter >= TILES_SOFT_LIMIT) {
272d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang                break;
27355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            } else {
274791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee                id = cursor.getLong(mContactIdIndex);
27555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            }
2764e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
27743f543900efac8edc10159851184da8cfca3d81aYorke Lee            final ContactEntry existing = (ContactEntry) duplicates.get(id);
27843f543900efac8edc10159851184da8cfca3d81aYorke Lee            if (existing != null) {
27943f543900efac8edc10159851184da8cfca3d81aYorke Lee                // Check if the existing number is a default number. If not, clear the phone number
28043f543900efac8edc10159851184da8cfca3d81aYorke Lee                // and label fields so that the disambiguation dialog will show up.
28143f543900efac8edc10159851184da8cfca3d81aYorke Lee                if (!existing.isDefaultNumber) {
28243f543900efac8edc10159851184da8cfca3d81aYorke Lee                    existing.phoneLabel = null;
28343f543900efac8edc10159851184da8cfca3d81aYorke Lee                    existing.phoneNumber = null;
28443f543900efac8edc10159851184da8cfca3d81aYorke Lee                }
2854e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee                continue;
2864e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            }
2874e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
2884e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final String photoUri = cursor.getString(mPhotoUriIndex);
2894e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final String lookupKey = cursor.getString(mLookupIndex);
2904e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final int pinned = cursor.getInt(mPinnedIndex);
2914e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final String name = cursor.getString(mNameIndex);
2929bead9c01ab09ebfbd1c2be8555d8c8d4e3cf6bdChristine Chen            final boolean isStarred = cursor.getInt(mStarredIndex) > 0;
29343f543900efac8edc10159851184da8cfca3d81aYorke Lee            final boolean isDefaultNumber = cursor.getInt(mIsDefaultNumberIndex) > 0;
2944e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
2954e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final ContactEntry contact = new ContactEntry();
2964e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
2974e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            contact.id = id;
2984e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            contact.name = (!TextUtils.isEmpty(name)) ? name :
2994e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee                    mResources.getString(R.string.missing_name);
300443d758d53d1f7ebb12e8ed39fc6195c9d5ed18dYorke Lee            contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
301443d758d53d1f7ebb12e8ed39fc6195c9d5ed18dYorke Lee            contact.lookupKey = ContentUris.withAppendedId(
302443d758d53d1f7ebb12e8ed39fc6195c9d5ed18dYorke Lee                    Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
3039bead9c01ab09ebfbd1c2be8555d8c8d4e3cf6bdChristine Chen            contact.isFavorite = isStarred;
30443f543900efac8edc10159851184da8cfca3d81aYorke Lee            contact.isDefaultNumber = isDefaultNumber;
3054e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
30643f543900efac8edc10159851184da8cfca3d81aYorke Lee            // Set phone number and label
307791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            final int phoneNumberType = cursor.getInt(mPhoneNumberTypeIndex);
308791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            final String phoneNumberCustomLabel = cursor.getString(mPhoneNumberLabelIndex);
309791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            contact.phoneLabel = (String) Phone.getTypeLabel(mResources, phoneNumberType,
310791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee                    phoneNumberCustomLabel);
311791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            contact.phoneNumber = cursor.getString(mPhoneNumberIndex);
31255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
31355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            contact.pinned = pinned;
314443d758d53d1f7ebb12e8ed39fc6195c9d5ed18dYorke Lee            mContactEntries.add(contact);
315d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang
31643f543900efac8edc10159851184da8cfca3d81aYorke Lee            duplicates.put(id, contact);
31743f543900efac8edc10159851184da8cfca3d81aYorke Lee
318d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang            counter++;
319316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
32055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
3215a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee        mAwaitingRemove = false;
3225a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee
32355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        arrangeContactsByPinnedPosition(mContactEntries);
32455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
32555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        notifyDataSetChanged();
326dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
327dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
328dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
329dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Iterates over the {@link Cursor}
330dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Returns position of the first NON Starred Contact
331dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Returns -1 if {@link DisplayType#STARRED_ONLY}
332dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Returns 0 if {@link DisplayType#FREQUENT_ONLY}
333dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
3346a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee    protected int getNumStarredContacts(Cursor cursor) {
335dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        cursor.moveToPosition(-1);
336dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        while (cursor.moveToNext()) {
337dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            if (cursor.getInt(mStarredIndex) == 0) {
338dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                return cursor.getPosition();
339dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            }
340dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
341dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
342dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        // There are not NON Starred contacts in cursor
343dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        // Set divider positon to end
344dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        return cursor.getCount();
345dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
346dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
347316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
348316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Loads a contact from the cached list.
349316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     *
350316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * @param position Position of the Contact.
351316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * @return Contact at the requested position.
352316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
353316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    protected ContactEntry getContactEntryFromCache(int position) {
354316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        if (mContactEntries.size() <= position) return null;
355316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        return mContactEntries.get(position);
356dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
357dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
358dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
359dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Returns the number of frequents that will be displayed in the list.
360dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
361dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public int getNumFrequents() {
362dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        return mNumFrequents;
363dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
364dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
365dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
366dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public int getCount() {
367c1e769c16792a880ac803b912851d6d258ed4550Yorke Lee        if (mContactEntries == null || mContactEntries.isEmpty()) {
3684fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen            return 0;
369dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
370dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
37163b92b5f2cc4d4099aed4c6d10cd24e1389e7a44Yorke Lee        int total = mContactEntries.size();
372c1e769c16792a880ac803b912851d6d258ed4550Yorke Lee        // The number of contacts that don't show up as tiles
373c1e769c16792a880ac803b912851d6d258ed4550Yorke Lee        final int nonTiledRows = Math.max(0, total - getMaxContactsInTiles());
374c1e769c16792a880ac803b912851d6d258ed4550Yorke Lee        // The number of tiled rows
375c1e769c16792a880ac803b912851d6d258ed4550Yorke Lee        final int tiledRows = getRowCount(total - nonTiledRows);
376c1e769c16792a880ac803b912851d6d258ed4550Yorke Lee        return nonTiledRows + tiledRows;
377dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
378dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
3795fe874fbdfe0010b5f2439cf6c13eaee46d406eaYorke Lee    public int getMaxTiledRows() {
3805fe874fbdfe0010b5f2439cf6c13eaee46d406eaYorke Lee        return mMaxTiledRows;
3815fe874fbdfe0010b5f2439cf6c13eaee46d406eaYorke Lee    }
3825fe874fbdfe0010b5f2439cf6c13eaee46d406eaYorke Lee
383dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
384dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Returns the number of rows required to show the provided number of entries
385dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * with the current number of columns.
386dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
387dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int getRowCount(int entryCount) {
388dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        if (entryCount == 0) return 0;
389dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        final int nonLimitedRows = ((entryCount - 1) / mColumnCount) + 1;
3903ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee        if (mMaxTiledRows == NO_ROW_LIMIT) {
3913ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee            return nonLimitedRows;
3923ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee        }
3936a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee        return Math.min(mMaxTiledRows, nonLimitedRows);
3946a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee    }
3956a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee
3966a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee    private int getMaxContactsInTiles() {
3973ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee        if (mMaxTiledRows == NO_ROW_LIMIT) {
3983ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee            return Integer.MAX_VALUE;
3993ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee        }
4006a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee        return mColumnCount * mMaxTiledRows;
401dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
402dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
4035b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang    public int getRowIndex(int entryIndex) {
4043ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee        if (entryIndex < getMaxContactsInTiles()) {
405316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            return entryIndex / mColumnCount;
406316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        } else {
4073ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee            return entryIndex - mMaxTiledRows * (mColumnCount + 1);
408316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
409316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
410316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
411dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public int getColumnCount() {
412dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        return mColumnCount;
413dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
414dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
415dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
416dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Returns an ArrayList of the {@link ContactEntry}s that are to appear
417dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * on the row for the given position.
418dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
419dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
420dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public ArrayList<ContactEntry> getItem(int position) {
421dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        ArrayList<ContactEntry> resultList = new ArrayList<ContactEntry>(mColumnCount);
42211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
42311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final int entryIndex = getFirstContactEntryIndexForPosition(position);
42411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
42511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final int viewType = getItemViewType(position);
42611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
42711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final int columnCount;
42811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (viewType == ViewTypes.TOP) {
42911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            columnCount = mColumnCount;
43011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        } else {
43111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            columnCount = 1;
43211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
43311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
43411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        for (int i = 0; i < columnCount; i++) {
43511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            final ContactEntry entry = getContactEntryFromCache(entryIndex + i);
43611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            if (entry == null) break; // less than mColumnCount contacts
43711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            resultList.add(entry);
43811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
43911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
44011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return resultList;
44111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
44211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
44311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    /*
44411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee     * Given a position in the adapter, returns the index of the first contact entry that is to be
44511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee     * in that row.
44611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee     */
44711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private int getFirstContactEntryIndexForPosition(int position) {
4486a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee        final int maxContactsInTiles = getMaxContactsInTiles();
4496a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee        if (position < getRowCount(maxContactsInTiles)) {
4506a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee            // Contacts that appear as tiles
45111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            return position * mColumnCount;
452dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        } else {
4536a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee            // Contacts that appear as rows
4546a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee            // The actual position of the contact in the cursor is simply total the number of
4556a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee            // tiled contacts + the given position
4562a066fed83f99c26a0e64a7c25fd72385bbc6568Yorke Lee            return maxContactsInTiles + position - mMaxTiledRows;
457dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
458dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
459dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
460c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    /**
461c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * For the top row of tiled contacts, the item id is the position of the row of
462c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * contacts.
463c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * For frequent contacts, the item id is the maximum number of rows of tiled contacts +
464c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * the actual contact id. Since contact ids are always greater than 0, this guarantees that
465c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * all items within this adapter will always have unique ids.
466c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     */
467dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
468dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public long getItemId(int position) {
469c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        if (getItemViewType(position) == ViewTypes.FREQUENT) {
470c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            return getAdjustedItemId(getItem(position).get(0).id);
471c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        } else {
472c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            return position;
473c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        }
474c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
475c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
476c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    /**
4773ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee     * Calculates the stable itemId for a particular entry based on the entry's contact ID. This
4783ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee     * stable itemId is used for animation purposes.
479c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     */
480c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    public long getAdjustedItemId(long id) {
4813ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee        if (mMaxTiledRows == NO_ROW_LIMIT) {
4823ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee            return id;
4833ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee        }
484c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        return mMaxTiledRows + id;
485c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
486c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
487c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    @Override
488c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    public boolean hasStableIds() {
489c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        return true;
490dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
491dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
492dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
493dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public boolean areAllItemsEnabled() {
494dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        // No dividers, so all items are enabled.
495dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        return true;
496dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
497dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
498dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
499dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public boolean isEnabled(int position) {
5004fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        return getCount() > 0;
501dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
502dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
503dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
504316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public void notifyDataSetChanged() {
505316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        if (DEBUG) {
50655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            Log.v(TAG, "notifyDataSetChanged");
507316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
508316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        super.notifyDataSetChanged();
509316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
510316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
511316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    @Override
512dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public View getView(int position, View convertView, ViewGroup parent) {
51355d3bb4a7f70d4a4359c31b3240f5740b788e643Yorke Lee        if (DEBUG) {
51455d3bb4a7f70d4a4359c31b3240f5740b788e643Yorke Lee            Log.v(TAG, "get view for " + String.valueOf(position));
51555d3bb4a7f70d4a4359c31b3240f5740b788e643Yorke Lee        }
51618e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee
517dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        int itemViewType = getItemViewType(position);
518dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
5191f06f0ad1229f3b4e00553c294e664ffa5234c90Christine Chen        ContactTileRow contactTileRowView = null;
5201f06f0ad1229f3b4e00553c294e664ffa5234c90Christine Chen
5211f06f0ad1229f3b4e00553c294e664ffa5234c90Christine Chen        if (convertView instanceof  ContactTileRow) {
5221f06f0ad1229f3b4e00553c294e664ffa5234c90Christine Chen            contactTileRowView  = (ContactTileRow) convertView;
5231f06f0ad1229f3b4e00553c294e664ffa5234c90Christine Chen        }
5246091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen
525dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        ArrayList<ContactEntry> contactList = getItem(position);
526dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
527dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        if (contactTileRowView == null) {
528dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            // Creating new row if needed
529316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            contactTileRowView = new ContactTileRow(mContext, itemViewType, position);
530dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
531dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
532316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        contactTileRowView.configureRow(contactList, position, position == getCount() - 1);
533316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
534dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        return contactTileRowView;
535dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
536dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
537dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private int getLayoutResourceId(int viewType) {
538dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        switch (viewType) {
539dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            case ViewTypes.FREQUENT:
540dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                return R.layout.phone_favorite_regular_row_view;
541d99993213962df64c9d76005df86c2fb83fca074Yorke Lee            case ViewTypes.TOP:
542dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                return R.layout.phone_favorite_tile_view;
543dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            default:
544dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                throw new IllegalArgumentException("Unrecognized viewType " + viewType);
545dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
546dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
547dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
548dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public int getViewTypeCount() {
549dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        return ViewTypes.COUNT;
550dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
551dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
552dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
553dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public int getItemViewType(int position) {
5543ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee        if (position < getMaxContactsInTiles()) {
555d99993213962df64c9d76005df86c2fb83fca074Yorke Lee            return ViewTypes.TOP;
556dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        } else {
557dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            return ViewTypes.FREQUENT;
558dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
559dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
560dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
561dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
562316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Temporarily removes a contact from the list for UI refresh. Stores data for this contact
563316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * in the back-up variable.
564316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     *
565316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * @param index Position of the contact to be removed.
566316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
567316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public void popContactEntry(int index) {
56872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        if (isIndexInBound(index)) {
569316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            mDraggedEntry = mContactEntries.get(index);
570316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            mDraggedEntryIndex = index;
57172418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mDragEnteredEntryIndex = index;
57272418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            markDropArea(mDragEnteredEntryIndex);
57372418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        }
57472418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    }
57572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang
57672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    /**
57772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     * @param itemIndex Position of the contact in {@link #mContactEntries}.
57872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     * @return True if the given index is valid for {@link #mContactEntries}.
57972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     */
58072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    private boolean isIndexInBound(int itemIndex) {
58172418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        return itemIndex >= 0 && itemIndex < mContactEntries.size();
58272418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    }
58372418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang
58472418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    /**
58572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     * Mark the tile as drop area by given the item index in {@link #mContactEntries}.
58672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     *
58772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     * @param itemIndex Position of the contact in {@link #mContactEntries}.
58872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     */
58972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    private void markDropArea(int itemIndex) {
59072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        if (isIndexInBound(mDragEnteredEntryIndex) && isIndexInBound(itemIndex)) {
5915b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang            mDataSetChangedListener.cacheOffsetsForDatasetChange();
59272418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            // Remove the old placeholder item and place the new placeholder item.
5935b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang            final int oldIndex = mDragEnteredEntryIndex;
59472418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mContactEntries.remove(mDragEnteredEntryIndex);
59572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mDragEnteredEntryIndex = itemIndex;
59672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mContactEntries.add(mDragEnteredEntryIndex, ContactEntry.BLANK_ENTRY);
59772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            ContactEntry.BLANK_ENTRY.id = mDraggedEntry.id;
5985b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang            mDataSetChangedListener.onDataSetChangedForAnimation();
599316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            notifyDataSetChanged();
600316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
601316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
602316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
603316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
604316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Drops the temporarily removed contact to the desired location in the list.
605316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
60672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    public void handleDrop() {
60755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        boolean changed = false;
608316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        if (mDraggedEntry != null) {
6094911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang            if (isIndexInBound(mDragEnteredEntryIndex) &&
6104911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang                    mDragEnteredEntryIndex != mDraggedEntryIndex) {
61155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // Don't add the ContactEntry here (to prevent a double animation from occuring).
61255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // When we receive a new cursor the list of contact entries will automatically be
61355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // populated with the dragged ContactEntry at the correct spot.
61472418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                mDropEntryIndex = mDragEnteredEntryIndex;
61572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                mContactEntries.set(mDropEntryIndex, mDraggedEntry);
616c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                mIdToKeepInPlace = getAdjustedItemId(mDraggedEntry.id);
617c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                mDataSetChangedListener.cacheOffsetsForDatasetChange();
61855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                changed = true;
61972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            } else if (isIndexInBound(mDraggedEntryIndex)) {
62072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                // If {@link #mDragEnteredEntryIndex} is invalid,
62172418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                // falls back to the original position of the contact.
62272418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                mContactEntries.remove(mDragEnteredEntryIndex);
62372418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                mContactEntries.add(mDraggedEntryIndex, mDraggedEntry);
624316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                mDropEntryIndex = mDraggedEntryIndex;
62555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                notifyDataSetChanged();
62655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            }
62755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
62855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            if (changed && mDropEntryIndex < PIN_LIMIT) {
62955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                final ContentValues cv = getReflowedPinnedPositions(mContactEntries, mDraggedEntry,
63055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                        mDraggedEntryIndex, mDropEntryIndex);
6319bead9c01ab09ebfbd1c2be8555d8c8d4e3cf6bdChristine Chen                final Uri pinUri = PinnedPositions.UPDATE_URI.buildUpon().build();
63255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // update the database here with the new pinned positions
63355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                mContext.getContentResolver().update(pinUri, cv, null, null);
634316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            }
635316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            mDraggedEntry = null;
636316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
637316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
638316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
639316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
64001f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen     * Invoked when the dragged item is dropped to unsupported location. We will then move the
64101f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen     * contact back to where it was dragged from.
64201f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen     */
64301f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen    public void dropToUnsupportedView() {
64472418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        if (isIndexInBound(mDragEnteredEntryIndex)) {
64572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mContactEntries.remove(mDragEnteredEntryIndex);
64672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mContactEntries.add(mDraggedEntryIndex, mDraggedEntry);
64772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            notifyDataSetChanged();
64872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        }
64901f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen    }
65001f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen
65101f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen    /**
652316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Sets an item to for pending removal. If the user does not click the undo button, the item
653316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * will be removed at the next interaction.
654316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     *
655316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * @param index Index of the item to be removed.
656316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
657316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public void setPotentialRemoveEntryIndex(int index) {
658316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mPotentialRemoveEntryIndex = index;
659316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
660316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
661316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
66255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * Removes a contact entry from the list.
663316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     *
664316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * @return True is an item is removed. False is there is no item to be removed.
665316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
66611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public boolean removePendingContactEntry() {
66711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        boolean removed = false;
66872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        if (isIndexInBound(mPotentialRemoveEntryIndex)) {
669c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            final ContactEntry entry = mContactEntries.get(mPotentialRemoveEntryIndex);
67055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            unstarAndUnpinContact(entry.lookupKey);
67111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            removed = true;
6725a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee            mAwaitingRemove = true;
673316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
67411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        cleanTempVariables();
67511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return removed;
676316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
677316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
678316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
679316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Resets the item for pending removal.
680316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
681316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public void undoPotentialRemoveEntryIndex() {
682316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mPotentialRemoveEntryIndex = -1;
683316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
684316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
68552112414bcc95394e537185316168dcdc943544fYorke Lee    public boolean hasPotentialRemoveEntryIndex() {
68652112414bcc95394e537185316168dcdc943544fYorke Lee        return isIndexInBound(mPotentialRemoveEntryIndex);
68752112414bcc95394e537185316168dcdc943544fYorke Lee    }
68852112414bcc95394e537185316168dcdc943544fYorke Lee
689316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
690316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Clears all temporary variables at a new interaction.
691316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
692316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public void cleanTempVariables() {
693316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mDraggedEntryIndex = -1;
694316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mDropEntryIndex = -1;
69572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        mDragEnteredEntryIndex = -1;
696316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mDraggedEntry = null;
697316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mPotentialRemoveEntryIndex = -1;
698316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
699316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
700316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
701dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Acts as a row item composed of {@link ContactTileView}
702dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     *
703dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
70411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public class ContactTileRow extends FrameLayout implements SwipeHelperCallback {
70511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public static final int CONTACT_ENTRY_INDEX_TAG = R.id.contact_entry_index_tag;
70611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
707dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        private int mItemViewType;
708dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        private int mLayoutResId;
709d99993213962df64c9d76005df86c2fb83fca074Yorke Lee        private final int mRowPaddingStart;
710d99993213962df64c9d76005df86c2fb83fca074Yorke Lee        private final int mRowPaddingEnd;
711d99993213962df64c9d76005df86c2fb83fca074Yorke Lee        private final int mRowPaddingTop;
712d99993213962df64c9d76005df86c2fb83fca074Yorke Lee        private final int mRowPaddingBottom;
7133ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee        private final float mHeightToWidthRatio;
714316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        private int mPosition;
71511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        private SwipeHelper mSwipeHelper;
71611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        private OnItemGestureListener mOnItemSwipeListener;
717dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
718316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        public ContactTileRow(Context context, int itemViewType, int position) {
719dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            super(context);
720dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            mItemViewType = itemViewType;
721dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            mLayoutResId = getLayoutResourceId(mItemViewType);
722316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            mPosition = position;
723dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
724d99993213962df64c9d76005df86c2fb83fca074Yorke Lee            final Resources resources = mContext.getResources();
725ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee
7263ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee            mHeightToWidthRatio = getResources().getFraction(
7273ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee                    R.dimen.contact_tile_height_to_width_ratio, 1, 1);
7283ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee
729ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee            if (mItemViewType == ViewTypes.TOP) {
730ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                // For tiled views, we still want padding to be set on the ContactTileRow.
731ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                // Otherwise the padding would be set around each of the tiles, which we don't want
732ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                mRowPaddingTop = resources.getDimensionPixelSize(
733ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                        R.dimen.favorites_row_top_padding);
734ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                mRowPaddingBottom = resources.getDimensionPixelSize(
735ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                        R.dimen.favorites_row_bottom_padding);
736ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                mRowPaddingStart = resources.getDimensionPixelSize(
737ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                        R.dimen.favorites_row_start_padding);
738ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                mRowPaddingEnd = resources.getDimensionPixelSize(
739ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                        R.dimen.favorites_row_end_padding);
740ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee            } else {
741ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                // For row views, padding is set on the view itself.
742ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                mRowPaddingTop = 0;
743ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                mRowPaddingBottom = 0;
744ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                mRowPaddingStart = 0;
745ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee                mRowPaddingEnd = 0;
746ba6b3366f21c47454b3ba5189e94bb6bf58fd11dYorke Lee            }
747d99993213962df64c9d76005df86c2fb83fca074Yorke Lee
748d99993213962df64c9d76005df86c2fb83fca074Yorke Lee            setPaddingRelative(mRowPaddingStart, mRowPaddingTop, mRowPaddingEnd,
749d99993213962df64c9d76005df86c2fb83fca074Yorke Lee                    mRowPaddingBottom);
750d99993213962df64c9d76005df86c2fb83fca074Yorke Lee
751dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            // Remove row (but not children) from accessibility node tree.
752dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
75311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
75411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            if (mItemViewType == ViewTypes.FREQUENT) {
75511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                // ListView handles swiping for this item
75611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                SwipeHelper.setSwipeable(this, true);
75711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            } else if (mItemViewType == ViewTypes.TOP) {
75811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                // The contact tile row has its own swipe helpers, that makes each individual
75911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                // tile swipeable.
76011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                final float densityScale = getResources().getDisplayMetrics().density;
76111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                final float pagingTouchSlop = ViewConfiguration.get(context)
76211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        .getScaledPagingTouchSlop();
76311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale,
76411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        pagingTouchSlop);
765362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang                // Increase swipe thresholds for square tiles since they are relatively small.
766362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang                mSwipeHelper.setChildSwipedFarEnoughFactor(0.9f);
767362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang                mSwipeHelper.setChildSwipedFastEnoughFactor(0.1f);
76811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mOnItemSwipeListener = PhoneFavoritesTileAdapter.this;
76911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
770dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
771dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
772dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        /**
773dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee         * Configures the row to add {@link ContactEntry}s information to the views
774dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee         */
775316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        public void configureRow(ArrayList<ContactEntry> list, int position, boolean isLastRow) {
776dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            int columnCount = mItemViewType == ViewTypes.FREQUENT ? 1 : mColumnCount;
777316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            mPosition = position;
778dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
779dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            // Adding tiles to row and filling in contact information
780dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            for (int columnCounter = 0; columnCounter < columnCount; columnCounter++) {
781dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                ContactEntry entry =
782dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                        columnCounter < list.size() ? list.get(columnCounter) : null;
783dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                addTileFromEntry(entry, columnCounter, isLastRow);
784dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            }
785c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            if (columnCount == 1) {
786c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                if (list.get(0) == ContactEntry.BLANK_ENTRY) {
787c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    setVisibility(View.INVISIBLE);
788c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                } else {
789c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                    setVisibility(View.VISIBLE);
790c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                }
791c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
792dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
793dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
794dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        private void addTileFromEntry(ContactEntry entry, int childIndex, boolean isLastRow) {
795316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            final PhoneFavoriteTileView contactTile;
796dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
797dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            if (getChildCount() <= childIndex) {
798dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
799316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                contactTile = (PhoneFavoriteTileView) inflate(mContext, mLayoutResId, null);
800dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                // Note: the layoutparam set here is only actually used for FREQUENT.
801dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                // We override onMeasure() for STARRED and we don't care the layout param there.
802d99993213962df64c9d76005df86c2fb83fca074Yorke Lee                final Resources resources = mContext.getResources();
803dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
804dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                        ViewGroup.LayoutParams.WRAP_CONTENT,
805dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                        ViewGroup.LayoutParams.WRAP_CONTENT);
806dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
807dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                params.setMargins(
808316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                        resources.getDimensionPixelSize(R.dimen.detail_item_side_margin), 0,
809316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                        resources.getDimensionPixelSize(R.dimen.detail_item_side_margin), 0);
810dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                contactTile.setLayoutParams(params);
811dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                contactTile.setPhotoManager(mPhotoManager);
812dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                contactTile.setListener(mListener);
813dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                addView(contactTile);
814dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            } else {
815316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                contactTile = (PhoneFavoriteTileView) getChildAt(childIndex);
816dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            }
817dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            contactTile.loadFromContact(entry);
81811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
81911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            int entryIndex = -1;
820dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            switch (mItemViewType) {
821d99993213962df64c9d76005df86c2fb83fca074Yorke Lee                case ViewTypes.TOP:
822dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    // Setting divider visibilities
823dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    contactTile.setPaddingRelative(0, 0,
824d99993213962df64c9d76005df86c2fb83fca074Yorke Lee                            childIndex >= mColumnCount - 1 ? 0 : mPaddingInPixels, 0);
82511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    entryIndex = getFirstContactEntryIndexForPosition(mPosition) + childIndex;
8266c2b2ec252f2a9788bb967c069740f5a1bdc495eYorke Lee                    SwipeHelper.setSwipeable(contactTile, false);
827dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    break;
828dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                case ViewTypes.FREQUENT:
829dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    contactTile.setHorizontalDividerVisibility(
830dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                            isLastRow ? View.GONE : View.VISIBLE);
83111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    entryIndex = getFirstContactEntryIndexForPosition(mPosition);
83211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    SwipeHelper.setSwipeable(this, true);
833dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    break;
834dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                default:
835dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    break;
836dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            }
83711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            // tag the tile with the index of the contact entry it is associated with
83811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            if (entryIndex != -1) {
83911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                contactTile.setTag(CONTACT_ENTRY_INDEX_TAG, entryIndex);
84011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
841316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            contactTile.setupFavoriteContactCard();
842dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
843dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
844dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        @Override
845dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
846dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            switch (mItemViewType) {
847d99993213962df64c9d76005df86c2fb83fca074Yorke Lee                case ViewTypes.TOP:
848dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    onLayoutForTiles();
849dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    return;
850dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                default:
851dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    super.onLayout(changed, left, top, right, bottom);
852dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    return;
853dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            }
854dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
855dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
856dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        private void onLayoutForTiles() {
857dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            final int count = getChildCount();
858dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
859dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            // Just line up children horizontally.
860d99993213962df64c9d76005df86c2fb83fca074Yorke Lee            int childLeft = getPaddingStart();
861dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            for (int i = 0; i < count; i++) {
862dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                final View child = getChildAt(i);
863dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
864dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                // Note MeasuredWidth includes the padding.
865dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                final int childWidth = child.getMeasuredWidth();
8662a066fed83f99c26a0e64a7c25fd72385bbc6568Yorke Lee                child.layout(childLeft, getPaddingTop(), childLeft + childWidth,
8672a066fed83f99c26a0e64a7c25fd72385bbc6568Yorke Lee                        getPaddingTop() + child.getMeasuredHeight());
868dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                childLeft += childWidth;
869dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            }
870dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
871dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
872dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        @Override
873dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
874dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            switch (mItemViewType) {
875d99993213962df64c9d76005df86c2fb83fca074Yorke Lee                case ViewTypes.TOP:
876dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    onMeasureForTiles(widthMeasureSpec);
877dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    return;
878dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                default:
879dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
880dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                    return;
881dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            }
882dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
883dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
884dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        private void onMeasureForTiles(int widthMeasureSpec) {
885dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            final int width = MeasureSpec.getSize(widthMeasureSpec);
886dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
887dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            final int childCount = getChildCount();
888dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            if (childCount == 0) {
889dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                // Just in case...
890dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                setMeasuredDimension(width, 0);
891dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                return;
892dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            }
893dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
894dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            // 1. Calculate image size.
895dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            //      = ([total width] - [total padding]) / [child count]
896dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            //
897dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            // 2. Set it to width/height of each children.
898dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            //    If we have a remainder, some tiles will have 1 pixel larger width than its height.
899dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            //
900dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            // 3. Set the dimensions of itself.
901dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            //    Let width = given width.
902dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            //    Let height = image size + bottom paddding.
903dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
904d99993213962df64c9d76005df86c2fb83fca074Yorke Lee            final int totalPaddingsInPixels = (mColumnCount - 1) * mPaddingInPixels
905d99993213962df64c9d76005df86c2fb83fca074Yorke Lee                    + mRowPaddingStart + mRowPaddingEnd;
906dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
907dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            // Preferred width / height for images (excluding the padding).
908dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            // The actual width may be 1 pixel larger than this if we have a remainder.
9093ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee            final int imageWidth = (width - totalPaddingsInPixels) / mColumnCount;
9103ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee            final int remainder = width - (imageWidth * mColumnCount) - totalPaddingsInPixels;
9113ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee
9123ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee            final int height = (int) (mHeightToWidthRatio * imageWidth);
913dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
914dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            for (int i = 0; i < childCount; i++) {
915dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                final View child = getChildAt(i);
9163ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee                final int childWidth = imageWidth + child.getPaddingRight()
917dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                        // Compensate for the remainder
918dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                        + (i < remainder ? 1 : 0);
919dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                child.measure(
920dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                        MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
9213ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
922dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                        );
923dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            }
9243ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee            setMeasuredDimension(width, height + getPaddingTop() + getPaddingBottom());
925dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
926316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
927316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        /**
928316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen         * Gets the index of the item at the specified coordinates.
929316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen         *
930316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen         * @param itemX X-coordinate of the selected item.
931316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen         * @param itemY Y-coordinate of the selected item.
932316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen         * @return Index of the selected item in the cached array.
933316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen         */
934316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        public int getItemIndex(float itemX, float itemY) {
9353ce60186f646b461635b16d7d9a806ddc0b61bacYorke Lee            if (mMaxTiledRows == NO_ROW_LIMIT || mPosition < mMaxTiledRows) {
936316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                if (DEBUG) {
937316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                    Log.v(TAG, String.valueOf(itemX) + " " + String.valueOf(itemY));
938316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                }
939316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                for (int i = 0; i < getChildCount(); ++i) {
940316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                    /** If the row contains multiple tiles, checks each tile to see if the point
941316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                     * is contained in the tile. */
942774206bc2d92d542a2a064e0a0b1718418d3044fHongwei Wang                    final View child = getChildAt(i);
943774206bc2d92d542a2a064e0a0b1718418d3044fHongwei Wang                    /** The coordinates passed in are based on the ListView,
944774206bc2d92d542a2a064e0a0b1718418d3044fHongwei Wang                     * translate for each child first */
945774206bc2d92d542a2a064e0a0b1718418d3044fHongwei Wang                    final int xInListView = child.getLeft() + getLeft();
946774206bc2d92d542a2a064e0a0b1718418d3044fHongwei Wang                    final int yInListView = child.getTop() + getTop();
947774206bc2d92d542a2a064e0a0b1718418d3044fHongwei Wang                    final int distanceX = (int) itemX - xInListView;
948774206bc2d92d542a2a064e0a0b1718418d3044fHongwei Wang                    final int distanceY = (int) itemY - yInListView;
949774206bc2d92d542a2a064e0a0b1718418d3044fHongwei Wang                    if ((distanceX > 0 && distanceX < child.getWidth()) &&
950774206bc2d92d542a2a064e0a0b1718418d3044fHongwei Wang                            (distanceY > 0 && distanceY < child.getHeight())) {
951316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                        /** If the point is contained in the rectangle, computes the index of the
952316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                         * item in the cached array. */
953316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                        return i + (mPosition) * mColumnCount;
954316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                    }
955316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                }
956316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            } else {
957316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                /** If the selected item is one of the rows, compute the index. */
95872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                return getRegularRowItemIndex();
959316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            }
960316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            return -1;
961316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
962316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
96372418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        /**
96472418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang         * Gets the index of the regular row item.
96572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang         *
96672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang         * @return Index of the selected item in the cached array.
96772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang         */
96872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        public int getRegularRowItemIndex() {
96972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            return (mPosition - mMaxTiledRows) + mColumnCount * mMaxTiledRows;
97072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        }
97172418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang
972316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        public PhoneFavoritesTileAdapter getTileAdapter() {
973316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            return PhoneFavoritesTileAdapter.this;
974316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
975316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
976316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        public int getPosition() {
977316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            return mPosition;
978316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
97911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
980f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang        /**
981f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang         * Find the view under the pointer.
982f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang         */
983f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang        public View getViewAtPosition(int x, int y) {
98411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            // find the view under the pointer, accounting for GONE views
98511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            final int count = getChildCount();
986f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang            View view;
98711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            for (int childIdx = 0; childIdx < count; childIdx++) {
988f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang                view = getChildAt(childIdx);
989f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang                if (x >= view.getLeft() && x <= view.getRight()) {
990f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang                    return view;
99111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
99211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
99311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            return null;
99411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
99511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
99611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        @Override
997f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang        public View getChildAtPosition(MotionEvent ev) {
998f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang            final View view = getViewAtPosition((int) ev.getX(), (int) ev.getY());
999f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang            if (view != null &&
1000f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang                    SwipeHelper.isSwipeable(view) &&
1001f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang                    view.getVisibility() != GONE) {
1002f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang                // If this view is swipable, then return it. If not, because the removal
1003f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang                // dialog is currently showing, then return a null view, which will simply
1004f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang                // be ignored by the swipe helper.
1005f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang                return view;
1006f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang            }
1007f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang            return null;
1008f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang        }
1009f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang
1010f796309c9492475cc1911d22fda5319d6d4c4853Hongwei Wang        @Override
101111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public View getChildContentView(View v) {
101211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            return v.findViewById(R.id.contact_favorite_card);
101311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
101411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
101511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        @Override
101611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public void onScroll() {}
101711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
101811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        @Override
101911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public boolean canChildBeDismissed(View v) {
102011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            return true;
102111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
102211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
102311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        @Override
102411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public void onBeginDrag(View v) {
102511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            removePendingContactEntry();
102611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            final int index = indexOfChild(v);
1027a2bdac8d96eaed2dacea274c55036ca9c1a77b4dYorke Lee
1028a2bdac8d96eaed2dacea274c55036ca9c1a77b4dYorke Lee            /*
102911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            if (index > 0) {
103011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                detachViewFromParent(index);
103111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                attachViewToParent(v, 0, v.getLayoutParams());
1032a2bdac8d96eaed2dacea274c55036ca9c1a77b4dYorke Lee            }*/
103311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
103411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            // We do this so the underlying ScrollView knows that it won't get
103511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            // the chance to intercept events anymore
103611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            requestDisallowInterceptTouchEvent(true);
103711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
103811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
103911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        @Override
104011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public void onChildDismissed(View v) {
104111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            if (v != null) {
104211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (mOnItemSwipeListener != null) {
104311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mOnItemSwipeListener.onSwipe(v);
104411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
104511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
104611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
104711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
104811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        @Override
104911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public void onDragCancelled(View v) {}
105011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
105111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        @Override
105211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public boolean onInterceptTouchEvent(MotionEvent ev) {
10535a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee            if (mSwipeHelper != null && isSwipeEnabled()) {
105411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                return mSwipeHelper.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev);
105511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            } else {
105611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                return super.onInterceptTouchEvent(ev);
105711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
105811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
105911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
106011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        @Override
106111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public boolean onTouchEvent(MotionEvent ev) {
10625a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee            if (mSwipeHelper != null && isSwipeEnabled()) {
106311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev);
106411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            } else {
106511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                return super.onTouchEvent(ev);
106611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
106711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
106811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
106911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public int getItemViewType() {
107011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            return mItemViewType;
107111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
107211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
107311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public void setOnItemSwipeListener(OnItemGestureListener listener) {
107411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            mOnItemSwipeListener = listener;
107511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
1076dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
1077dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
107855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    /**
107955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * Used when a contact is swiped away. This will both unstar and set pinned position of the
108055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * contact to PinnedPosition.DEMOTED so that it doesn't show up anymore in the favorites list.
108155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     */
108255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    private void unstarAndUnpinContact(Uri contactUri) {
108355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        final ContentValues values = new ContentValues(2);
108455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        values.put(Contacts.STARRED, false);
108555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        values.put(Contacts.PINNED, PinnedPositions.DEMOTED);
108655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        mContext.getContentResolver().update(contactUri, values, null, null);
108755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    }
108855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
108955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    /**
109055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * Given a list of contacts that each have pinned positions, rearrange the list (destructive)
109155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * such that all pinned contacts are in their defined pinned positions, and unpinned contacts
109255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * take the spaces between those pinned contacts. Demoted contacts should not appear in the
109355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * resulting list.
109455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     *
109555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * This method also updates the pinned positions of pinned contacts so that they are all
109655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * unique positive integers within range from 0 to toArrange.size() - 1. This is because
109755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * when the contact entries are read from the database, it is possible for them to have
109855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * overlapping pin positions due to sync or modifications by third party apps.
109955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     */
110055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    @VisibleForTesting
110155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    /* package */ void arrangeContactsByPinnedPosition(ArrayList<ContactEntry> toArrange) {
110255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        final PriorityQueue<ContactEntry> pinnedQueue =
110355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                new PriorityQueue<ContactEntry>(PIN_LIMIT, mContactEntryComparator);
110455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
110555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        final List<ContactEntry> unpinnedContacts = new LinkedList<ContactEntry>();
110655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
110755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        final int length = toArrange.size();
110855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        for (int i = 0; i < length; i++) {
110955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            final ContactEntry contact = toArrange.get(i);
111055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            // Decide whether the contact is hidden(demoted), pinned, or unpinned
111155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            if (contact.pinned > PIN_LIMIT) {
111255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                unpinnedContacts.add(contact);
111355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            } else if (contact.pinned > PinnedPositions.DEMOTED) {
111455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // Demoted or contacts with negative pinned positions are ignored.
111555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // Pinned contacts go into a priority queue where they are ranked by pinned
111655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // position. This is required because the contacts provider does not return
111755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // contacts ordered by pinned position.
111855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                pinnedQueue.add(contact);
111955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            }
112055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        }
112155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
112255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        final int maxToPin = Math.min(PIN_LIMIT, pinnedQueue.size() + unpinnedContacts.size());
112355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
112455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        toArrange.clear();
112555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        for (int i = 0; i < maxToPin; i++) {
112655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            if (!pinnedQueue.isEmpty() && pinnedQueue.peek().pinned <= i) {
112755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                final ContactEntry toPin = pinnedQueue.poll();
112855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                toPin.pinned = i;
112955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                toArrange.add(toPin);
113055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            } else if (!unpinnedContacts.isEmpty()) {
113155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                toArrange.add(unpinnedContacts.remove(0));
113255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            }
113355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        }
113455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
113555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // If there are still contacts in pinnedContacts at this point, it means that the pinned
113655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // positions of these pinned contacts exceed the actual number of contacts in the list.
113755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // For example, the user had 10 frequents, starred and pinned one of them at the last spot,
113855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // and then cleared frequents. Contacts in this situation should become unpinned.
113955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        while (!pinnedQueue.isEmpty()) {
114055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            final ContactEntry entry = pinnedQueue.poll();
114155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            entry.pinned = PinnedPositions.UNPINNED;
114255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            toArrange.add(entry);
114355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        }
114455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
114555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // Any remaining unpinned contacts that weren't in the gaps between the pinned contacts
114655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // now just get appended to the end of the list.
114755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        toArrange.addAll(unpinnedContacts);
114855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    }
114955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
115055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    /**
115155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * Given an existing list of contact entries and a single entry that is to be pinned at a
115255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * particular position, return a ContentValues object that contains new pinned positions for
115355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * all contacts that are forced to be pinned at new positions, trying as much as possible to
115455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * keep pinned contacts at their original location.
115555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     *
115655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * At this point in time the pinned position of each contact in the list has already been
115755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * updated by {@link #arrangeContactsByPinnedPosition}, so we can assume that all pinned
115855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * positions(within {@link #PIN_LIMIT} are unique positive integers.
115955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     */
116055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    @VisibleForTesting
116155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    /* package */ ContentValues getReflowedPinnedPositions(ArrayList<ContactEntry> list,
116255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            ContactEntry entryToPin, int oldPos, int newPinPos) {
116355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
116455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        final ContentValues cv = new ContentValues();
116572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        final int lowerBound = Math.min(oldPos, newPinPos);
116672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        final int upperBound = Math.max(oldPos, newPinPos);
116772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        for (int i = lowerBound; i <= upperBound; i++) {
116872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            final ContactEntry entry = list.get(i);
116972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            if (entry.pinned == i) continue;
117072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            cv.put(String.valueOf(entry.id), i);
117155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        }
117255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        return cv;
117355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    }
117455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
1175dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected static class ViewTypes {
1176d99993213962df64c9d76005df86c2fb83fca074Yorke Lee        public static final int FREQUENT = 0;
1177d99993213962df64c9d76005df86c2fb83fca074Yorke Lee        public static final int TOP = 1;
11784fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        public static final int COUNT = 2;
1179dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
118011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
118111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    @Override
118211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public void onSwipe(View view) {
118311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final PhoneFavoriteTileView tileView = (PhoneFavoriteTileView) view.findViewById(
118411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                R.id.contact_tile);
118511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        // When the view is in the removal dialog, it should no longer be swipeable
118611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        SwipeHelper.setSwipeable(view, false);
118711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        tileView.displayRemovalDialog();
118811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
118911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final Integer entryIndex = (Integer) tileView.getTag(
119011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                ContactTileRow.CONTACT_ENTRY_INDEX_TAG);
119111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
119211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        setPotentialRemoveEntryIndex(entryIndex);
119311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
119411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
119511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    @Override
119611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public void onTouch() {
119711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        removePendingContactEntry();
11985a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee        return;
11995a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee    }
12005a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee
12015a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee    @Override
12025a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee    public boolean isSwipeEnabled() {
12035a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee        return !mAwaitingRemove;
120411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
120518e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee
12067639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang    @Override
1207d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee    public void onDragStarted(int itemIndex, int x, int y, PhoneFavoriteTileView view) {
12087639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang        setInDragging(true);
12097639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang        popContactEntry(itemIndex);
121018e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee    }
121172418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang
12127639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang    @Override
1213d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee    public void onDragHovered(int itemIndex, int x, int y) {
121472418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        if (mInDragging &&
121572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                mDragEnteredEntryIndex != itemIndex &&
121672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                isIndexInBound(itemIndex) &&
1217d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee                itemIndex < PIN_LIMIT &&
1218d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee                itemIndex >= 0) {
121972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            markDropArea(itemIndex);
122072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        }
122172418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    }
12227639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang
12237639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang    @Override
1224d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee    public void onDragFinished(int x, int y) {
12257639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang        setInDragging(false);
1226d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee        // A contact has been dragged to the RemoveView in order to be unstarred,  so simply wait
1227d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee        // for the new contact cursor which will cause the UI to be refreshed without the unstarred
1228d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee        // contact.
1229d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee        if (!mAwaitingRemove) {
1230d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee            handleDrop();
1231d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee        }
1232d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee    }
1233d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee
1234d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee    @Override
1235d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee    public void onDroppedOnRemove() {
1236d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee        if (mDraggedEntry != null) {
1237d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee            unstarAndUnpinContact(mDraggedEntry.lookupKey);
1238d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee            mAwaitingRemove = true;
1239d5b5d0a35d0f0e7dfd75828976dc3c0f95cdd672Yorke Lee        }
12407639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang    }
1241dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee}
1242