ContactEditorFragment.java revision 916cf2661ada41a83556884d8f5b322d32ef452b
1cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann/*
2ef3f8f045ed4150caa64749742227acd0e6b85d8Daniel Lehmann * Copyright (C) 2010 The Android Open Source Project
3cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann *
4cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann * Licensed under the Apache License, Version 2.0 (the "License");
5cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann * you may not use this file except in compliance with the License.
6cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann * You may obtain a copy of the License at
7cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann *
8cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann *      http://www.apache.org/licenses/LICENSE-2.0
9cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann *
10cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann * Unless required by applicable law or agreed to in writing, software
11cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann * distributed under the License is distributed on an "AS IS" BASIS,
12cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann * See the License for the specific language governing permissions and
14cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann * limitations under the License
15cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann */
16cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
17cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannpackage com.android.contacts.views.editor;
18cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
19f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport com.android.contacts.JoinContactActivity;
20cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport com.android.contacts.R;
2135769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmannimport com.android.contacts.model.BaseAccountType;
22f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport com.android.contacts.model.EntityDelta;
23f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport com.android.contacts.model.EntityDelta.ValuesDelta;
24ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmannimport com.android.contacts.model.EntityDeltaList;
25f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport com.android.contacts.model.EntityModifier;
2635769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmannimport com.android.contacts.model.GoogleAccountType;
2735769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmannimport com.android.contacts.model.AccountTypes;
28f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport com.android.contacts.util.EmptyService;
29f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport com.android.contacts.util.WeakAsyncTask;
30bb11be3f5867453e7561cdc7bebe6a380eff6881Daniel Lehmannimport com.android.contacts.views.ContactLoader;
31e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport com.android.contacts.views.GroupMetaDataLoader;
32f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport com.android.contacts.views.editor.AggregationSuggestionEngine.Suggestion;
33afeae64e9a9bb39c918202a5775c4904e2f0d92eDaniel Lehmannimport com.android.contacts.views.editor.Editor.EditorListener;
34f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
35f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.accounts.Account;
36cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.app.Activity;
37222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmannimport android.app.Fragment;
38222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmannimport android.app.LoaderManager;
39222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmannimport android.app.LoaderManager.LoaderCallbacks;
40825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmannimport android.app.ProgressDialog;
41cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.content.ActivityNotFoundException;
42f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.content.ContentProviderOperation;
43f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.content.ContentProviderOperation.Builder;
44f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.content.ContentProviderResult;
45f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.content.ContentResolver;
46cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.content.ContentUris;
47cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.content.ContentValues;
48cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.content.Context;
49e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.content.CursorLoader;
50e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikovimport android.content.Entity;
51cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.content.Intent;
52cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.content.Loader;
53f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.content.OperationApplicationException;
54f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.database.Cursor;
55f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.graphics.Bitmap;
56f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.graphics.Rect;
57f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.media.MediaScannerConnection;
58cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.net.Uri;
59cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.os.Bundle;
60f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.os.Environment;
61f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.os.RemoteException;
629767e09ae37332d74b5558a3ec16ded20a9328dcDaniel Lehmannimport android.os.SystemClock;
63f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.provider.ContactsContract;
64f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport android.provider.ContactsContract.AggregationExceptions;
65cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.provider.ContactsContract.CommonDataKinds.Email;
66cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.provider.ContactsContract.CommonDataKinds.Phone;
6741f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
68f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.ContactsContract.Contacts;
696a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikovimport android.provider.ContactsContract.Intents.Insert;
70f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
71f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.MediaStore;
7241f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikovimport android.text.TextUtils;
73cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.util.Log;
74cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.view.LayoutInflater;
75cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.view.Menu;
76cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.view.MenuInflater;
77cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.view.MenuItem;
78cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.view.View;
79cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.view.ViewGroup;
80f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.view.ViewGroup.LayoutParams;
81f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.view.ViewStub;
8280d527c94389cab477e2c75ded03eec877ea6835Daniel Lehmannimport android.widget.LinearLayout;
83f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.widget.TextView;
84cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport android.widget.Toast;
85cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
86f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport java.io.File;
87825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmannimport java.lang.ref.WeakReference;
88f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport java.text.SimpleDateFormat;
89cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmannimport java.util.ArrayList;
90f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport java.util.Collections;
91f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport java.util.Comparator;
92f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmannimport java.util.Date;
93f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport java.util.List;
94f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
95a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmannpublic class ContactEditorFragment extends Fragment implements
96a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        SplitContactConfirmationDialogFragment.Listener, PickPhotoDialogFragment.Listener,
974a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        SelectAccountDialogFragment.Listener, ModifyPhotoDialogFragment.Listener,
984a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        AggregationSuggestionEngine.Listener {
99cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
100cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    private static final String TAG = "ContactEditorFragment";
101cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
102f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private static final int LOADER_DATA = 1;
103e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov    private static final int LOADER_GROUPS = 2;
104f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
105d364314c460152db7089b304beec81b3675893baDmitri Plotnikov    private static final String KEY_URI = "uri";
106d364314c460152db7089b304beec81b3675893baDmitri Plotnikov    private static final String KEY_ACTION = "action";
107f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private static final String KEY_EDIT_STATE = "state";
108f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private static final String KEY_RAW_CONTACT_ID_REQUESTING_PHOTO = "photorequester";
109f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private static final String KEY_VIEW_ID_GENERATOR = "viewidgenerator";
110f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile";
111f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private static final String KEY_QUERY_SELECTION = "queryselection";
112f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private static final String KEY_CONTACT_ID_FOR_JOIN = "contactidforjoin";
1139302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov    private static final String KEY_SHOW_JOIN_SUGGESTIONS = "showJoinSuggestions";
114f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
11515972398b530b91612418dc11d41c356d4f43739Daniel Lehmann    /**
11615972398b530b91612418dc11d41c356d4f43739Daniel Lehmann     * Modes that specify what the AsyncTask has to perform after saving
11715972398b530b91612418dc11d41c356d4f43739Daniel Lehmann     */
11815972398b530b91612418dc11d41c356d4f43739Daniel Lehmann    private interface SaveMode {
11915972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        /**
12015972398b530b91612418dc11d41c356d4f43739Daniel Lehmann         * Close the editor after saving
12115972398b530b91612418dc11d41c356d4f43739Daniel Lehmann         */
12215972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        public static final int CLOSE = 0;
12315972398b530b91612418dc11d41c356d4f43739Daniel Lehmann
12415972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        /**
12515972398b530b91612418dc11d41c356d4f43739Daniel Lehmann         * Reload the data so that the user can continue editing
12615972398b530b91612418dc11d41c356d4f43739Daniel Lehmann         */
12715972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        public static final int RELOAD = 1;
12815972398b530b91612418dc11d41c356d4f43739Daniel Lehmann
12915972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        /**
13015972398b530b91612418dc11d41c356d4f43739Daniel Lehmann         * Split the contact after saving
13115972398b530b91612418dc11d41c356d4f43739Daniel Lehmann         */
13215972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        public static final int SPLIT = 2;
13315972398b530b91612418dc11d41c356d4f43739Daniel Lehmann
13415972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        /**
13515972398b530b91612418dc11d41c356d4f43739Daniel Lehmann         * Join another contact after saving
13615972398b530b91612418dc11d41c356d4f43739Daniel Lehmann         */
13715972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        public static final int JOIN = 3;
13815972398b530b91612418dc11d41c356d4f43739Daniel Lehmann    }
139a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann
140825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann    private interface Status {
141825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        /**
142825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann         * The loader is fetching data
143825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann         */
144825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        public static final int LOADING = 0;
145825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann
146825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        /**
147825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann         * Not currently busy. We are waiting for the user to enter data
148825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann         */
149825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        public static final int EDITING = 1;
150825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann
151825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        /**
152825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann         * The data is currently being saved. This is used to prevent more auto-saves (they shouldn't
153825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann         * overlap)
154825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann         */
155825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        public static final int SAVING = 2;
156825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann
157825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        /**
158825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann         * Prevents any more savings (this is used if Save/Close or Revert was executed by the user)
159825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann         */
160825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        public static final int CLOSING = 3;
161825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann    }
162825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann
163a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    private static final int REQUEST_CODE_JOIN = 0;
164a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    private static final int REQUEST_CODE_CAMERA_WITH_DATA = 1;
165a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    private static final int REQUEST_CODE_PHOTO_PICKED_WITH_DATA = 2;
166f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
167c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann    private Bitmap mPhoto = null;
168f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private long mRawContactIdRequestingPhoto = -1;
169c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann    private long mRawContactIdRequestingPhotoAfterLoad = -1;
170cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
171f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private final EntityDeltaComparator mComparator = new EntityDeltaComparator();
172f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
173f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private static final int ICON_SIZE = 96;
174f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
175f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private static final File PHOTO_DIR = new File(
176f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            Environment.getExternalStorageDirectory() + "/DCIM/Camera");
177f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
178e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov    private Cursor mGroupMetaData;
179e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov
180f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    /**
181f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * A delay in milliseconds used for bringing aggregation suggestions to
182f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * the visible part of the screen. The reason this has to be done after
183f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * a delay is a race condition with the soft keyboard.  The keyboard
184f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * may expand to display its own autocomplete suggestions, which will
185f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * reduce the visible area of the screen.  We will yield to the keyboard
186f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * hoping that the delay is sufficient.  If not - part of the
187f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * suggestion will be hidden, which is not fatal.
188f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     */
189f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTION_SCROLL_DELAY = 200;
190f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
191f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private File mCurrentPhotoFile;
192cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
193cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    private Context mContext;
194f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private String mAction;
195b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann    private Uri mLookupUri;
196f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private String mMimeType;
197f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private Bundle mIntentExtras;
198cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    private Listener mListener;
199cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
200f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private String mQuerySelection;
201cc8bfc88f5c066b4e1b8d4a08eee52164decf24fDaniel Lehmann
202f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private long mContactIdForJoin;
203cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
204f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private LinearLayout mContent;
205ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann    private EntityDeltaList mState;
206cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
207f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private ViewIdGenerator mViewIdGenerator;
208cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
2099767e09ae37332d74b5558a3ec16ded20a9328dcDaniel Lehmann    private long mLoaderStartTime;
2109767e09ae37332d74b5558a3ec16ded20a9328dcDaniel Lehmann
211825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann    private int mStatus;
212825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann
213f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    private AggregationSuggestionEngine mAggregationSuggestionEngine;
2149302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov    private long mAggregationSuggestionsRawContactId;
2159302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov    private View mAggregationSuggestionView;
216f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
217cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    public ContactEditorFragment() {
218cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
219cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
220cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    @Override
221cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    public void onAttach(Activity activity) {
222cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        super.onAttach(activity);
223cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        mContext = activity;
224cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
225cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
226cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    @Override
227f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    public void onStop() {
228f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        super.onStop();
229f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        if (mAggregationSuggestionEngine != null) {
230f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            mAggregationSuggestionEngine.quit();
231f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        }
232825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        // If anything was left unsaved, save it now but keep the editor open.
233825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        if (!getActivity().isChangingConfigurations() && mStatus == Status.EDITING) {
234825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            save(false);
235825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        }
236f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    }
237f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
238f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    @Override
239cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
240cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        final View view = inflater.inflate(R.layout.contact_editor_fragment, container, false);
241cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
242f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        mContent = (LinearLayout) view.findViewById(R.id.editors);
243e8767a1136d2a2f9cb3e3ca5f44b2142baab9709Daniel Lehmann
2443648d2ea76a6649230d1fdd3dba92797ae7cfb26Daniel Lehmann        setHasOptionsMenu(true);
245cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
246825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        // If we are in an orientation change, we already have mState (it was loaded by onCreate)
247825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        if (mState != null) {
248825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            bindEditors();
249825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        }
250825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann
251cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        return view;
252cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
253cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
254222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann    @Override
255222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann    public void onActivityCreated(Bundle savedInstanceState) {
256222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann        super.onActivityCreated(savedInstanceState);
257222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann
258825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        Log.d(TAG, "onActivityCreated(" + savedInstanceState + ")");
259222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann
260825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        // Handle initial actions only when existing state missing
2614a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        final boolean hasIncomingState = savedInstanceState != null;
262825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann
263825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        if (!hasIncomingState) {
264825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            if (Intent.ACTION_EDIT.equals(mAction)) {
265825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                if (mListener != null) mListener.setTitleTo(R.string.editContact_title_edit);
266825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                getLoaderManager().initLoader(LOADER_DATA, null, mDataLoaderListener);
267825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            } else if (Intent.ACTION_INSERT.equals(mAction)) {
268825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                if (mListener != null) mListener.setTitleTo(R.string.editContact_title_insert);
269825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann
270e17b1e52e6895f8177b4192f34126000fcb3f983Daniel Lehmann                doAddAction(true);
271825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            } else throw new IllegalArgumentException("Unknown Action String " + mAction +
272825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                    ". Only support " + Intent.ACTION_EDIT + " or " + Intent.ACTION_INSERT);
273825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        }
274222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann    }
275222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann
276e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov    @Override
277e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov    public void onStart() {
278e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        getLoaderManager().initLoader(LOADER_GROUPS, null, mGroupLoaderListener);
279e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        super.onStart();
280e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov    }
281e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov
282b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann    public void load(String action, Uri lookupUri, String mimeType, Bundle intentExtras) {
283f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        mAction = action;
284b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann        mLookupUri = lookupUri;
285f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        mMimeType = mimeType;
286f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        mIntentExtras = intentExtras;
287f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
288f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
289f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    public void setListener(Listener value) {
290f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        mListener = value;
291cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
292cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
293cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    @Override
294f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    public void onCreate(Bundle savedState) {
295d364314c460152db7089b304beec81b3675893baDmitri Plotnikov        if (savedState != null) {
296d364314c460152db7089b304beec81b3675893baDmitri Plotnikov            // Restore mUri before calling super.onCreate so that onInitializeLoaders
297d364314c460152db7089b304beec81b3675893baDmitri Plotnikov            // would already have a uri and an action to work with
298b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann            mLookupUri = savedState.getParcelable(KEY_URI);
299d364314c460152db7089b304beec81b3675893baDmitri Plotnikov            mAction = savedState.getString(KEY_ACTION);
300d364314c460152db7089b304beec81b3675893baDmitri Plotnikov        }
301d364314c460152db7089b304beec81b3675893baDmitri Plotnikov
302f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        super.onCreate(savedState);
303f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
304f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (savedState == null) {
305f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // If savedState is non-null, onRestoreInstanceState() will restore the generator.
306f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            mViewIdGenerator = new ViewIdGenerator();
307f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        } else {
308825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            // Read state from savedState. No loading involved here
309ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann            mState = savedState.<EntityDeltaList> getParcelable(KEY_EDIT_STATE);
310f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            mRawContactIdRequestingPhoto = savedState.getLong(
311f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    KEY_RAW_CONTACT_ID_REQUESTING_PHOTO);
312f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            mViewIdGenerator = savedState.getParcelable(KEY_VIEW_ID_GENERATOR);
313f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            String fileName = savedState.getString(KEY_CURRENT_PHOTO_FILE);
314f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (fileName != null) {
315f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                mCurrentPhotoFile = new File(fileName);
316cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann            }
317f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            mQuerySelection = savedState.getString(KEY_QUERY_SELECTION);
318f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            mContactIdForJoin = savedState.getLong(KEY_CONTACT_ID_FOR_JOIN);
3199302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov            mAggregationSuggestionsRawContactId = savedState.getLong(KEY_SHOW_JOIN_SUGGESTIONS);
320825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            mStatus = Status.EDITING;
321cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        }
322cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
323cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
324bb11be3f5867453e7561cdc7bebe6a380eff6881Daniel Lehmann    public void setData(ContactLoader.Result data) {
325d3f1e3128b709d7d9a0229f1d1843f015f4977baDaniel Lehmann        // If we have already loaded data, we do not want to change it here to not confuse the user
326d3f1e3128b709d7d9a0229f1d1843f015f4977baDaniel Lehmann        if (mState != null) {
327d3f1e3128b709d7d9a0229f1d1843f015f4977baDaniel Lehmann            Log.v(TAG, "Ignoring background change. This will have to be rebased later");
328d3f1e3128b709d7d9a0229f1d1843f015f4977baDaniel Lehmann            return;
329d3f1e3128b709d7d9a0229f1d1843f015f4977baDaniel Lehmann        }
330d3f1e3128b709d7d9a0229f1d1843f015f4977baDaniel Lehmann
331825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        // Build Filter mQuerySelection
332825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        final ArrayList<Entity> entities = data.getEntities();
333825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        final StringBuilder sb = new StringBuilder(RawContacts._ID + " IN(");
334825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        final int count = entities.size();
335e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikov        for (int i = 0; i < count; i++) {
336e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikov            if (i > 0) {
337e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikov                sb.append(',');
338e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikov            }
339e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikov            sb.append(entities.get(i).getEntityValues().get(RawContacts._ID));
340e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikov        }
341e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikov        sb.append(")");
342e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikov        mQuerySelection = sb.toString();
343e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikov        mState = EntityDeltaList.fromIterator(entities.iterator());
344e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikov
345825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        // Merge in Extras from Intent
346916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (mIntentExtras != null && mIntentExtras.size() > 0) {
34735769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann            final AccountTypes sources = AccountTypes.getInstance(mContext);
348916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            for (EntityDelta state : mState) {
349916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
350916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                final BaseAccountType source = sources.getInflatedSource(accountType,
351916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        BaseAccountType.LEVEL_CONSTRAINTS);
352916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (!source.readOnly) {
353916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    // Apply extras to the first writable raw contact only
354916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    EntityModifier.parseExtras(mContext, source, state, mIntentExtras);
355916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    mIntentExtras = null;
356916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    break;
357916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
358916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
359bb11be3f5867453e7561cdc7bebe6a380eff6881Daniel Lehmann        }
360916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
361f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        bindEditors();
362f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
363cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
364e17b1e52e6895f8177b4192f34126000fcb3f983Daniel Lehmann    private void selectAccountAndCreateContact(boolean isNewContact) {
36535769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann        final ArrayList<Account> accounts = AccountTypes.getInstance(mContext).getAccounts(true);
366f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // No Accounts available.  Create a phone-local contact.
367f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (accounts.isEmpty()) {
368f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            createContact(null, isNewContact);
369f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            return;  // Don't show a dialog.
370f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
371cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
372f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // In the common case of a single account being writable, auto-select
373f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // it without showing a dialog.
374f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (accounts.size() == 1) {
375f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            createContact(accounts.get(0), isNewContact);
376f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            return;  // Don't show a dialog.
377f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
378f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
3794a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        final SelectAccountDialogFragment dialog = new SelectAccountDialogFragment(isNewContact);
3804a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        dialog.setTargetFragment(this, 0);
3814a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        dialog.show(getFragmentManager(), SelectAccountDialogFragment.TAG);
382cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
383cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
384cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    /**
385f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * @param account may be null to signal a device-local contact should
386f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     *     be created.
387f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * @param prefillFromIntent If this is set, the intent extras will be used to prefill the fields
388cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann     */
389f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private void createContact(Account account, boolean prefillFromIntent) {
39035769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann        final AccountTypes sources = AccountTypes.getInstance(mContext);
391f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        final ContentValues values = new ContentValues();
392f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (account != null) {
393f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            values.put(RawContacts.ACCOUNT_NAME, account.name);
394f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            values.put(RawContacts.ACCOUNT_TYPE, account.type);
395f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        } else {
396f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            values.putNull(RawContacts.ACCOUNT_NAME);
397f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            values.putNull(RawContacts.ACCOUNT_TYPE);
398cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        }
399cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
400f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // Parse any values from incoming intent
401f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        EntityDelta insert = new EntityDelta(ValuesDelta.fromAfter(values));
40235769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann        final BaseAccountType source = sources.getInflatedSource(
403f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                account != null ? account.type : null,
40435769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann                BaseAccountType.LEVEL_CONSTRAINTS);
405f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        EntityModifier.parseExtras(mContext, source, insert,
406f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                prefillFromIntent ? mIntentExtras : null);
407f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
408f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // Ensure we have some default fields
409f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        EntityModifier.ensureKindExists(insert, source, Phone.CONTENT_ITEM_TYPE);
410f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        EntityModifier.ensureKindExists(insert, source, Email.CONTENT_ITEM_TYPE);
411f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
412f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (mState == null) {
413f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Create state if none exists yet
414ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann            mState = EntityDeltaList.fromSingle(insert);
415f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        } else {
416f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Add contact onto end of existing state
417f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            mState.add(insert);
418cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        }
419f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
420f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        bindEditors();
421cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
422cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
423f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private void bindEditors() {
424f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // Sort the editors
425f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        Collections.sort(mState, mComparator);
426cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
427f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // Remove any existing editors and rebuild any visible
428f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        mContent.removeAllViews();
429cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
430f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
431f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                Context.LAYOUT_INFLATER_SERVICE);
43235769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann        final AccountTypes sources = AccountTypes.getInstance(mContext);
433f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        int size = mState.size();
434f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        for (int i = 0; i < size; i++) {
435f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // TODO ensure proper ordering of entities in the list
436f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            final EntityDelta entity = mState.get(i);
437f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            final ValuesDelta values = entity.getValues();
438f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (!values.isVisible()) continue;
439f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
440f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
44135769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann            final BaseAccountType source = sources.getInflatedSource(accountType,
44235769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann                    BaseAccountType.LEVEL_CONSTRAINTS);
443f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            final long rawContactId = values.getAsLong(RawContacts._ID);
444f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
44572af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann            final BaseRawContactEditorView editor;
446f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (!source.readOnly) {
44772af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann                editor = (BaseRawContactEditorView) inflater.inflate(R.layout.item_contact_editor,
448f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        mContent, false);
449f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            } else {
45072af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann                editor = (BaseRawContactEditorView) inflater.inflate(
451f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        R.layout.item_read_only_contact_editor, mContent, false);
452cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann            }
453f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            final PhotoEditorView photoEditor = editor.getPhotoEditor();
4544a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann            final boolean sourceReadOnly = source.readOnly;
4554a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann            photoEditor.setEditorListener(new EditorListener() {
4564a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                @Override
4574a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                public void onRequest(int request) {
4584a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                    if (!hasValidState()) return;
4594a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann
4604a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                    if (request == EditorListener.REQUEST_PICK_PHOTO) {
4614a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                        if (editor.hasSetPhoto()) {
4624a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                            // There is an existing photo, offer to remove, replace, or promote to
4634a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                            // primary
4644a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                            final ModifyPhotoDialogFragment fragment =
4654a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                                    new ModifyPhotoDialogFragment(sourceReadOnly, rawContactId);
4664a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                            fragment.setTargetFragment(ContactEditorFragment.this, 0);
4674a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                            fragment.show(getFragmentManager(), ModifyPhotoDialogFragment.TAG);
4684a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                        } else if (!sourceReadOnly) {
4694a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                            // No photo set and not read-only, try to set the photo
4704a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                            doPickPhotoAction(rawContactId);
4714a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                        }
4724a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                    }
4734a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                }
4744a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann
4754a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                @Override
4764a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                public void onDeleted(Editor removedEditor) {
4774a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                }
4784a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann            });
479cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
480f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            mContent.addView(editor);
481f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            editor.setState(entity, source, mViewIdGenerator);
482f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
48372af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann            if (editor instanceof RawContactEditorView) {
48472af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann                final RawContactEditorView rawContactEditor = (RawContactEditorView) editor;
48572af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann                final FieldEditorView nameEditor = rawContactEditor.getNameEditor();
486f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                nameEditor.setEditorListener(new EditorListener() {
487f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
488f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                    @Override
489f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                    public void onRequest(int request) {
49041f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                        onContactNameChange(request, rawContactEditor, nameEditor);
491f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                    }
492f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
493f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                    @Override
4944a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                    public void onDeleted(Editor removedEditor) {
495f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                    }
496f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                });
497f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
4989302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                if (rawContactId == mAggregationSuggestionsRawContactId) {
499f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                    acquireAggregationSuggestions(rawContactEditor);
500f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                }
501f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            }
502cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        }
503f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
504e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        bindGroupMetaData();
505e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov
506f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // Show editor now that we've loaded state
507f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        mContent.setVisibility(View.VISIBLE);
508a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann
509a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        // Refresh Action Bar as the visibility of the join command
510825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        // Activity can be null if we have been detached from the Activity
511825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        final Activity activity = getActivity();
512825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        if (activity != null) activity.invalidateOptionsMenu();
513cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
514cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
515e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov    private void bindGroupMetaData() {
516e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        if (mGroupMetaData == null) {
517e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov            return;
518e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        }
519e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov
520e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        int editorCount = mContent.getChildCount();
521e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        for (int i = 0; i < editorCount; i++) {
52272af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann            BaseRawContactEditorView editor = (BaseRawContactEditorView) mContent.getChildAt(i);
523e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov            editor.setGroupMetaData(mGroupMetaData);
524e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        }
525e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov    }
526e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov
527b85b83ee4adc8b72fe68499f669ca530d58e6815Daniel Lehmann    @Override
528eb4d1bf9868dfddd958d8f028e36a4d2da7a8731Dianne Hackborn    public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
529f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        inflater.inflate(R.menu.edit, menu);
530cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
531cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
532b85b83ee4adc8b72fe68499f669ca530d58e6815Daniel Lehmann    @Override
533eb4d1bf9868dfddd958d8f028e36a4d2da7a8731Dianne Hackborn    public void onPrepareOptionsMenu(Menu menu) {
534f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        menu.findItem(R.id.menu_split).setVisible(mState != null && mState.size() > 1);
535cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
536cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
537b85b83ee4adc8b72fe68499f669ca530d58e6815Daniel Lehmann    @Override
538cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    public boolean onOptionsItemSelected(MenuItem item) {
539cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        switch (item.getItemId()) {
540f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            case R.id.menu_done:
54115972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                return doSaveAction(SaveMode.CLOSE);
542f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            case R.id.menu_discard:
543f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return doRevertAction();
544328a7c9bba58e77b8a91f91d717307b32958640aDaniel Lehmann            case R.id.menu_add_raw_contact:
545e17b1e52e6895f8177b4192f34126000fcb3f983Daniel Lehmann                return doAddAction(false);
546f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            case R.id.menu_delete:
547f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return doDeleteAction();
548f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            case R.id.menu_split:
549f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return doSplitContactAction();
550f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            case R.id.menu_join:
551f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return doJoinContactAction();
552f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
553f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        return false;
554f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
555cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
556e17b1e52e6895f8177b4192f34126000fcb3f983Daniel Lehmann    private boolean doAddAction(boolean isNewContact) {
557f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // Load Accounts async so that we can present them
558e17b1e52e6895f8177b4192f34126000fcb3f983Daniel Lehmann        selectAccountAndCreateContact(isNewContact);
559cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
560f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        return true;
561f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
562cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
563f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
564f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Delete the entire contact currently being edited, which usually asks for
565f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * user confirmation before continuing.
566f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
567f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private boolean doDeleteAction() {
568f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (!hasValidState())
569f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            return false;
570cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
571b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann        // TODO: Make sure Insert turns into Edit if/once it is autosaved
572b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann        if (Intent.ACTION_INSERT.equals(mAction)) {
573b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann            if (mListener != null) mListener.onReverted();
574cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        } else {
575b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann            if (mListener != null) mListener.onDeleteRequested(mLookupUri);
576cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        }
577f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        return true;
578f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
579f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
580f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
581f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Pick a specific photo to be added under the currently selected tab.
582f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
583f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /* package */ boolean doPickPhotoAction(long rawContactId) {
584f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (!hasValidState()) return false;
585f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
586f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        mRawContactIdRequestingPhoto = rawContactId;
5874a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        final PickPhotoDialogFragment dialogFragment = new PickPhotoDialogFragment();
5884a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        dialogFragment.setTargetFragment(this, 0);
5894a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        dialogFragment.show(getFragmentManager(), PickPhotoDialogFragment.TAG);
590f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
591a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        return true;
592f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
593f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
594f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private boolean doSplitContactAction() {
595f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (!hasValidState()) return false;
596f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
597a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        final SplitContactConfirmationDialogFragment dialog =
5984a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                new SplitContactConfirmationDialogFragment();
5994a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        dialog.setTargetFragment(this, 0);
6004a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        dialog.show(getFragmentManager(), SplitContactConfirmationDialogFragment.TAG);
601f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        return true;
602f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
603f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
604f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private boolean doJoinContactAction() {
60515972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        return doSaveAction(SaveMode.JOIN);
606f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
607f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
608f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
609f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Constructs an intent for picking a photo from Gallery, cropping it and returning the bitmap.
610f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
611f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    public static Intent getPhotoPickIntent() {
612f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
613f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.setType("image/*");
614f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("crop", "true");
615f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("aspectX", 1);
616f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("aspectY", 1);
617f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("outputX", ICON_SIZE);
618f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("outputY", ICON_SIZE);
619f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("return-data", true);
620f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        return intent;
621f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
622f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
623f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
624f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Check if our internal {@link #mState} is valid, usually checked before
625f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * performing user actions.
626f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
627f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private boolean hasValidState() {
628f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        return mState != null && mState.size() > 0;
629f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
630f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
631f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
632f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Create a file name for the icon photo using current time.
633f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
634f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private String getPhotoFileName() {
635f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        Date date = new Date(System.currentTimeMillis());
636f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
637f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        return dateFormat.format(date) + ".jpg";
638f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
639f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
640f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
641f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Constructs an intent for capturing a photo and storing it in a temporary file.
642f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
643f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    public static Intent getTakePickIntent(File f) {
644f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);
645f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
646f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        return intent;
647f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
648f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
649f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
650f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Sends a newly acquired photo to Gallery for cropping
651f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
652f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    protected void doCropPhoto(File f) {
653f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        try {
654f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Add the image to the media store
655f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            MediaScannerConnection.scanFile(
656f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    mContext,
657f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    new String[] { f.getAbsolutePath() },
658f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    new String[] { null },
659f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    null);
660f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
661f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Launch gallery to crop the photo
662f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            final Intent intent = getCropImageIntent(Uri.fromFile(f));
663a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            startActivityForResult(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA);
664f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        } catch (Exception e) {
665f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            Log.e(TAG, "Cannot crop image", e);
666f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
667cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        }
668cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
669cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
670f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
671f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Constructs an intent for image cropping.
672f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
673f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    public static Intent getCropImageIntent(Uri photoUri) {
674f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        Intent intent = new Intent("com.android.camera.action.CROP");
675f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.setDataAndType(photoUri, "image/*");
676f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("crop", "true");
677f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("aspectX", 1);
678f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("aspectY", 1);
679f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("outputX", ICON_SIZE);
680f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("outputY", ICON_SIZE);
681f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra("return-data", true);
682f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        return intent;
683f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
684f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
685f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
686f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Saves or creates the contact based on the mode, and if successful
687f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * finishes the activity.
688f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
689f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private boolean doSaveAction(int saveMode) {
690f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (!hasValidState()) {
691f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            return false;
692f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
693f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
6948c6955ff3c05e1f3899110701c3cb5559a492f1eDmitri Plotnikov        // If we are about to close the editor - there is no need to refresh the data
6958c6955ff3c05e1f3899110701c3cb5559a492f1eDmitri Plotnikov        if (saveMode == SaveMode.CLOSE) {
6968c6955ff3c05e1f3899110701c3cb5559a492f1eDmitri Plotnikov            getLoaderManager().stopLoader(LOADER_DATA);
6978c6955ff3c05e1f3899110701c3cb5559a492f1eDmitri Plotnikov        }
6988c6955ff3c05e1f3899110701c3cb5559a492f1eDmitri Plotnikov
699825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        mStatus = Status.SAVING;
7006cb8fe23106bc05139798318f19b29c569140628Dmitri Plotnikov
7016cb8fe23106bc05139798318f19b29c569140628Dmitri Plotnikov        // Trim any empty fields, and RawContacts, before persisting
70235769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann        final AccountTypes sources = AccountTypes.getInstance(mContext);
7036cb8fe23106bc05139798318f19b29c569140628Dmitri Plotnikov        EntityModifier.trimEmpty(mState, sources);
7046cb8fe23106bc05139798318f19b29c569140628Dmitri Plotnikov
7056cb8fe23106bc05139798318f19b29c569140628Dmitri Plotnikov        if (mState.buildDiff().isEmpty()) {
7066cb8fe23106bc05139798318f19b29c569140628Dmitri Plotnikov            onSaveCompleted(true, saveMode, mLookupUri);
7076cb8fe23106bc05139798318f19b29c569140628Dmitri Plotnikov            return true;
7086cb8fe23106bc05139798318f19b29c569140628Dmitri Plotnikov        }
7096cb8fe23106bc05139798318f19b29c569140628Dmitri Plotnikov
710f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        final PersistTask task = new PersistTask(this, saveMode);
711f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        task.execute(mState);
712f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
713f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        return true;
714f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
715f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
716ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann    /**
717f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * Asynchronously saves the changes made by the user. This can be called even if nothing
718ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann     * has changed
719ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann     */
72015972398b530b91612418dc11d41c356d4f43739Daniel Lehmann    public void save(boolean closeAfterSave) {
72115972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        doSaveAction(closeAfterSave ? SaveMode.CLOSE : SaveMode.RELOAD);
722ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann    }
723ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann
724f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private boolean doRevertAction() {
725825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        // When this Fragment is closed we don't want it to auto-save
726825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        mStatus = Status.CLOSING;
72722fb2053fe2bfe98376bf1a0e564f23ae9cecdedDaniel Lehmann        if (mListener != null) mListener.onReverted();
728f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
729f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        return true;
730f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
731f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
732f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private void onSaveCompleted(boolean success, int saveMode, Uri contactLookupUri) {
73315972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        Log.d(TAG, "onSaveCompleted(" + success + ", " + saveMode + ", " + contactLookupUri);
734f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        switch (saveMode) {
73515972398b530b91612418dc11d41c356d4f43739Daniel Lehmann            case SaveMode.CLOSE:
736f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                final Intent resultIntent;
737f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                final int resultCode;
738f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                if (success && contactLookupUri != null) {
739b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann                    final String requestAuthority =
740b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann                            mLookupUri == null ? null : mLookupUri.getAuthority();
741f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
742f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    final String legacyAuthority = "contacts";
743f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
744f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    resultIntent = new Intent();
745f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    if (legacyAuthority.equals(requestAuthority)) {
746f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        // Build legacy Uri when requested by caller
747f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        final long contactId = ContentUris.parseId(Contacts.lookupContact(
748f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                                mContext.getContentResolver(), contactLookupUri));
749f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        final Uri legacyContentUri = Uri.parse("content://contacts/people");
750f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        final Uri legacyUri = ContentUris.withAppendedId(
751f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                                legacyContentUri, contactId);
752f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        resultIntent.setData(legacyUri);
753f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    } else {
754f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        // Otherwise pass back a lookup-style Uri
755f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        resultIntent.setData(contactLookupUri);
756f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    }
757f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
758f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    resultCode = Activity.RESULT_OK;
759f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                } else {
760f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    resultCode = Activity.RESULT_CANCELED;
761f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    resultIntent = null;
762f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                }
763825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                // It is already saved, so prevent that it is saved again
764825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                mStatus = Status.CLOSING;
76522fb2053fe2bfe98376bf1a0e564f23ae9cecdedDaniel Lehmann                if (mListener != null) mListener.onSaveFinished(resultCode, resultIntent);
766f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                break;
76715972398b530b91612418dc11d41c356d4f43739Daniel Lehmann            case SaveMode.RELOAD:
76815972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                if (success && contactLookupUri != null) {
76915972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                    // If this was in INSERT, we are changing into an EDIT now.
77015972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                    // If it already was an EDIT, we are changing to the new Uri now
77115972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                    mState = null;
77215972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                    load(Intent.ACTION_EDIT, contactLookupUri, mMimeType, null);
773825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                    mStatus = Status.LOADING;
77415972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                    getLoaderManager().restartLoader(LOADER_DATA, null, mDataLoaderListener);
77515972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                }
77615972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                break;
77715972398b530b91612418dc11d41c356d4f43739Daniel Lehmann            case SaveMode.SPLIT:
778a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann                if (mListener != null) {
7798c91dfb9ef1c7ac1a4b2feed0b9a8585d4873542Dmitri Plotnikov                    mListener.onContactSplit(contactLookupUri);
780a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann                } else {
781a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann                    Log.d(TAG, "No listener registered, can not call onSplitFinished");
782a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann                }
783825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                mStatus = Status.EDITING;
784f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                break;
785f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
78615972398b530b91612418dc11d41c356d4f43739Daniel Lehmann            case SaveMode.JOIN:
787f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                if (success) {
788f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    showJoinAggregateActivity(contactLookupUri);
789f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                }
790825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                mStatus = Status.EDITING;
791f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                break;
792f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
793f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
794f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
795f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
796f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Shows a list of aggregates that can be joined into the currently viewed aggregate.
797f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     *
798f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * @param contactLookupUri the fresh URI for the currently edited contact (after saving it)
799f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
800f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private void showJoinAggregateActivity(Uri contactLookupUri) {
801f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (contactLookupUri == null) {
802f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            return;
803f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
804f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
805f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        mContactIdForJoin = ContentUris.parseId(contactLookupUri);
806f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        final Intent intent = new Intent(JoinContactActivity.JOIN_CONTACT);
807f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        intent.putExtra(JoinContactActivity.EXTRA_TARGET_CONTACT_ID, mContactIdForJoin);
808a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        startActivityForResult(intent, REQUEST_CODE_JOIN);
809f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
810f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
811f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private interface JoinContactQuery {
812f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        String[] PROJECTION = {
813f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                RawContacts._ID,
814f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                RawContacts.CONTACT_ID,
815f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                RawContacts.NAME_VERIFIED,
816f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        };
817f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
818f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        String SELECTION = RawContacts.CONTACT_ID + "=? OR " + RawContacts.CONTACT_ID + "=?";
819f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
820f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        int _ID = 0;
821f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        int CONTACT_ID = 1;
822f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        int NAME_VERIFIED = 2;
823f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
824f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
825f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
826f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Performs aggregation with the contact selected by the user from suggestions or A-Z list.
827f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
828f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private void joinAggregate(final long contactId) {
829f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        final ContentResolver resolver = mContext.getContentResolver();
830f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
831f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // Load raw contact IDs for all raw contacts involved - currently edited and selected
832f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // in the join UIs
833f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        Cursor c = resolver.query(RawContacts.CONTENT_URI,
834f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                JoinContactQuery.PROJECTION,
835f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                JoinContactQuery.SELECTION,
836f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                new String[]{String.valueOf(contactId), String.valueOf(mContactIdForJoin)}, null);
837f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
838f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        long rawContactIds[];
839f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        long verifiedNameRawContactId = -1;
840f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        try {
841f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            rawContactIds = new long[c.getCount()];
842f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            for (int i = 0; i < rawContactIds.length; i++) {
843f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                c.moveToNext();
844f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                long rawContactId = c.getLong(JoinContactQuery._ID);
845f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                rawContactIds[i] = rawContactId;
846f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                if (c.getLong(JoinContactQuery.CONTACT_ID) == mContactIdForJoin) {
847f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    if (verifiedNameRawContactId == -1
848f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                            || c.getInt(JoinContactQuery.NAME_VERIFIED) != 0) {
849f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        verifiedNameRawContactId = rawContactId;
850f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    }
851f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                }
852b85b83ee4adc8b72fe68499f669ca530d58e6815Daniel Lehmann            }
853f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        } finally {
854f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            c.close();
855cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        }
856f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
857f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // For each pair of raw contacts, insert an aggregation exception
858f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
859f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        for (int i = 0; i < rawContactIds.length; i++) {
860f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            for (int j = 0; j < rawContactIds.length; j++) {
861f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                if (i != j) {
862f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]);
863f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                }
864f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
865cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        }
866f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
867f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // Mark the original contact as "name verified" to make sure that the contact
868f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // display name does not change as a result of the join
869f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        Builder builder = ContentProviderOperation.newUpdate(
870f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, verifiedNameRawContactId));
871f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        builder.withValue(RawContacts.NAME_VERIFIED, 1);
872f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        operations.add(builder.build());
873f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
8748c91dfb9ef1c7ac1a4b2feed0b9a8585d4873542Dmitri Plotnikov        boolean success = false;
875f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // Apply all aggregation exceptions as one batch
876f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        try {
877f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            resolver.applyBatch(ContactsContract.AUTHORITY, operations);
8788c91dfb9ef1c7ac1a4b2feed0b9a8585d4873542Dmitri Plotnikov            Toast.makeText(mContext, R.string.contactsJoinedMessage, Toast.LENGTH_LONG).show();
8798c91dfb9ef1c7ac1a4b2feed0b9a8585d4873542Dmitri Plotnikov            success = true;
880f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        } catch (RemoteException e) {
881f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            Log.e(TAG, "Failed to apply aggregation exception batch", e);
882f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
883f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        } catch (OperationApplicationException e) {
884f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            Log.e(TAG, "Failed to apply aggregation exception batch", e);
885f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
886cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        }
887a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann
8888c91dfb9ef1c7ac1a4b2feed0b9a8585d4873542Dmitri Plotnikov        if (success) {
8898c91dfb9ef1c7ac1a4b2feed0b9a8585d4873542Dmitri Plotnikov            onSaveCompleted(true, SaveMode.RELOAD, mLookupUri);
890a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        }
891f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
892f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
893f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
894f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Construct a {@link AggregationExceptions#TYPE_KEEP_TOGETHER} ContentProviderOperation.
895f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
896f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private void buildJoinContactDiff(ArrayList<ContentProviderOperation> operations,
897f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            long rawContactId1, long rawContactId2) {
898f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        Builder builder =
899f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
900f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
901f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
902f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
903f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        operations.add(builder.build());
904f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
905cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann
906cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    public static interface Listener {
907cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann        /**
90822fb2053fe2bfe98376bf1a0e564f23ae9cecdedDaniel Lehmann         * Contact was not found, so somehow close this fragment. This is raised after a contact
90922fb2053fe2bfe98376bf1a0e564f23ae9cecdedDaniel Lehmann         * is removed via Menu/Delete (unless it was a new contact)
910cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann         */
91122fb2053fe2bfe98376bf1a0e564f23ae9cecdedDaniel Lehmann        void onContactNotFound();
912f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
913f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        /**
9148c91dfb9ef1c7ac1a4b2feed0b9a8585d4873542Dmitri Plotnikov         * Contact was split, so we can close now.
915a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann         * @param newLookupUri The lookup uri of the new contact that should be shown to the user.
916a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann         * The editor tries best to chose the most natural contact here.
917f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann         */
9188c91dfb9ef1c7ac1a4b2feed0b9a8585d4873542Dmitri Plotnikov        void onContactSplit(Uri newLookupUri);
919f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
920f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        /**
921f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann         * User was presented with an account selection and couldn't decide.
922f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann         */
92322fb2053fe2bfe98376bf1a0e564f23ae9cecdedDaniel Lehmann        void onAccountSelectorAborted();
924f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
925f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        /**
926f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann         * User has tapped Revert, close the fragment now.
927f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann         */
92822fb2053fe2bfe98376bf1a0e564f23ae9cecdedDaniel Lehmann        void onReverted();
929f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
930f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        /**
931f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann         * Set the Title (e.g. of the Activity)
932f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann         */
933f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        void setTitleTo(int resourceId);
934f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
935f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        /**
93622fb2053fe2bfe98376bf1a0e564f23ae9cecdedDaniel Lehmann         * Contact was saved and the Fragment can now be closed safely.
937f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann         */
93822fb2053fe2bfe98376bf1a0e564f23ae9cecdedDaniel Lehmann        void onSaveFinished(int resultCode, Intent resultIntent);
939b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann
940b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann        /**
941b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann         * User decided to delete the contact.
942b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann         */
943a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        void onDeleteRequested(Uri lookupUri);
944f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
945f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
946f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    private class EntityDeltaComparator implements Comparator<EntityDelta> {
947f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        /**
948f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann         * Compare EntityDeltas for sorting the stack of editors.
949f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann         */
9504a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        @Override
951f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        public int compare(EntityDelta one, EntityDelta two) {
952f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Check direct equality
953f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (one.equals(two)) {
954f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return 0;
955f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
956f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
95735769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann            final AccountTypes sources = AccountTypes.getInstance(mContext);
958f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            String accountType = one.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
95935769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann            final BaseAccountType oneSource = sources.getInflatedSource(accountType,
96035769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann                    BaseAccountType.LEVEL_SUMMARY);
961f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            accountType = two.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
96235769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann            final BaseAccountType twoSource = sources.getInflatedSource(accountType,
96335769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann                    BaseAccountType.LEVEL_SUMMARY);
964f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
965f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Check read-only
966f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (oneSource.readOnly && !twoSource.readOnly) {
967f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return 1;
968f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            } else if (twoSource.readOnly && !oneSource.readOnly) {
969f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return -1;
970f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
971f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
972f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Check account type
973f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            boolean skipAccountTypeCheck = false;
97435769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann            boolean oneIsGoogle = oneSource instanceof GoogleAccountType;
97535769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann            boolean twoIsGoogle = twoSource instanceof GoogleAccountType;
976f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (oneIsGoogle && !twoIsGoogle) {
977f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return -1;
978f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            } else if (twoIsGoogle && !oneIsGoogle) {
979f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return 1;
980f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            } else if (oneIsGoogle && twoIsGoogle){
981f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                skipAccountTypeCheck = true;
982f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
983f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
984f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            int value;
985f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (!skipAccountTypeCheck) {
9862776c7fe8117773fdf773396f61ed047238470f9Jean-Baptiste Queru                if (oneSource.accountType == null) {
9872776c7fe8117773fdf773396f61ed047238470f9Jean-Baptiste Queru                    return 1;
9882776c7fe8117773fdf773396f61ed047238470f9Jean-Baptiste Queru                }
989f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                value = oneSource.accountType.compareTo(twoSource.accountType);
990f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                if (value != 0) {
991f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    return value;
992f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                }
993f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
994f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
995f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Check account name
996f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            ValuesDelta oneValues = one.getValues();
997f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            String oneAccount = oneValues.getAsString(RawContacts.ACCOUNT_NAME);
998f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (oneAccount == null) oneAccount = "";
999f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            ValuesDelta twoValues = two.getValues();
1000f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            String twoAccount = twoValues.getAsString(RawContacts.ACCOUNT_NAME);
1001f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (twoAccount == null) twoAccount = "";
1002f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            value = oneAccount.compareTo(twoAccount);
1003f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (value != 0) {
1004f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return value;
1005f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
1006f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1007f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Both are in the same account, fall back to contact ID
1008f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            Long oneId = oneValues.getAsLong(RawContacts._ID);
1009f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            Long twoId = twoValues.getAsLong(RawContacts._ID);
1010f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (oneId == null) {
1011f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return -1;
1012f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            } else if (twoId == null) {
1013f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return 1;
1014f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
1015f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1016f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            return (int)(oneId - twoId);
1017f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
1018f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
1019f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1020f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
1021f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * Returns the contact ID for the currently edited contact or 0 if the contact is new.
1022f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     */
1023f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    protected long getContactId() {
1024f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        for (EntityDelta rawContact : mState) {
1025f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            Long contactId = rawContact.getValues().getAsLong(RawContacts.CONTACT_ID);
1026f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            if (contactId != null) {
1027f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                return contactId;
1028f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            }
1029f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        }
1030f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        return 0;
1031f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    }
1032f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
103341f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
103472af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann    private void onContactNameChange(int request, final RawContactEditorView rawContactEditor,
103572af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann            FieldEditorView nameEditor) {
103641f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
103741f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        switch (request) {
103841f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov            case EditorListener.EDITOR_FORM_CHANGED:
10390b85aed7ae694341ea226c88bf10c0e11ea990ceDmitri Plotnikov                if (nameEditor.hasShortAndLongForms()) {
10400b85aed7ae694341ea226c88bf10c0e11ea990ceDmitri Plotnikov                    if (nameEditor.areOptionalFieldsVisible()) {
10410b85aed7ae694341ea226c88bf10c0e11ea990ceDmitri Plotnikov                        switchFromFullNameToStructuredName(nameEditor);
10420b85aed7ae694341ea226c88bf10c0e11ea990ceDmitri Plotnikov                    } else {
10430b85aed7ae694341ea226c88bf10c0e11ea990ceDmitri Plotnikov                        switchFromStructuredNameToFullName(nameEditor);
10440b85aed7ae694341ea226c88bf10c0e11ea990ceDmitri Plotnikov                    }
104541f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                }
104641f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                break;
104741f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
104841f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov            case EditorListener.FIELD_CHANGED:
1049dbf3e60e3b3cfbd7114d319d6c9ea0cdab5997afDmitri Plotnikov                if (nameEditor.hasShortAndLongForms()) {
1050dbf3e60e3b3cfbd7114d319d6c9ea0cdab5997afDmitri Plotnikov                    if (nameEditor.areOptionalFieldsVisible()) {
1051dbf3e60e3b3cfbd7114d319d6c9ea0cdab5997afDmitri Plotnikov                        eraseFullName(nameEditor.getValues());
1052dbf3e60e3b3cfbd7114d319d6c9ea0cdab5997afDmitri Plotnikov                    } else {
1053dbf3e60e3b3cfbd7114d319d6c9ea0cdab5997afDmitri Plotnikov                        eraseStructuredName(nameEditor.getValues());
1054dbf3e60e3b3cfbd7114d319d6c9ea0cdab5997afDmitri Plotnikov                    }
105541f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                }
105641f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                acquireAggregationSuggestions(rawContactEditor);
105741f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                break;
105841f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        }
105941f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov    }
106041f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
106172af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann    private void switchFromFullNameToStructuredName(FieldEditorView nameEditor) {
106241f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        ValuesDelta values = nameEditor.getValues();
106341f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
106441f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
106541f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        if (displayName == null) {
106641f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov            displayName = "";
106741f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        }
106841f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
106941f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name")
107041f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                .appendQueryParameter(StructuredName.DISPLAY_NAME, displayName).build();
107141f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        Cursor cursor = getActivity().getContentResolver().query(uri, new String[]{
107241f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                StructuredName.PREFIX,
107341f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                StructuredName.GIVEN_NAME,
107441f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
107541f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                StructuredName.FAMILY_NAME,
107641f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                StructuredName.SUFFIX,
107741f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        }, null, null, null);
107841f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
107941f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        try {
108041f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov            if (cursor.moveToFirst()) {
108141f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                eraseFullName(values);
108241f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                values.put(StructuredName.PREFIX, cursor.getString(0));
108341f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                values.put(StructuredName.GIVEN_NAME, cursor.getString(1));
108441f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                values.put(StructuredName.MIDDLE_NAME, cursor.getString(2));
108541f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                values.put(StructuredName.FAMILY_NAME, cursor.getString(3));
108641f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                values.put(StructuredName.SUFFIX, cursor.getString(4));
108741f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov            }
108841f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        } finally {
108941f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov            cursor.close();
109041f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        }
109141f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov    }
109241f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
109372af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann    private void switchFromStructuredNameToFullName(FieldEditorView nameEditor) {
109441f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        ValuesDelta values = nameEditor.getValues();
109541f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
109641f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        Uri.Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath(
109741f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                "complete_name");
109841f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        appendQueryParameter(builder, values, StructuredName.PREFIX);
109941f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        appendQueryParameter(builder, values, StructuredName.GIVEN_NAME);
110041f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        appendQueryParameter(builder, values, StructuredName.MIDDLE_NAME);
110141f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        appendQueryParameter(builder, values, StructuredName.FAMILY_NAME);
110241f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        appendQueryParameter(builder, values, StructuredName.SUFFIX);
110341f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        Uri uri = builder.build();
110441f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        Cursor cursor = getActivity().getContentResolver().query(uri, new String[]{
110541f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
110641f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        }, null, null, null);
110741f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
110841f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        try {
110941f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov            if (cursor.moveToFirst()) {
111041f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                eraseStructuredName(values);
111141f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov                values.put(StructuredName.DISPLAY_NAME, cursor.getString(0));
111241f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov            }
111341f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        } finally {
111441f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov            cursor.close();
111541f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        }
111641f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov    }
111741f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
111841f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov    private void eraseFullName(ValuesDelta values) {
111941f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        values.putNull(StructuredName.DISPLAY_NAME);
112041f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov    }
112141f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
112241f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov    private void eraseStructuredName(ValuesDelta values) {
112341f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        values.putNull(StructuredName.PREFIX);
112441f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        values.putNull(StructuredName.GIVEN_NAME);
112541f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        values.putNull(StructuredName.MIDDLE_NAME);
112641f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        values.putNull(StructuredName.FAMILY_NAME);
112741f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        values.putNull(StructuredName.SUFFIX);
112841f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov    }
112941f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
113041f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov    private void appendQueryParameter(Uri.Builder builder, ValuesDelta values, String field) {
113141f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        String value = values.getAsString(field);
113241f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        if (!TextUtils.isEmpty(value)) {
113341f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov            builder.appendQueryParameter(field, value);
113441f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov        }
113541f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov    }
113641f026d52e0277e21236787b10f76129a8bc6fa4Dmitri Plotnikov
1137f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    /**
1138f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * Triggers an asynchronous search for aggregation suggestions.
1139f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     */
114072af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann    public void acquireAggregationSuggestions(RawContactEditorView rawContactEditor) {
11419302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        long rawContactId = rawContactEditor.getRawContactId();
11429302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        if (mAggregationSuggestionsRawContactId != rawContactId
11439302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                && mAggregationSuggestionView != null) {
11449302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov            mAggregationSuggestionView.setVisibility(View.GONE);
11459302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov            mAggregationSuggestionView = null;
11469302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov            mAggregationSuggestionEngine.reset();
11479302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        }
11489302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov
11499302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        mAggregationSuggestionsRawContactId = rawContactId;
11509302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov
1151f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        if (mAggregationSuggestionEngine == null) {
1152f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            mAggregationSuggestionEngine = new AggregationSuggestionEngine(getActivity());
1153f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            mAggregationSuggestionEngine.setListener(this);
1154f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            mAggregationSuggestionEngine.start();
1155f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        }
1156f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
1157916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        mAggregationSuggestionEngine.setContactId(getContactId());
1158916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
115972af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann        FieldEditorView nameEditor = rawContactEditor.getNameEditor();
1160f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        mAggregationSuggestionEngine.onNameChange(nameEditor.getValues());
1161f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    }
1162f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
1163f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    @Override
1164f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    public void onAggregationSuggestionChange() {
11659302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        View rawContactView = getContactEditorView(mAggregationSuggestionsRawContactId);
11669302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        if (rawContactView == null) {
11679302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov            return;
11689302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        }
1169f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
11709302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        ViewStub stub = (ViewStub)rawContactView.findViewById(R.id.aggregation_suggestion_stub);
1171f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        if (stub != null) {
1172f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            stub.inflate();
1173f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        }
1174f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
11759302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        // Only request the view on screen when it is first displayed
11769302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        boolean requestOnScreen = mAggregationSuggestionView == null;
11779302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        mAggregationSuggestionView = rawContactView.findViewById(R.id.aggregation_suggestion);
1178f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
1179f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        int count = mAggregationSuggestionEngine.getSuggestedContactCount();
1180f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        if (count == 0) {
11819302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov            mAggregationSuggestionView.setVisibility(View.GONE);
1182f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            return;
1183f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        }
1184f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
11859302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        List<Suggestion> suggestions = mAggregationSuggestionEngine.getSuggestions();
11869302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov
11879302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        TextView title = (TextView) mAggregationSuggestionView.findViewById(
11889302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                R.id.aggregation_suggestion_title);
11899302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        title.setText(getActivity().getResources().getQuantityString(
11909302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                R.plurals.aggregation_suggestion_title, count));
1191f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
11929302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        LinearLayout itemList = (LinearLayout) mAggregationSuggestionView.findViewById(
11939302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                R.id.aggregation_suggestions);
1194f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        itemList.removeAllViews();
1195f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
1196f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        LayoutInflater inflater = getActivity().getLayoutInflater();
1197f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
1198f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        for (Suggestion suggestion : suggestions) {
1199f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            AggregationSuggestionView suggestionView =
1200f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                    (AggregationSuggestionView) inflater.inflate(
1201f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                            R.layout.aggregation_suggestions_item, null);
1202f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            suggestionView.setLayoutParams(
1203f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                    new LinearLayout.LayoutParams(
1204f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                            LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
1205c0130cc28edccb21f6698066d90ce2120096eff2Dmitri Plotnikov            suggestionView.setNewContact(mState.size() == 1 && mState.get(0).isContactInsert());
1206f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            suggestionView.setListener(new AggregationSuggestionView.Listener() {
1207f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
1208f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                @Override
1209f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                public void onJoinAction(long contactId, List<Long> rawContactIds) {
1210f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                    mState.setJoinWithRawContacts(rawContactIds);
1211f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                    // If we are in the edit mode (indicated by a non-zero contact ID),
1212f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                    // join the suggested contact, save all changes, and stay in the editor.
12139302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                    doSaveAction(SaveMode.RELOAD);
1214f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov                }
1215c0130cc28edccb21f6698066d90ce2120096eff2Dmitri Plotnikov
1216c0130cc28edccb21f6698066d90ce2120096eff2Dmitri Plotnikov                @Override
1217c0130cc28edccb21f6698066d90ce2120096eff2Dmitri Plotnikov                public void onEditAction(Uri contactLookupUri) {
12186a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov                    // Save all the data that has been entered so far
12196a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov                    ArrayList<ContentValues> values = mState.get(0).getContentValues();
12206a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov
12216a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov                    // Abandon the currently inserted contact
1222c0130cc28edccb21f6698066d90ce2120096eff2Dmitri Plotnikov                    mState = null;
12236a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov
12246a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov                    // Load the suggested one
12256a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov                    Bundle extras = null;
12266a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov                    if (values.size() != 0) {
12276a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov                        extras = new Bundle();
1228916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        extras.putParcelableArrayList(Insert.DATA, values);
12296a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov                    }
1230916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    load(Intent.ACTION_EDIT, contactLookupUri, Contacts.CONTENT_TYPE, extras);
1231916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    mStatus = Status.LOADING;
1232916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    getLoaderManager().restartLoader(LOADER_DATA, null, mDataLoaderListener);
1233c0130cc28edccb21f6698066d90ce2120096eff2Dmitri Plotnikov                }
1234f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            });
12359302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov            suggestionView.bindSuggestion(suggestion);
1236f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            itemList.addView(suggestionView);
1237f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        }
12389302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov
12399302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        mAggregationSuggestionView.setVisibility(View.VISIBLE);
12409302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov
12419302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        if (requestOnScreen) {
12429302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov            mContent.postDelayed(new Runnable() {
12439302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov
12449302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                @Override
12459302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                public void run() {
12469302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                    requestAggregationSuggestionOnScreen(mAggregationSuggestionView);
12479302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                }
12489302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov            }, AGGREGATION_SUGGESTION_SCROLL_DELAY);
12499302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        }
1250f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    }
1251f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
1252f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    /**
1253f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * Scrolls the editor if necessary to reveal the aggregation suggestion that is
1254f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * shown below the name editor. Makes sure that the currently focused field
1255f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * remains visible.
1256f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     */
1257f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    private void requestAggregationSuggestionOnScreen(final View view) {
1258f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        Rect rect = getRelativeBounds(mContent, view);
1259f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        View focused = mContent.findFocus();
1260f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        if (focused != null) {
1261f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            rect.union(getRelativeBounds(mContent, focused));
1262f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        }
1263f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        mContent.requestRectangleOnScreen(rect);
1264f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    }
1265f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
1266f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    /**
1267f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     * Computes bounds of the supplied view relative to its ascendant.
1268f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov     */
1269f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    private Rect getRelativeBounds(View ascendant, View view) {
1270f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        Rect rect = new Rect();
1271f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        rect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
1272f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
1273f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        View parent = (View) view.getParent();
1274f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        while (parent != ascendant) {
1275f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            rect.offset(parent.getLeft(), parent.getTop());
1276f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov            parent = (View) parent.getParent();
1277f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        }
1278f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov        return rect;
1279f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov    }
1280f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov
1281f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    // TODO: There has to be a nicer way than this WeakAsyncTask...? Maybe call a service?
1282f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    /**
1283f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * Background task for persisting edited contact data, using the changes
1284f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * defined by a set of {@link EntityDelta}. This task starts
1285f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * {@link EmptyService} to make sure the background thread can finish
1286f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     * persisting in cases where the system wants to reclaim our process.
1287f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann     */
1288f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    public static class PersistTask extends
1289ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann            WeakAsyncTask<EntityDeltaList, Void, Integer, ContactEditorFragment> {
1290f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        private static final int PERSIST_TRIES = 3;
1291f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1292f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        private static final int RESULT_UNCHANGED = 0;
1293f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        private static final int RESULT_SUCCESS = 1;
1294f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        private static final int RESULT_FAILURE = 2;
1295f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1296f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        private final Context mContext;
1297f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1298825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann        private WeakReference<ProgressDialog> mProgress;
1299825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann
1300f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        private int mSaveMode;
1301f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        private Uri mContactLookupUri = null;
1302f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1303f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        public PersistTask(ContactEditorFragment target, int saveMode) {
1304f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            super(target);
1305f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            mSaveMode = saveMode;
1306f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            mContext = target.mContext;
1307f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
1308f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1309f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        /** {@inheritDoc} */
1310f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        @Override
1311f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        protected void onPreExecute(ContactEditorFragment target) {
1312825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            mProgress = new WeakReference<ProgressDialog>(ProgressDialog.show(target.getActivity(),
1313825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                    null, target.getActivity().getText(R.string.savingContact)));
1314825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann
1315f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Before starting this task, start an empty service to protect our
1316f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // process from being reclaimed by the system.
1317f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            mContext.startService(new Intent(mContext, EmptyService.class));
1318f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
1319f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1320f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        /** {@inheritDoc} */
1321f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        @Override
1322ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann        protected Integer doInBackground(ContactEditorFragment target, EntityDeltaList... params) {
1323f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            final ContentResolver resolver = mContext.getContentResolver();
1324f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1325ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann            EntityDeltaList state = params[0];
1326f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1327f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Attempt to persist changes
1328f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            int tries = 0;
1329f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            Integer result = RESULT_FAILURE;
1330f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            while (tries++ < PERSIST_TRIES) {
1331f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                try {
1332f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    // Build operations and try applying
1333f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    final ArrayList<ContentProviderOperation> diff = state.buildDiff();
1334f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    ContentProviderResult[] results = null;
1335f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    if (!diff.isEmpty()) {
133615972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                        results = resolver.applyBatch(ContactsContract.AUTHORITY, diff);
1337f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    }
1338f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1339f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    final long rawContactId = getRawContactId(state, diff, results);
1340f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    if (rawContactId != -1) {
1341f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        final Uri rawContactUri = ContentUris.withAppendedId(
1342f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                                RawContacts.CONTENT_URI, rawContactId);
1343f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1344f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        // convert the raw contact URI to a contact URI
1345f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        mContactLookupUri = RawContacts.getContactLookupUri(resolver,
1346f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                                rawContactUri);
134715972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                        Log.d(TAG, "Looked up RawContact Uri " + rawContactUri +
134815972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                                " into ContactLookupUri " + mContactLookupUri);
134915972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                    } else {
135015972398b530b91612418dc11d41c356d4f43739Daniel Lehmann                        Log.w(TAG, "Could not determine RawContact ID after save");
1351f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    }
1352f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    result = (diff.size() > 0) ? RESULT_SUCCESS : RESULT_UNCHANGED;
1353f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    break;
1354f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1355f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                } catch (RemoteException e) {
1356f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    // Something went wrong, bail without success
1357f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    Log.e(TAG, "Problem persisting user edits", e);
1358f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    break;
1359f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1360f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                } catch (OperationApplicationException e) {
1361f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    // Version consistency failed, re-parent change and try again
1362f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
1363ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann                    final EntityDeltaList newState = EntityDeltaList.fromQuery(resolver,
1364e579a175bdc18153014da00d989cf0a5343d25a9Dmitri Plotnikov                            target.mQuerySelection, null, null);
1365ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann                    state = EntityDeltaList.mergeAfter(newState, state);
1366f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                }
1367f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
1368f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1369f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            return result;
1370f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
1371f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1372ca98444277723c60be83062cf5a25ec7ca1fdb34Daniel Lehmann        private long getRawContactId(EntityDeltaList state,
1373f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                final ArrayList<ContentProviderOperation> diff,
1374f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                final ContentProviderResult[] results) {
1375f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            long rawContactId = state.findRawContactId();
1376f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            if (rawContactId != -1) {
1377f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                return rawContactId;
1378f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
1379f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
138015972398b530b91612418dc11d41c356d4f43739Daniel Lehmann
1381f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // we gotta do some searching for the id
1382f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            final int diffSize = diff.size();
1383f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            for (int i = 0; i < diffSize; i++) {
1384f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                ContentProviderOperation operation = diff.get(i);
1385f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                if (operation.getType() == ContentProviderOperation.TYPE_INSERT
1386f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                        && operation.getUri().getEncodedPath().contains(
1387f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                                RawContacts.CONTENT_URI.getEncodedPath())) {
1388f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    return ContentUris.parseId(results[i].uri);
1389f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                }
1390f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
1391f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            return -1;
1392f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
1393f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1394f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        /** {@inheritDoc} */
1395f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        @Override
1396f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        protected void onPostExecute(ContactEditorFragment target, Integer result) {
1397a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            Log.d(TAG, "onPostExecute(something," + result + "). mSaveMode=" + mSaveMode);
139815972398b530b91612418dc11d41c356d4f43739Daniel Lehmann            if (result == RESULT_SUCCESS && mSaveMode != SaveMode.JOIN) {
1399f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                Toast.makeText(mContext, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
1400f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            } else if (result == RESULT_FAILURE) {
1401f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
1402f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
1403f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1404825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            final ProgressDialog progress = mProgress.get();
1405825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            if (progress != null && progress.isShowing()) {
1406825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                try {
1407825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                    progress.dismiss();
1408825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                } catch (Exception e) {
1409825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                    // this can happen if our view has already been closed. this can safely be
1410825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                    // ignored
1411825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann                }
1412825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            }
1413825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann
1414f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Stop the service that was protecting us
1415f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            mContext.stopService(new Intent(mContext, EmptyService.class));
1416f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1417f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            target.onSaveCompleted(result != RESULT_FAILURE, mSaveMode, mContactLookupUri);
1418f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
1419f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
1420f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1421f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    @Override
1422f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    public void onSaveInstanceState(Bundle outState) {
1423b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann        outState.putParcelable(KEY_URI, mLookupUri);
1424d364314c460152db7089b304beec81b3675893baDmitri Plotnikov        outState.putString(KEY_ACTION, mAction);
1425d364314c460152db7089b304beec81b3675893baDmitri Plotnikov
1426f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (hasValidState()) {
1427f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            // Store entities with modifications
1428f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            outState.putParcelable(KEY_EDIT_STATE, mState);
1429f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
1430f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1431f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        outState.putLong(KEY_RAW_CONTACT_ID_REQUESTING_PHOTO, mRawContactIdRequestingPhoto);
1432f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        outState.putParcelable(KEY_VIEW_ID_GENERATOR, mViewIdGenerator);
1433f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (mCurrentPhotoFile != null) {
1434f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            outState.putString(KEY_CURRENT_PHOTO_FILE, mCurrentPhotoFile.toString());
1435f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
1436f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        outState.putString(KEY_QUERY_SELECTION, mQuerySelection);
1437f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        outState.putLong(KEY_CONTACT_ID_FOR_JOIN, mContactIdForJoin);
14389302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        outState.putLong(KEY_SHOW_JOIN_SUGGESTIONS, mAggregationSuggestionsRawContactId);
1439f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        super.onSaveInstanceState(outState);
1440f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    }
1441f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1442d364314c460152db7089b304beec81b3675893baDmitri Plotnikov    @Override
1443f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann    public void onActivityResult(int requestCode, int resultCode, Intent data) {
1444f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        // Ignore failed requests
1445f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        if (resultCode != Activity.RESULT_OK) return;
1446f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        switch (requestCode) {
1447a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            case REQUEST_CODE_PHOTO_PICKED_WITH_DATA: {
1448c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann                // As we are coming back to this view, the editor will be reloaded automatically,
1449c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann                // which will cause the photo that is set here to disappear. To prevent this,
14504a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                // we remember to set a flag which is interpreted after loading.
14514a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                // This photo is set here already to reduce flickering.
1452c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann                mPhoto = data.getParcelableExtra("data");
1453c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann                setPhoto(mRawContactIdRequestingPhoto, mPhoto);
1454c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann                mRawContactIdRequestingPhotoAfterLoad = mRawContactIdRequestingPhoto;
1455c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann                mRawContactIdRequestingPhoto = -1;
1456f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann
1457f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                break;
1458f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
1459a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            case REQUEST_CODE_CAMERA_WITH_DATA: {
1460f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                doCropPhoto(mCurrentPhotoFile);
1461f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                break;
1462f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
1463a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            case REQUEST_CODE_JOIN: {
1464f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                if (data != null) {
1465f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    final long contactId = ContentUris.parseId(data.getData());
1466f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                    joinAggregate(contactId);
1467f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann                }
14684a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                break;
1469f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann            }
1470f319cf8549738feca5965a598af9a310869e1f3dDaniel Lehmann        }
1471cdef2b60aa87b1178033fdc964a3402386c03d7cDaniel Lehmann    }
14729cd1708f333eda622dc6a4fed2a0df9058193a9dDmitri Plotnikov
1473c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann    /**
1474c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann     * Sets the photo stored in mPhoto and writes it to the RawContact with the given id
1475c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann     */
1476c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann    private void setPhoto(long rawContact, Bitmap photo) {
147772af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann        BaseRawContactEditorView requestingEditor = getContactEditorView(rawContact);
14789302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        if (requestingEditor != null) {
14799302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov            requestingEditor.setPhotoBitmap(photo);
14809302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        } else {
14819302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov            Log.w(TAG, "The contact that requested the photo is no longer present.");
14829302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        }
14839302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov    }
14849302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov
14859302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov    /**
14869302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov     * Finds raw contact editor view for the given rawContactId.
14879302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov     */
148872af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann    public BaseRawContactEditorView getContactEditorView(long rawContactId) {
1489c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann        for (int i = 0; i < mContent.getChildCount(); i++) {
1490c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann            final View childView = mContent.getChildAt(i);
149172af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann            if (childView instanceof BaseRawContactEditorView) {
149272af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann                final BaseRawContactEditorView editor = (BaseRawContactEditorView) childView;
14939302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                if (editor.getRawContactId() == rawContactId) {
14949302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov                    return editor;
1495c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann                }
1496c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann            }
1497c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann        }
14989302ba7bc47d995c88eba7604aeea00a7e41779fDmitri Plotnikov        return null;
1499c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann    }
1500c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann
1501b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann    public Uri getLookupUri() {
1502b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann        return mLookupUri;
15039cd1708f333eda622dc6a4fed2a0df9058193a9dDmitri Plotnikov    }
1504222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann
1505222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann    /**
1506222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann     * The listener for the data loader
1507222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann     */
1508222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann    private final LoaderManager.LoaderCallbacks<ContactLoader.Result> mDataLoaderListener =
1509222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann            new LoaderCallbacks<ContactLoader.Result>() {
1510222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann        @Override
1511222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann        public Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
1512222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann            mLoaderStartTime = SystemClock.elapsedRealtime();
1513b846fc117b2c4c0c3ed5eb3e9ce32591986a0253Daniel Lehmann            return new ContactLoader(mContext, mLookupUri);
1514222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann        }
1515222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann
1516222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann        @Override
1517222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann        public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) {
1518222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann            final long loaderCurrentTime = SystemClock.elapsedRealtime();
1519222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann            Log.v(TAG, "Time needed for loading: " + (loaderCurrentTime-mLoaderStartTime));
15205da9363bc4f2e04dc388579e1df979a5843e25e6Dmitri Plotnikov            if (data == ContactLoader.Result.NOT_FOUND || data == ContactLoader.Result.ERROR) {
1521222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann                // Item has been deleted
1522222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann                Log.i(TAG, "No contact found. Closing activity");
152322fb2053fe2bfe98376bf1a0e564f23ae9cecdedDaniel Lehmann                if (mListener != null) mListener.onContactNotFound();
1524222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann                return;
1525222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann            }
1526222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann
1527825cb62b7df3cfe208651d488278e1b57c863884Daniel Lehmann            mStatus = Status.EDITING;
1528222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann            final long setDataStartTime = SystemClock.elapsedRealtime();
1529222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann            setData(data);
1530222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann            final long setDataEndTime = SystemClock.elapsedRealtime();
1531c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann
1532c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann            // If we are coming back from the photo trimmer, this will be set.
1533c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann            if (mRawContactIdRequestingPhotoAfterLoad != -1) {
1534c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann                setPhoto(mRawContactIdRequestingPhotoAfterLoad, mPhoto);
1535c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann                mRawContactIdRequestingPhotoAfterLoad = -1;
1536c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann                mPhoto = null;
1537c1406450bb2a1a214ff35e99a173ee1a28b926faDaniel Lehmann            }
1538222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann            Log.v(TAG, "Time needed for setting UI: " + (setDataEndTime-setDataStartTime));
1539222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann        }
1540222744ea3cd7233677f0ef88804eb3e2db710e43Daniel Lehmann    };
1541a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann
1542e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov    /**
1543e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov     * The listener for the group meta data loader
1544e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov     */
1545e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov    private final LoaderManager.LoaderCallbacks<Cursor> mGroupLoaderListener =
1546e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov            new LoaderCallbacks<Cursor>() {
1547e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov
1548e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        @Override
1549e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        public CursorLoader onCreateLoader(int id, Bundle args) {
1550e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov            return new GroupMetaDataLoader(mContext);
1551e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        }
1552e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov
1553e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        @Override
1554e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
1555e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov            mGroupMetaData = data;
1556e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov            bindGroupMetaData();
1557e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov        }
1558e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov    };
1559e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov
1560a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    @Override
1561a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    public void onSplitContactConfirmed() {
1562a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        mState.markRawContactsForSplitting();
156315972398b530b91612418dc11d41c356d4f43739Daniel Lehmann        doSaveAction(SaveMode.SPLIT);
1564a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    }
1565a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann
1566a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    /**
1567a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann     * Launches Camera to take a picture and store it in a file.
1568a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann     */
1569a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    @Override
1570a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    public void onTakePhotoChosen() {
1571a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        try {
1572a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            // Launch camera to take photo for selected contact
1573a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            PHOTO_DIR.mkdirs();
1574a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            mCurrentPhotoFile = new File(PHOTO_DIR, getPhotoFileName());
1575a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            final Intent intent = getTakePickIntent(mCurrentPhotoFile);
1576a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann
1577a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            startActivityForResult(intent, REQUEST_CODE_CAMERA_WITH_DATA);
1578a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        } catch (ActivityNotFoundException e) {
1579a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
1580a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        }
1581a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    }
1582a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann
1583a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    /**
1584a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann     * Launches Gallery to pick a photo.
1585a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann     */
1586a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    @Override
1587a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    public void onPickFromGalleryChosen() {
1588a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        try {
1589a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            // Launch picker to choose photo for selected contact
1590a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            final Intent intent = getPhotoPickIntent();
1591a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            startActivityForResult(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA);
1592a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        } catch (ActivityNotFoundException e) {
1593a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann            Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
1594a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        }
1595a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    }
1596a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann
1597a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    /**
1598a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann     * Account was chosen in the selector. Create a RawContact for this account now
1599a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann     */
1600a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    @Override
1601e17b1e52e6895f8177b4192f34126000fcb3f983Daniel Lehmann    public void onAccountChosen(Account account, boolean isNewContact) {
1602e17b1e52e6895f8177b4192f34126000fcb3f983Daniel Lehmann        createContact(account, isNewContact);
1603a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    }
1604a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann
1605a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    /**
1606a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann     * The account selector has been aborted. If we are in "New" mode, we have to close now
1607a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann     */
1608a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    @Override
1609a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    public void onAccountSelectorCancelled() {
16104a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        if (!hasValidState() && mListener != null) {
16114a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann            mListener.onAccountSelectorAborted();
16124a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        }
16134a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    }
16144a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann
16154a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    /**
16164a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann     * User has chosen to set the selected photo as the (super) primary photo
16174a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann     */
16184a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    @Override
16194a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    public void onUseAsPrimaryChosen(long rawContactId) {
16204a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        // Set the IsSuperPrimary for each editor
16214a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        int count = mContent.getChildCount();
16224a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        for (int i = 0; i < count; i++) {
16234a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann            final View childView = mContent.getChildAt(i);
162472af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann            if (childView instanceof BaseRawContactEditorView) {
162572af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann                final BaseRawContactEditorView editor = (BaseRawContactEditorView) childView;
16264a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                final PhotoEditorView photoEditor = editor.getPhotoEditor();
16274a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                photoEditor.setSuperPrimary(editor.getRawContactId() == rawContactId);
16284a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann            }
1629a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann        }
1630a35a7e2aabc4edfafb9b4171e279bdb4c0c03d44Daniel Lehmann    }
16314a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann
16324a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    /**
16334a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann     * User has chosen to remove a picture
16344a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann     */
16354a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    @Override
16364a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    public void onRemovePictureChose(long rawContactId) {
16374a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        // find the correct editor and remove it's photo
16384a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        final int editorCount = mContent.getChildCount();
16394a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        for (int i = 0; i < editorCount; i++) {
16404a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann            final View child = mContent.getChildAt(i);
164172af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann            if (child instanceof BaseRawContactEditorView) {
164272af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann                final BaseRawContactEditorView editor =
164372af89f9e497bb8ef54fe3deeec7e8477d192242Daniel Lehmann                    (BaseRawContactEditorView) child;
16444a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                if (editor.getRawContactId() == rawContactId) {
16454a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                    editor.setPhotoBitmap(null);
16464a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                    break;
16474a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann                }
16484a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann            }
16494a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        }
16504a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    }
16514a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann
16524a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    /**
16534a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann     * User has chosen to change a picture
16544a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann     */
16554a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    @Override
16564a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    public void onChangePictureChosen(long rawContactId) {
16574a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        // Pick a new photo for the contact
16584a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann        doPickPhotoAction(rawContactId);
16594a62be4f5a365e5afde71becfd446381f2baa494Daniel Lehmann    }
16602ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey}
1661