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
18765734c1d602c9a6d166d653b3684e6408b771c4Yorke Leeimport com.google.common.annotations.VisibleForTesting;
19765734c1d602c9a6d166d653b3684e6408b771c4Yorke Leeimport com.google.common.collect.ComparisonChain;
209b8cc3fa7ea04358624466162fe3ca942673963dYorke Leeimport com.google.common.collect.Lists;
21765734c1d602c9a6d166d653b3684e6408b771c4Yorke Lee
229b8cc3fa7ea04358624466162fe3ca942673963dYorke Leeimport android.content.ContentProviderOperation;
23dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.content.ContentUris;
2455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport android.content.ContentValues;
25dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.content.Context;
269b8cc3fa7ea04358624466162fe3ca942673963dYorke Leeimport android.content.OperationApplicationException;
27dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.content.res.Resources;
28dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.database.Cursor;
29dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.net.Uri;
309b8cc3fa7ea04358624466162fe3ca942673963dYorke Leeimport android.os.RemoteException;
319b8cc3fa7ea04358624466162fe3ca942673963dYorke Leeimport android.provider.ContactsContract;
32dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Phone;
33dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.provider.ContactsContract.Contacts;
3455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport android.provider.ContactsContract.PinnedPositions;
354e741192ec40278d9189b46cf2ae6bc547d9071dYorke Leeimport android.text.TextUtils;
36dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.util.Log;
374e741192ec40278d9189b46cf2ae6bc547d9071dYorke Leeimport android.util.LongSparseArray;
38dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.view.View;
39dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.view.ViewGroup;
40dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.widget.BaseAdapter;
41dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport android.widget.FrameLayout;
42dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
43dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.contacts.common.ContactPhotoManager;
44dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.contacts.common.ContactTileLoaderFactory;
45dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.contacts.common.list.ContactEntry;
4655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport com.android.contacts.common.list.ContactTileAdapter.DisplayType;
47dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport com.android.contacts.common.list.ContactTileView;
48fc6ed03fb6bd06cd0e0865d1e8a8cb065d5511f8Jeff Davidsonimport com.android.dialer.R;
4955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
50dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Leeimport java.util.ArrayList;
5155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport java.util.Comparator;
5255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport java.util.LinkedList;
5355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport java.util.List;
5455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Leeimport java.util.PriorityQueue;
55dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
56dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee/**
57dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee * Also allows for a configurable number of columns as well as a maximum row of tiled contacts.
58dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee */
5911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leepublic class PhoneFavoritesTileAdapter extends BaseAdapter implements
60475236caa815206b0d9fca8c2dc4b7a3a5b9c866Yorke Lee        OnDragDropListener {
61316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private static final String TAG = PhoneFavoritesTileAdapter.class.getSimpleName();
62316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private static final boolean DEBUG = false;
63dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
64765734c1d602c9a6d166d653b3684e6408b771c4Yorke Lee    public static final int NO_ROW_LIMIT = -1;
65765734c1d602c9a6d166d653b3684e6408b771c4Yorke Lee
66765734c1d602c9a6d166d653b3684e6408b771c4Yorke Lee    public static final int ROW_LIMIT_DEFAULT = NO_ROW_LIMIT;
67dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
68dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private ContactTileView.Listener mListener;
69c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    private OnDataSetChangedForAnimationListener mDataSetChangedListener;
70c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
71dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private Context mContext;
72dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private Resources mResources;
73316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
74316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** Contact data stored in cache. This is used to populate the associated view. */
75316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    protected ArrayList<ContactEntry> mContactEntries = null;
76316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** Back up of the temporarily removed Contact during dragging. */
77316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private ContactEntry mDraggedEntry = null;
78316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** Position of the temporarily removed contact in the cache. */
79316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private int mDraggedEntryIndex = -1;
80316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** New position of the temporarily removed contact in the cache. */
81316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private int mDropEntryIndex = -1;
8272418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    /** New position of the temporarily entered contact in the cache. */
8372418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    private int mDragEnteredEntryIndex = -1;
84316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
855a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee    private boolean mAwaitingRemove = false;
86670fd3f59160bdb81f38b6be2f176ba233915686Yorke Lee    private boolean mDelayCursorUpdates = false;
875a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee
88dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private ContactPhotoManager mPhotoManager;
89dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mNumFrequents;
906a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee    protected int mNumStarred;
91dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
92dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mIdIndex;
93dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mLookupIndex;
94dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mPhotoUriIndex;
95dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mNameIndex;
96dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mPresenceIndex;
97dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected int mStatusIndex;
98dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
99dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private int mPhoneNumberIndex;
100dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private int mPhoneNumberTypeIndex;
101dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    private int mPhoneNumberLabelIndex;
10243f543900efac8edc10159851184da8cfca3d81aYorke Lee    private int mIsDefaultNumberIndex;
103efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee    private int mStarredIndex;
10455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    protected int mPinnedIndex;
105791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee    protected int mContactIdIndex;
106dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
107316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** Indicates whether a drag is in process. */
108316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private boolean mInDragging = false;
109316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
1100a3ad7a81821da5b8f4bde566368f5dbc9889aadYorke Lee    // Pinned positions start from 1, so there are a total of 20 maximum pinned contacts
1110a3ad7a81821da5b8f4bde566368f5dbc9889aadYorke Lee    public static final int PIN_LIMIT = 21;
11255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
113d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang    /**
114d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     * The soft limit on how many contact tiles to show.
115d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     * NOTE This soft limit would not restrict the number of starred contacts to show, rather
116d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     * 1. If the count of starred contacts is less than this limit, show 20 tiles total.
117d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     * 2. If the count of starred contacts is more than or equal to this limit,
118d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     * show all starred tiles and no frequents.
119d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang     */
120d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang    private static final int TILES_SOFT_LIMIT = 20;
121d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang
12255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    final Comparator<ContactEntry> mContactEntryComparator = new Comparator<ContactEntry>() {
12355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        @Override
12455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        public int compare(ContactEntry lhs, ContactEntry rhs) {
12555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            return ComparisonChain.start()
12655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                    .compare(lhs.pinned, rhs.pinned)
12755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                    .compare(lhs.name, rhs.name)
12855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                    .result();
12955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        }
13055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    };
13155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
132c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    public interface OnDataSetChangedForAnimationListener {
133c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        public void onDataSetChangedForAnimation(long... idsInPlace);
134c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        public void cacheOffsetsForDatasetChange();
135c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    };
136dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
137316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public PhoneFavoritesTileAdapter(Context context, ContactTileView.Listener listener,
138efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee            OnDataSetChangedForAnimationListener dataSetChangedListener) {
139c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        mDataSetChangedListener = dataSetChangedListener;
140dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mListener = listener;
141dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mContext = context;
142dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mResources = context.getResources();
143dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mNumFrequents = 0;
144316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mContactEntries = new ArrayList<ContactEntry>();
145efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee
146316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
147dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        bindColumnIndices();
148dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
149dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
150dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void setPhotoLoader(ContactPhotoManager photoLoader) {
151dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPhotoManager = photoLoader;
152dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
153dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
154dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
155316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Indicates whether a drag is in process.
156316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     *
157316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * @param inDragging Boolean variable indicating whether there is a drag in process.
158316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
159316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public void setInDragging(boolean inDragging) {
160670fd3f59160bdb81f38b6be2f176ba233915686Yorke Lee        mDelayCursorUpdates = inDragging;
161316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mInDragging = inDragging;
162316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
163316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
164316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /** Gets whether the drag is in process. */
165316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public boolean getInDragging() {
166316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        return mInDragging;
167316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
168316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
169316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
170dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Sets the column indices for expected {@link Cursor}
171dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * based on {@link DisplayType}.
172dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
173dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected void bindColumnIndices() {
174dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mIdIndex = ContactTileLoaderFactory.CONTACT_ID;
175dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mLookupIndex = ContactTileLoaderFactory.LOOKUP_KEY;
176dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPhotoUriIndex = ContactTileLoaderFactory.PHOTO_URI;
177dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mNameIndex = ContactTileLoaderFactory.DISPLAY_NAME;
178dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mStarredIndex = ContactTileLoaderFactory.STARRED;
179dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPresenceIndex = ContactTileLoaderFactory.CONTACT_PRESENCE;
180dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mStatusIndex = ContactTileLoaderFactory.CONTACT_STATUS;
181dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
182dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPhoneNumberIndex = ContactTileLoaderFactory.PHONE_NUMBER;
183dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPhoneNumberTypeIndex = ContactTileLoaderFactory.PHONE_NUMBER_TYPE;
184dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        mPhoneNumberLabelIndex = ContactTileLoaderFactory.PHONE_NUMBER_LABEL;
18543f543900efac8edc10159851184da8cfca3d81aYorke Lee        mIsDefaultNumberIndex = ContactTileLoaderFactory.IS_DEFAULT_NUMBER;
18655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        mPinnedIndex = ContactTileLoaderFactory.PINNED;
187791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee        mContactIdIndex = ContactTileLoaderFactory.CONTACT_ID_FOR_DATA;
188dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
189dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
190dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
191dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Gets the number of frequents from the passed in cursor.
192dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     *
193dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * This methods is needed so the GroupMemberTileAdapter can override this.
194dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     *
195dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * @param cursor The cursor to get number of frequents from.
196dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
197dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected void saveNumFrequentsFromCursor(Cursor cursor) {
1986a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee        mNumFrequents = cursor.getCount() - mNumStarred;
199dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
200dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
201dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
202dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Creates {@link ContactTileView}s for each item in {@link Cursor}.
203dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     *
204dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Else use {@link ContactTileLoaderFactory}
205dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
206dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public void setContactCursor(Cursor cursor) {
207670fd3f59160bdb81f38b6be2f176ba233915686Yorke Lee        if (!mDelayCursorUpdates && cursor != null && !cursor.isClosed()) {
208316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            mNumStarred = getNumStarredContacts(cursor);
209c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            if (mAwaitingRemove) {
210c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                mDataSetChangedListener.cacheOffsetsForDatasetChange();
211c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            }
212c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
213316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            saveNumFrequentsFromCursor(cursor);
214316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            saveCursorToCache(cursor);
215316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            // cause a refresh of any views that rely on this data
216316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            notifyDataSetChanged();
217c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee            // about to start redraw
218efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee            mDataSetChangedListener.onDataSetChangedForAnimation();
219316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
220316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
221dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
222316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
223316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Saves the cursor data to the cache, to speed up UI changes.
224316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     *
225316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * @param cursor Returned cursor with data to populate the view.
226316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
227316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    private void saveCursorToCache(Cursor cursor) {
228316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mContactEntries.clear();
22955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
230443d758d53d1f7ebb12e8ed39fc6195c9d5ed18dYorke Lee        cursor.moveToPosition(-1);
23155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
2324e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee        final LongSparseArray<Object> duplicates = new LongSparseArray<Object>(cursor.getCount());
233443d758d53d1f7ebb12e8ed39fc6195c9d5ed18dYorke Lee
234d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang        // Track the length of {@link #mContactEntries} and compare to {@link #TILES_SOFT_LIMIT}.
235d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang        int counter = 0;
236d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang
2374e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee        while (cursor.moveToNext()) {
2384e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
2394e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final int starred = cursor.getInt(mStarredIndex);
2404e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final long id;
24155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
242791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            // We display a maximum of TILES_SOFT_LIMIT contacts, or the total number of starred
243791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            // whichever is greater.
244791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            if (starred < 1 && counter >= TILES_SOFT_LIMIT) {
245d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang                break;
24655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            } else {
247791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee                id = cursor.getLong(mContactIdIndex);
24855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            }
2494e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
25043f543900efac8edc10159851184da8cfca3d81aYorke Lee            final ContactEntry existing = (ContactEntry) duplicates.get(id);
25143f543900efac8edc10159851184da8cfca3d81aYorke Lee            if (existing != null) {
25243f543900efac8edc10159851184da8cfca3d81aYorke Lee                // Check if the existing number is a default number. If not, clear the phone number
25343f543900efac8edc10159851184da8cfca3d81aYorke Lee                // and label fields so that the disambiguation dialog will show up.
25443f543900efac8edc10159851184da8cfca3d81aYorke Lee                if (!existing.isDefaultNumber) {
25543f543900efac8edc10159851184da8cfca3d81aYorke Lee                    existing.phoneLabel = null;
25643f543900efac8edc10159851184da8cfca3d81aYorke Lee                    existing.phoneNumber = null;
25743f543900efac8edc10159851184da8cfca3d81aYorke Lee                }
2584e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee                continue;
2594e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            }
2604e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
2614e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final String photoUri = cursor.getString(mPhotoUriIndex);
2624e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final String lookupKey = cursor.getString(mLookupIndex);
2634e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final int pinned = cursor.getInt(mPinnedIndex);
2644e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final String name = cursor.getString(mNameIndex);
2659bead9c01ab09ebfbd1c2be8555d8c8d4e3cf6bdChristine Chen            final boolean isStarred = cursor.getInt(mStarredIndex) > 0;
26643f543900efac8edc10159851184da8cfca3d81aYorke Lee            final boolean isDefaultNumber = cursor.getInt(mIsDefaultNumberIndex) > 0;
2674e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
2684e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            final ContactEntry contact = new ContactEntry();
2694e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
2704e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            contact.id = id;
2714e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee            contact.name = (!TextUtils.isEmpty(name)) ? name :
2724e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee                    mResources.getString(R.string.missing_name);
273443d758d53d1f7ebb12e8ed39fc6195c9d5ed18dYorke Lee            contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
27456cb0efa5eda1670077e66fc0e8c79478d0c1c67Yorke Lee            contact.lookupKey = lookupKey;
27556cb0efa5eda1670077e66fc0e8c79478d0c1c67Yorke Lee            contact.lookupUri = ContentUris.withAppendedId(
276443d758d53d1f7ebb12e8ed39fc6195c9d5ed18dYorke Lee                    Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
2779bead9c01ab09ebfbd1c2be8555d8c8d4e3cf6bdChristine Chen            contact.isFavorite = isStarred;
27843f543900efac8edc10159851184da8cfca3d81aYorke Lee            contact.isDefaultNumber = isDefaultNumber;
2794e741192ec40278d9189b46cf2ae6bc547d9071dYorke Lee
28043f543900efac8edc10159851184da8cfca3d81aYorke Lee            // Set phone number and label
281791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            final int phoneNumberType = cursor.getInt(mPhoneNumberTypeIndex);
282791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            final String phoneNumberCustomLabel = cursor.getString(mPhoneNumberLabelIndex);
283791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            contact.phoneLabel = (String) Phone.getTypeLabel(mResources, phoneNumberType,
284791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee                    phoneNumberCustomLabel);
285791082e22b50db98de6749bb5ef878d3ec483e28Yorke Lee            contact.phoneNumber = cursor.getString(mPhoneNumberIndex);
28655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
28755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            contact.pinned = pinned;
288443d758d53d1f7ebb12e8ed39fc6195c9d5ed18dYorke Lee            mContactEntries.add(contact);
289d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang
29043f543900efac8edc10159851184da8cfca3d81aYorke Lee            duplicates.put(id, contact);
29143f543900efac8edc10159851184da8cfca3d81aYorke Lee
292d954cd76c610e8fafd91aec857f4232ab49ef2cdHongwei Wang            counter++;
293316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
29455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
2955a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee        mAwaitingRemove = false;
2965a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee
29755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        arrangeContactsByPinnedPosition(mContactEntries);
29855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
29955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        notifyDataSetChanged();
300dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
301dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
302dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
303dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Iterates over the {@link Cursor}
304dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Returns position of the first NON Starred Contact
305dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Returns -1 if {@link DisplayType#STARRED_ONLY}
306dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Returns 0 if {@link DisplayType#FREQUENT_ONLY}
307dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
3086a8402939f667ab70bd0043ed0859f8afbc012ecYorke Lee    protected int getNumStarredContacts(Cursor cursor) {
309dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        cursor.moveToPosition(-1);
310dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        while (cursor.moveToNext()) {
311dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            if (cursor.getInt(mStarredIndex) == 0) {
312dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee                return cursor.getPosition();
313dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee            }
314dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
315dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
316dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        // There are not NON Starred contacts in cursor
317dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        // Set divider positon to end
318dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        return cursor.getCount();
319dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
320dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
321316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
322dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Returns the number of frequents that will be displayed in the list.
323dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
324dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public int getNumFrequents() {
325dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        return mNumFrequents;
326dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
327dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
328dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
329dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public int getCount() {
330efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        if (mContactEntries == null) {
3314fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen            return 0;
332dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
333dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
334efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        return mContactEntries.size();
335dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
336dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
337dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
338dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * Returns an ArrayList of the {@link ContactEntry}s that are to appear
339dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     * on the row for the given position.
340dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee     */
341dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
342efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee    public ContactEntry getItem(int position) {
343efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        return mContactEntries.get(position);
344dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
345dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
346c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    /**
347c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * For the top row of tiled contacts, the item id is the position of the row of
348c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * contacts.
349c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * For frequent contacts, the item id is the maximum number of rows of tiled contacts +
350c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * the actual contact id. Since contact ids are always greater than 0, this guarantees that
351c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     * all items within this adapter will always have unique ids.
352c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee     */
353dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
354dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public long getItemId(int position) {
355efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        return getItem(position).id;
356c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    }
357c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee
358c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    @Override
359c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee    public boolean hasStableIds() {
360c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee        return true;
361dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
362dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
363dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
364dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public boolean areAllItemsEnabled() {
365dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        return true;
366dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
367dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
368dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
369dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public boolean isEnabled(int position) {
3704fdf594962b3928c6497b471dbb1ee7be56e18efChristine Chen        return getCount() > 0;
371dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
372dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
373dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
374316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public void notifyDataSetChanged() {
375316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        if (DEBUG) {
37655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            Log.v(TAG, "notifyDataSetChanged");
377316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
378316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        super.notifyDataSetChanged();
379316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
380316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
381316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    @Override
382dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public View getView(int position, View convertView, ViewGroup parent) {
38355d3bb4a7f70d4a4359c31b3240f5740b788e643Yorke Lee        if (DEBUG) {
38455d3bb4a7f70d4a4359c31b3240f5740b788e643Yorke Lee            Log.v(TAG, "get view for " + String.valueOf(position));
38555d3bb4a7f70d4a4359c31b3240f5740b788e643Yorke Lee        }
38618e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee
387dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        int itemViewType = getItemViewType(position);
388dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
389efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        PhoneFavoriteTileView tileView = null;
3901f06f0ad1229f3b4e00553c294e664ffa5234c90Christine Chen
391efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        if (convertView instanceof PhoneFavoriteTileView) {
392efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee            tileView  = (PhoneFavoriteTileView) convertView;
3931f06f0ad1229f3b4e00553c294e664ffa5234c90Christine Chen        }
3946091473941d277ed3746143c1ca9bffdfbe2bd94Christine Chen
395efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        if (tileView == null) {
396efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee            tileView = (PhoneFavoriteTileView) View.inflate(mContext,
397efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee                    R.layout.phone_favorite_tile_view, null);
398dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        }
399efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        tileView.setPhotoManager(mPhotoManager);
40001fb754b6b4b869c58c3fdcc74735e027f8cea97Yorke Lee        tileView.setListener(mListener);
401efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        tileView.loadFromContact(getItem(position));
402efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        return tileView;
403dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
404dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
405dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
406dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public int getViewTypeCount() {
407dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee        return ViewTypes.COUNT;
408dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
409dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
410dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    @Override
411dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    public int getItemViewType(int position) {
412efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        return ViewTypes.TILE;
413dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
414dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee
415dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    /**
416316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Temporarily removes a contact from the list for UI refresh. Stores data for this contact
417316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * in the back-up variable.
418316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     *
419316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * @param index Position of the contact to be removed.
420316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
421316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public void popContactEntry(int index) {
42272418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        if (isIndexInBound(index)) {
423316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            mDraggedEntry = mContactEntries.get(index);
424316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            mDraggedEntryIndex = index;
42572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mDragEnteredEntryIndex = index;
42672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            markDropArea(mDragEnteredEntryIndex);
42772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        }
42872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    }
42972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang
43072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    /**
43172418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     * @param itemIndex Position of the contact in {@link #mContactEntries}.
43272418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     * @return True if the given index is valid for {@link #mContactEntries}.
43372418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     */
434e48124fa2d295c68cdb95f962ef8bd6d35a7c145Anthony Lee    public boolean isIndexInBound(int itemIndex) {
43572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        return itemIndex >= 0 && itemIndex < mContactEntries.size();
43672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    }
43772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang
43872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    /**
43972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     * Mark the tile as drop area by given the item index in {@link #mContactEntries}.
44072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     *
44172418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     * @param itemIndex Position of the contact in {@link #mContactEntries}.
44272418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang     */
44372418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    private void markDropArea(int itemIndex) {
444c57f6640569acb1df4fc5d7d2b026e824351b531Jay Shrauner        if (mDraggedEntry != null && isIndexInBound(mDragEnteredEntryIndex) &&
445c57f6640569acb1df4fc5d7d2b026e824351b531Jay Shrauner                isIndexInBound(itemIndex)) {
4465b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang            mDataSetChangedListener.cacheOffsetsForDatasetChange();
44772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            // Remove the old placeholder item and place the new placeholder item.
4485b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang            final int oldIndex = mDragEnteredEntryIndex;
44972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mContactEntries.remove(mDragEnteredEntryIndex);
45072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mDragEnteredEntryIndex = itemIndex;
45172418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mContactEntries.add(mDragEnteredEntryIndex, ContactEntry.BLANK_ENTRY);
45272418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            ContactEntry.BLANK_ENTRY.id = mDraggedEntry.id;
4535b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang            mDataSetChangedListener.onDataSetChangedForAnimation();
454316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            notifyDataSetChanged();
455316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
456316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
457316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
458316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
459316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Drops the temporarily removed contact to the desired location in the list.
460316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
46172418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    public void handleDrop() {
46255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        boolean changed = false;
463316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        if (mDraggedEntry != null) {
4644911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang            if (isIndexInBound(mDragEnteredEntryIndex) &&
4654911300ae3fa7ab186ee271f8bc431b65a819a3cHongwei Wang                    mDragEnteredEntryIndex != mDraggedEntryIndex) {
46655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // Don't add the ContactEntry here (to prevent a double animation from occuring).
46755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // When we receive a new cursor the list of contact entries will automatically be
46855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // populated with the dragged ContactEntry at the correct spot.
46972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                mDropEntryIndex = mDragEnteredEntryIndex;
47072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                mContactEntries.set(mDropEntryIndex, mDraggedEntry);
471c36684277aa45085999284bfe71cb8be71b3a464Yorke Lee                mDataSetChangedListener.cacheOffsetsForDatasetChange();
47255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                changed = true;
47372418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            } else if (isIndexInBound(mDraggedEntryIndex)) {
47472418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                // If {@link #mDragEnteredEntryIndex} is invalid,
47572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                // falls back to the original position of the contact.
47672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                mContactEntries.remove(mDragEnteredEntryIndex);
47772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                mContactEntries.add(mDraggedEntryIndex, mDraggedEntry);
478316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen                mDropEntryIndex = mDraggedEntryIndex;
47955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                notifyDataSetChanged();
48055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            }
48155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
48255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            if (changed && mDropEntryIndex < PIN_LIMIT) {
4839b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                final ArrayList<ContentProviderOperation> operations =
4849b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                        getReflowedPinningOperations(mContactEntries, mDraggedEntryIndex,
4859b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                                mDropEntryIndex);
4869b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                if (!operations.isEmpty()) {
4879b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                    // update the database here with the new pinned positions
4889b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                    try {
4899b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                        mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY,
4909b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                                operations);
4919b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                    } catch (RemoteException | OperationApplicationException e) {
4929b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                        Log.e(TAG, "Exception thrown when pinning contacts", e);
4939b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                    }
4949b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee                }
495316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            }
496316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen            mDraggedEntry = null;
497316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        }
498316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
499316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
500316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
50101f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen     * Invoked when the dragged item is dropped to unsupported location. We will then move the
50201f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen     * contact back to where it was dragged from.
50301f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen     */
50401f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen    public void dropToUnsupportedView() {
50572418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        if (isIndexInBound(mDragEnteredEntryIndex)) {
50672418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mContactEntries.remove(mDragEnteredEntryIndex);
50772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            mContactEntries.add(mDraggedEntryIndex, mDraggedEntry);
50872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            notifyDataSetChanged();
50972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        }
51001f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen    }
51101f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen
51201f15ad65dfbc72d7c8f165bda8e602ba287b434Christine Chen    /**
513316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     * Clears all temporary variables at a new interaction.
514316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen     */
515316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    public void cleanTempVariables() {
516316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mDraggedEntryIndex = -1;
517316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mDropEntryIndex = -1;
51872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        mDragEnteredEntryIndex = -1;
519316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen        mDraggedEntry = null;
520316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    }
521316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen
522316b4713b2f8f26f393ecc4bb4760512a4a9f096Christine Chen    /**
523efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee     * Used when a contact is removed from speeddial. This will both unstar and set pinned position
524efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee     * of the contact to PinnedPosition.DEMOTED so that it doesn't show up anymore in the favorites
525efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee     * list.
52655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     */
52755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    private void unstarAndUnpinContact(Uri contactUri) {
52855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        final ContentValues values = new ContentValues(2);
52955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        values.put(Contacts.STARRED, false);
53055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        values.put(Contacts.PINNED, PinnedPositions.DEMOTED);
53155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        mContext.getContentResolver().update(contactUri, values, null, null);
53255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    }
53355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
53455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    /**
53555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * Given a list of contacts that each have pinned positions, rearrange the list (destructive)
53655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * such that all pinned contacts are in their defined pinned positions, and unpinned contacts
53755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * take the spaces between those pinned contacts. Demoted contacts should not appear in the
53855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * resulting list.
53955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     *
54055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * This method also updates the pinned positions of pinned contacts so that they are all
54155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * unique positive integers within range from 0 to toArrange.size() - 1. This is because
54255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * when the contact entries are read from the database, it is possible for them to have
54355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * overlapping pin positions due to sync or modifications by third party apps.
54455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     */
54555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    @VisibleForTesting
54655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    /* package */ void arrangeContactsByPinnedPosition(ArrayList<ContactEntry> toArrange) {
54755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        final PriorityQueue<ContactEntry> pinnedQueue =
54855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                new PriorityQueue<ContactEntry>(PIN_LIMIT, mContactEntryComparator);
54955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
55055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        final List<ContactEntry> unpinnedContacts = new LinkedList<ContactEntry>();
55155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
55255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        final int length = toArrange.size();
55355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        for (int i = 0; i < length; i++) {
55455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            final ContactEntry contact = toArrange.get(i);
55555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            // Decide whether the contact is hidden(demoted), pinned, or unpinned
5560a3ad7a81821da5b8f4bde566368f5dbc9889aadYorke Lee            if (contact.pinned > PIN_LIMIT || contact.pinned == PinnedPositions.UNPINNED) {
55755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                unpinnedContacts.add(contact);
55855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            } else if (contact.pinned > PinnedPositions.DEMOTED) {
55955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // Demoted or contacts with negative pinned positions are ignored.
56055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // Pinned contacts go into a priority queue where they are ranked by pinned
56155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // position. This is required because the contacts provider does not return
56255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                // contacts ordered by pinned position.
56355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                pinnedQueue.add(contact);
56455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            }
56555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        }
56655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
56755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        final int maxToPin = Math.min(PIN_LIMIT, pinnedQueue.size() + unpinnedContacts.size());
56855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
56955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        toArrange.clear();
5700a3ad7a81821da5b8f4bde566368f5dbc9889aadYorke Lee        for (int i = 1; i < maxToPin + 1; i++) {
57155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            if (!pinnedQueue.isEmpty() && pinnedQueue.peek().pinned <= i) {
57255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                final ContactEntry toPin = pinnedQueue.poll();
57355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                toPin.pinned = i;
57455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                toArrange.add(toPin);
57555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            } else if (!unpinnedContacts.isEmpty()) {
57655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee                toArrange.add(unpinnedContacts.remove(0));
57755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            }
57855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        }
57955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
58055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // If there are still contacts in pinnedContacts at this point, it means that the pinned
58155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // positions of these pinned contacts exceed the actual number of contacts in the list.
58255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // For example, the user had 10 frequents, starred and pinned one of them at the last spot,
58355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // and then cleared frequents. Contacts in this situation should become unpinned.
58455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        while (!pinnedQueue.isEmpty()) {
58555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            final ContactEntry entry = pinnedQueue.poll();
58655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            entry.pinned = PinnedPositions.UNPINNED;
58755f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee            toArrange.add(entry);
58855f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        }
58955f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
59055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // Any remaining unpinned contacts that weren't in the gaps between the pinned contacts
59155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        // now just get appended to the end of the list.
59255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        toArrange.addAll(unpinnedContacts);
59355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    }
59455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
59555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    /**
59655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * Given an existing list of contact entries and a single entry that is to be pinned at a
5979b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee     * particular position, return a list of {@link ContentProviderOperation}s that contains new
5989b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee     * pinned positions for all contacts that are forced to be pinned at new positions, trying as
5999b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee     * much as possible to keep pinned contacts at their original location.
60055f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     *
60155f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * At this point in time the pinned position of each contact in the list has already been
60255f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * updated by {@link #arrangeContactsByPinnedPosition}, so we can assume that all pinned
60355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     * positions(within {@link #PIN_LIMIT} are unique positive integers.
60455f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee     */
60555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    @VisibleForTesting
6069b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee    /* package */ ArrayList<ContentProviderOperation> getReflowedPinningOperations(
6079b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee            ArrayList<ContactEntry> list, int oldPos, int newPinPos) {
6089b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee        final ArrayList<ContentProviderOperation> positions = Lists.newArrayList();
60972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        final int lowerBound = Math.min(oldPos, newPinPos);
61072418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        final int upperBound = Math.max(oldPos, newPinPos);
61172418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        for (int i = lowerBound; i <= upperBound; i++) {
61272418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            final ContactEntry entry = list.get(i);
61349447a9859b6f45dd4eb4cb9302931c725257038Yorke Lee
61449447a9859b6f45dd4eb4cb9302931c725257038Yorke Lee            // Pinned positions in the database start from 1 instead of being zero-indexed like
61549447a9859b6f45dd4eb4cb9302931c725257038Yorke Lee            // arrays, so offset by 1.
61649447a9859b6f45dd4eb4cb9302931c725257038Yorke Lee            final int databasePinnedPosition = i + 1;
61749447a9859b6f45dd4eb4cb9302931c725257038Yorke Lee            if (entry.pinned == databasePinnedPosition) continue;
6189b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee
6199b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee            final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_URI, String.valueOf(entry.id));
6209b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee            final ContentValues values = new ContentValues();
62149447a9859b6f45dd4eb4cb9302931c725257038Yorke Lee            values.put(Contacts.PINNED, databasePinnedPosition);
6229b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee            positions.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
62355f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee        }
6249b8cc3fa7ea04358624466162fe3ca942673963dYorke Lee        return positions;
62555f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee    }
62655f3ec9f882b2389e498ea68f68ce8670c5d4c73Yorke Lee
627dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    protected static class ViewTypes {
628efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        public static final int TILE = 0;
62964f9593c85ac00dbcdbc99d2d18c457c0bd0f68eYorke Lee        public static final int COUNT = 1;
630dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee    }
63111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
63211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    @Override
633efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee    public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
6347639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang        setInDragging(true);
635efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        final int itemIndex = mContactEntries.indexOf(view.getContactEntry());
6367639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang        popContactEntry(itemIndex);
63718e7fdd55f67e3636c161462cb54e7f5555a04deYorke Lee    }
63872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang
6397639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang    @Override
640efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee    public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {
641efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        if (view == null) {
642efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee            // The user is hovering over a view that is not a contact tile, no need to do
643efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee            // anything here.
644efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee            return;
645efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        }
646efb2d9c2e32ca4ed434ff785c4b3d18c9e08db6dYorke Lee        final int itemIndex = mContactEntries.indexOf(view.getContactEntry());
64772418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        if (mInDragging &&
64872418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                mDragEnteredEntryIndex != itemIndex &&
64972418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang                isIndexInBound(itemIndex) &&
6503cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee                itemIndex < PIN_LIMIT &&
6513cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee                itemIndex >= 0) {
65272418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang            markDropArea(itemIndex);
65372418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang        }
65472418af9f84b7a97ff8cb08b99aa68b2db9ee1a9Hongwei Wang    }
6557639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang
6567639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang    @Override
6573cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee    public void onDragFinished(int x, int y) {
6587639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang        setInDragging(false);
6593cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee        // A contact has been dragged to the RemoveView in order to be unstarred,  so simply wait
6603cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee        // for the new contact cursor which will cause the UI to be refreshed without the unstarred
6613cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee        // contact.
6623cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee        if (!mAwaitingRemove) {
6633cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee            handleDrop();
6643cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee        }
6653cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee    }
6663cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee
6673cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee    @Override
6683cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee    public void onDroppedOnRemove() {
6693cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee        if (mDraggedEntry != null) {
67056cb0efa5eda1670077e66fc0e8c79478d0c1c67Yorke Lee            unstarAndUnpinContact(mDraggedEntry.lookupUri);
6713cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee            mAwaitingRemove = true;
6723cefcc69c10ab12bd782a0b53779dbd1cc0e2aa1Yorke Lee        }
6737639fe9a3ecc9c197718a9f81d8a745d2f660cb3Hongwei Wang    }
674dfb2eee7d98f8540fd1614db66bb03e8e1f3a26aYorke Lee}
675