blob: 090ea31defeecbb928ccbf583633c17a2d896a37 [file] [log] [blame]
Walter Jangcab3dce2015-02-09 17:48:03 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.contacts.editor;
18
19import com.android.contacts.R;
Walter Jang31a74ad2015-10-02 19:17:39 -070020import com.android.contacts.common.ContactsUtils;
Walter Jangcab3dce2015-02-09 17:48:03 -080021import com.android.contacts.common.model.AccountTypeManager;
22import com.android.contacts.common.model.RawContactDelta;
23import com.android.contacts.common.model.RawContactDeltaList;
Walter Jange720fde2015-02-17 10:54:14 -080024import com.android.contacts.common.model.RawContactModifier;
Walter Jangcab3dce2015-02-09 17:48:03 -080025import com.android.contacts.common.model.ValuesDelta;
26import com.android.contacts.common.model.account.AccountType;
Walter Jang2d3f31c2015-06-18 23:15:31 -070027import com.android.contacts.common.model.account.AccountWithDataSet;
Walter Jangcab3dce2015-02-09 17:48:03 -080028import com.android.contacts.common.model.dataitem.DataKind;
Walter Jang708ea9e2015-09-10 15:42:05 -070029import com.android.contacts.common.util.AccountsListAdapter;
Walter Jangf46abd82015-02-20 16:52:04 -080030import com.android.contacts.common.util.MaterialColorMapUtils;
Walter Jang31a74ad2015-10-02 19:17:39 -070031import com.android.contacts.util.ContactPhotoUtils;
Walter Jang708ea9e2015-09-10 15:42:05 -070032import com.android.contacts.util.UiClosables;
Walter Jangcab3dce2015-02-09 17:48:03 -080033
34import android.content.Context;
Walter Jangf10ca152015-09-22 15:23:55 -070035import android.database.Cursor;
Walter Jang3efae4a2015-02-18 11:12:00 -080036import android.graphics.Bitmap;
Walter Jang41b3ea12015-03-09 17:30:06 -070037import android.net.Uri;
Walter Jang79658e12015-09-24 10:36:26 -070038import android.os.Parcel;
39import android.os.Parcelable;
Walter Jangcab3dce2015-02-09 17:48:03 -080040import android.provider.ContactsContract.CommonDataKinds.Email;
Walter Jangf5dfea42015-09-16 12:30:36 -070041import android.provider.ContactsContract.CommonDataKinds.Event;
Walter Jangcab3dce2015-02-09 17:48:03 -080042import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Walter Jangf5dfea42015-09-16 12:30:36 -070043import android.provider.ContactsContract.CommonDataKinds.Im;
Walter Jang3efae4a2015-02-18 11:12:00 -080044import android.provider.ContactsContract.CommonDataKinds.Nickname;
Walter Jangf5dfea42015-09-16 12:30:36 -070045import android.provider.ContactsContract.CommonDataKinds.Note;
46import android.provider.ContactsContract.CommonDataKinds.Organization;
Walter Jangcab3dce2015-02-09 17:48:03 -080047import android.provider.ContactsContract.CommonDataKinds.Phone;
48import android.provider.ContactsContract.CommonDataKinds.Photo;
Walter Jangf5dfea42015-09-16 12:30:36 -070049import android.provider.ContactsContract.CommonDataKinds.Relation;
50import android.provider.ContactsContract.CommonDataKinds.SipAddress;
Walter Jangcab3dce2015-02-09 17:48:03 -080051import android.provider.ContactsContract.CommonDataKinds.StructuredName;
Walter Jangf5dfea42015-09-16 12:30:36 -070052import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
53import android.provider.ContactsContract.CommonDataKinds.Website;
Walter Jangcab3dce2015-02-09 17:48:03 -080054import android.text.TextUtils;
55import android.util.AttributeSet;
56import android.util.Log;
Walter Jang2d3f31c2015-06-18 23:15:31 -070057import android.util.Pair;
Walter Jangcab3dce2015-02-09 17:48:03 -080058import android.view.LayoutInflater;
Walter Jangb1c87622015-02-13 17:51:38 -080059import android.view.View;
Walter Jangcab3dce2015-02-09 17:48:03 -080060import android.view.ViewGroup;
Walter Jang708ea9e2015-09-10 15:42:05 -070061import android.widget.AdapterView;
Walter Jangcab3dce2015-02-09 17:48:03 -080062import android.widget.LinearLayout;
Walter Jang708ea9e2015-09-10 15:42:05 -070063import android.widget.ListPopupWindow;
Walter Jang2d3f31c2015-06-18 23:15:31 -070064import android.widget.TextView;
Walter Jangcab3dce2015-02-09 17:48:03 -080065
Walter Jang31a74ad2015-10-02 19:17:39 -070066import java.io.File;
67import java.io.FileNotFoundException;
Walter Jangcab3dce2015-02-09 17:48:03 -080068import java.util.ArrayList;
Walter Jangf5dfea42015-09-16 12:30:36 -070069import java.util.Arrays;
Walter Jang192a01c2015-09-22 15:23:55 -070070import java.util.Collections;
Walter Jangf5dfea42015-09-16 12:30:36 -070071import java.util.Comparator;
Walter Jang19d985f2015-07-01 13:36:27 -070072import java.util.HashMap;
Walter Jangcab3dce2015-02-09 17:48:03 -080073import java.util.List;
Walter Jang19d985f2015-07-01 13:36:27 -070074import java.util.Map;
Walter Jang708ea9e2015-09-10 15:42:05 -070075import java.util.Objects;
Walter Jangf5dfea42015-09-16 12:30:36 -070076import java.util.TreeSet;
Walter Jangcab3dce2015-02-09 17:48:03 -080077
78/**
Walter Jangf5dfea42015-09-16 12:30:36 -070079 * View to display information from multiple {@link RawContactDelta}s grouped together.
Walter Jangcab3dce2015-02-09 17:48:03 -080080 */
Walter Jangb6ca2722015-02-20 11:10:25 -080081public class CompactRawContactsEditorView extends LinearLayout implements View.OnClickListener {
Walter Jangcab3dce2015-02-09 17:48:03 -080082
83 private static final String TAG = "CompactEditorView";
84
Walter Jang192a01c2015-09-22 15:23:55 -070085 private static final KindSectionDataMapEntryComparator
86 KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR = new KindSectionDataMapEntryComparator();
Walter Jangf5dfea42015-09-16 12:30:36 -070087
Walter Jangb6ca2722015-02-20 11:10:25 -080088 /**
89 * Callbacks for hosts of {@link CompactRawContactsEditorView}s.
90 */
91 public interface Listener {
92
93 /**
Walter Jang151f3e62015-02-26 15:29:40 -080094 * Invoked when the structured name editor field has changed.
95 *
96 * @param rawContactId The raw contact ID from the underlying {@link RawContactDelta}.
97 * @param valuesDelta The values from the underlying {@link RawContactDelta}.
98 */
99 public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta);
Walter Jang708ea9e2015-09-10 15:42:05 -0700100
101 /**
102 * Invoked when the compact editor should rebind editors for a new account.
103 *
104 * @param oldState Old data being edited.
105 * @param oldAccount Old account associated with oldState.
106 * @param newAccount New account to be used.
107 */
108 public void onRebindEditorsForNewContact(RawContactDelta oldState,
109 AccountWithDataSet oldAccount, AccountWithDataSet newAccount);
Walter Jang79658e12015-09-24 10:36:26 -0700110
111 /**
112 * Invoked when no editors could be bound for the contact.
113 */
114 public void onBindEditorsFailed();
Walter Jangd6753152015-10-02 09:23:13 -0700115
116 /**
117 * Invoked after editors have been bound for the contact.
118 */
119 public void onEditorsBound();
Walter Jang151f3e62015-02-26 15:29:40 -0800120 }
121
Walter Jang192a01c2015-09-22 15:23:55 -0700122 /** Used to sort entire kind sections. */
123 private static final class KindSectionDataMapEntryComparator implements
Walter Jangf5dfea42015-09-16 12:30:36 -0700124 Comparator<Map.Entry<String,List<KindSectionData>>> {
125
Walter Jang192a01c2015-09-22 15:23:55 -0700126 final MimeTypeComparator mMimeTypeComparator = new MimeTypeComparator();
127
128 @Override
129 public int compare(Map.Entry<String, List<KindSectionData>> entry1,
130 Map.Entry<String, List<KindSectionData>> entry2) {
131 if (entry1 == entry2) return 0;
132 if (entry1 == null) return -1;
133 if (entry2 == null) return 1;
134
135 final String mimeType1 = entry1.getKey();
136 final String mimeType2 = entry2.getKey();
137
138 return mMimeTypeComparator.compare(mimeType1, mimeType2);
139 }
140 }
141
142 /**
143 * Sorts kinds roughly the same as quick contacts; we diverge in the following ways:
144 * <ol>
145 * <li>All names are together at the top.</li>
146 * <li>IM is moved up after addresses</li>
147 * <li>SIP addresses are moved to below phone numbers</li>
Walter Jang79658e12015-09-24 10:36:26 -0700148 * <li>Group membership is placed at the end</li>
Walter Jang192a01c2015-09-22 15:23:55 -0700149 * </ol>
150 */
151 private static final class MimeTypeComparator implements Comparator<String> {
152
Walter Jangf5dfea42015-09-16 12:30:36 -0700153 private static final List<String> MIME_TYPE_ORDER = Arrays.asList(new String[] {
154 StructuredName.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700155 Nickname.CONTENT_ITEM_TYPE,
Walter Jang2ae21752015-09-25 11:25:38 -0700156 Organization.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700157 Phone.CONTENT_ITEM_TYPE,
158 SipAddress.CONTENT_ITEM_TYPE,
159 Email.CONTENT_ITEM_TYPE,
160 StructuredPostal.CONTENT_ITEM_TYPE,
161 Im.CONTENT_ITEM_TYPE,
162 Website.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700163 Event.CONTENT_ITEM_TYPE,
164 Relation.CONTENT_ITEM_TYPE,
Walter Jangf10ca152015-09-22 15:23:55 -0700165 Note.CONTENT_ITEM_TYPE,
166 GroupMembership.CONTENT_ITEM_TYPE
Walter Jangf5dfea42015-09-16 12:30:36 -0700167 });
168
169 @Override
Walter Jang192a01c2015-09-22 15:23:55 -0700170 public int compare(String mimeType1, String mimeType2) {
171 if (mimeType1 == mimeType2) return 0;
172 if (mimeType1 == null) return -1;
173 if (mimeType2 == null) return 1;
Walter Jangf5dfea42015-09-16 12:30:36 -0700174
175 int index1 = MIME_TYPE_ORDER.indexOf(mimeType1);
176 int index2 = MIME_TYPE_ORDER.indexOf(mimeType2);
177
178 // Fallback to alphabetical ordering of the mime type if both are not found
179 if (index1 < 0 && index2 < 0) return mimeType1.compareTo(mimeType2);
180 if (index1 < 0) return 1;
181 if (index2 < 0) return -1;
182
183 return index1 < index2 ? -1 : 1;
184 }
185 }
186
Walter Jang192a01c2015-09-22 15:23:55 -0700187 /**
188 * Sorts primary accounts and google account types before others.
189 */
190 private static final class EditorComparator implements Comparator<KindSectionData> {
191
192 private RawContactDeltaComparator mRawContactDeltaComparator;
193
194 private EditorComparator(Context context) {
195 mRawContactDeltaComparator = new RawContactDeltaComparator(context);
196 }
197
198 @Override
199 public int compare(KindSectionData kindSectionData1, KindSectionData kindSectionData2) {
200 if (kindSectionData1 == kindSectionData2) return 0;
201 if (kindSectionData1 == null) return -1;
202 if (kindSectionData2 == null) return 1;
203
204 final RawContactDelta rawContactDelta1 = kindSectionData1.getRawContactDelta();
205 final RawContactDelta rawContactDelta2 = kindSectionData2.getRawContactDelta();
206
207 if (rawContactDelta1 == rawContactDelta2) return 0;
208 if (rawContactDelta1 == null) return -1;
209 if (rawContactDelta2 == null) return 1;
210
211 return mRawContactDeltaComparator.compare(rawContactDelta1, rawContactDelta2);
212 }
213 }
214
215 /**
216 * Sorts primary account names first, followed by google account types, and other account
217 * types last. For names from the same account we order structured names before nicknames,
218 * but still keep names from the same account together.
219 */
220 private static final class NameEditorComparator implements Comparator<KindSectionData> {
221
222 private RawContactDeltaComparator mRawContactDeltaComparator;
223 private MimeTypeComparator mMimeTypeComparator;
224 private RawContactDelta mPrimaryRawContactDelta;
225
226 private NameEditorComparator(Context context, RawContactDelta primaryRawContactDelta) {
227 mRawContactDeltaComparator = new RawContactDeltaComparator(context);
228 mMimeTypeComparator = new MimeTypeComparator();
229 mPrimaryRawContactDelta = primaryRawContactDelta;
230 }
231
232 @Override
233 public int compare(KindSectionData kindSectionData1, KindSectionData kindSectionData2) {
234 if (kindSectionData1 == kindSectionData2) return 0;
235 if (kindSectionData1 == null) return -1;
236 if (kindSectionData2 == null) return 1;
237
238 final RawContactDelta rawContactDelta1 = kindSectionData1.getRawContactDelta();
239 final RawContactDelta rawContactDelta2 = kindSectionData2.getRawContactDelta();
240
241 if (rawContactDelta1 == rawContactDelta2) return 0;
242 if (rawContactDelta1 == null) return -1;
243 if (rawContactDelta2 == null) return 1;
244
245 final boolean isRawContactDelta1Primary =
246 mPrimaryRawContactDelta.equals(rawContactDelta1);
247 final boolean isRawContactDelta2Primary =
248 mPrimaryRawContactDelta.equals(rawContactDelta2);
249
250 // If both names are from the primary account, sort my by mime type
251 if (isRawContactDelta1Primary && isRawContactDelta2Primary) {
252 final String mimeType1 = kindSectionData1.getDataKind().mimeType;
253 final String mimeType2 = kindSectionData2.getDataKind().mimeType;
254 return mMimeTypeComparator.compare(mimeType1, mimeType2);
255 }
256
257 // The primary account name should be before all others
Walter Jang79658e12015-09-24 10:36:26 -0700258 if (isRawContactDelta1Primary) return -1;
259 if (isRawContactDelta2Primary) return 1;
Walter Jang192a01c2015-09-22 15:23:55 -0700260
261 return mRawContactDeltaComparator.compare(rawContactDelta1, rawContactDelta2);
262 }
263 }
264
Walter Jang79658e12015-09-24 10:36:26 -0700265 public static class SavedState extends BaseSavedState {
266
267 public static final Parcelable.Creator<SavedState> CREATOR =
268 new Parcelable.Creator<SavedState>() {
269 public SavedState createFromParcel(Parcel in) {
270 return new SavedState(in);
271 }
272 public SavedState[] newArray(int size) {
273 return new SavedState[size];
274 }
275 };
276
277 private boolean mIsExpanded;
278
279 public SavedState(Parcelable superState) {
280 super(superState);
281 }
282
283 private SavedState(Parcel in) {
284 super(in);
285 mIsExpanded = in.readInt() != 0;
286 }
287
288 @Override
289 public void writeToParcel(Parcel out, int flags) {
290 super.writeToParcel(out, flags);
291 out.writeInt(mIsExpanded ? 1 : 0);
292 }
293 }
294
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700295 private CompactRawContactsEditorView.Listener mListener;
Walter Jangb6ca2722015-02-20 11:10:25 -0800296
Walter Jangcab3dce2015-02-09 17:48:03 -0800297 private AccountTypeManager mAccountTypeManager;
298 private LayoutInflater mLayoutInflater;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800299
Walter Jangcab3dce2015-02-09 17:48:03 -0800300 private ViewIdGenerator mViewIdGenerator;
Walter Jangf46abd82015-02-20 16:52:04 -0800301 private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
Walter Jang708ea9e2015-09-10 15:42:05 -0700302 private long mPhotoId;
Walter Jang708ea9e2015-09-10 15:42:05 -0700303 private boolean mHasNewContact;
304 private boolean mIsUserProfile;
305 private AccountWithDataSet mPrimaryAccount;
306 private RawContactDelta mPrimaryRawContactDelta;
Walter Jang363d3fd2015-09-16 10:29:07 -0700307 private Map<String,List<KindSectionData>> mKindSectionDataMap = new HashMap<>();
Walter Jangcab3dce2015-02-09 17:48:03 -0800308
Walter Jang708ea9e2015-09-10 15:42:05 -0700309 // Account header
310 private View mAccountHeaderContainer;
311 private TextView mAccountHeaderType;
312 private TextView mAccountHeaderName;
313
314 // Account selector
315 private View mAccountSelectorContainer;
316 private View mAccountSelector;
317 private TextView mAccountSelectorType;
318 private TextView mAccountSelectorName;
319
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700320 private CompactPhotoEditorView mPhotoView;
Walter Jangf5dfea42015-09-16 12:30:36 -0700321 private ViewGroup mKindSectionViews;
Walter Jangf10ca152015-09-22 15:23:55 -0700322 private Map<String,List<CompactKindSectionView>> mKindSectionViewsMap = new HashMap<>();
Walter Jangb6ca2722015-02-20 11:10:25 -0800323 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -0800324
Walter Jang79658e12015-09-24 10:36:26 -0700325 private boolean mIsExpanded;
Walter Jang3efae4a2015-02-18 11:12:00 -0800326 private long mPhotoRawContactId;
Walter Jang31a74ad2015-10-02 19:17:39 -0700327 private ValuesDelta mPhotoValuesDelta;
Walter Jang3efae4a2015-02-18 11:12:00 -0800328
Walter Jangcab3dce2015-02-09 17:48:03 -0800329 public CompactRawContactsEditorView(Context context) {
330 super(context);
331 }
332
333 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
334 super(context, attrs);
335 }
336
Walter Jangb6ca2722015-02-20 11:10:25 -0800337 /**
338 * Sets the receiver for {@link CompactRawContactsEditorView} callbacks.
339 */
340 public void setListener(Listener listener) {
341 mListener = listener;
342 }
343
Walter Jangcab3dce2015-02-09 17:48:03 -0800344 @Override
345 protected void onFinishInflate() {
346 super.onFinishInflate();
347
348 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
349 mLayoutInflater = (LayoutInflater)
350 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
351
Walter Jang708ea9e2015-09-10 15:42:05 -0700352 // Account header
353 mAccountHeaderContainer = findViewById(R.id.account_container);
354 mAccountHeaderType = (TextView) findViewById(R.id.account_type);
355 mAccountHeaderName = (TextView) findViewById(R.id.account_name);
356
357 // Account selector
358 mAccountSelectorContainer = findViewById(R.id.account_selector_container);
359 mAccountSelector = findViewById(R.id.account);
360 mAccountSelectorType = (TextView) findViewById(R.id.account_type_selector);
361 mAccountSelectorName = (TextView) findViewById(R.id.account_name_selector);
362
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700363 mPhotoView = (CompactPhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangf5dfea42015-09-16 12:30:36 -0700364 mKindSectionViews = (LinearLayout) findViewById(R.id.kind_section_views);
Walter Jangb6ca2722015-02-20 11:10:25 -0800365 mMoreFields = findViewById(R.id.more_fields);
366 mMoreFields.setOnClickListener(this);
367 }
368
Walter Jangb6ca2722015-02-20 11:10:25 -0800369 @Override
370 public void onClick(View view) {
Walter Jangf5dfea42015-09-16 12:30:36 -0700371 if (view.getId() == R.id.more_fields) {
Walter Jangd6753152015-10-02 09:23:13 -0700372 showAllFields();
Walter Jangb6ca2722015-02-20 11:10:25 -0800373 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800374 }
375
376 @Override
377 public void setEnabled(boolean enabled) {
378 super.setEnabled(enabled);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700379 final int childCount = mKindSectionViews.getChildCount();
380 for (int i = 0; i < childCount; i++) {
381 mKindSectionViews.getChildAt(i).setEnabled(enabled);
Walter Jangcab3dce2015-02-09 17:48:03 -0800382 }
383 }
384
Walter Jang79658e12015-09-24 10:36:26 -0700385 @Override
386 public Parcelable onSaveInstanceState() {
387 final Parcelable superState = super.onSaveInstanceState();
388 final SavedState savedState = new SavedState(superState);
389 savedState.mIsExpanded = mIsExpanded;
390 return savedState;
391 }
392
393 @Override
394 public void onRestoreInstanceState(Parcelable state) {
395 if(!(state instanceof SavedState)) {
396 super.onRestoreInstanceState(state);
397 return;
398 }
399 final SavedState savedState = (SavedState) state;
400 super.onRestoreInstanceState(savedState.getSuperState());
401 mIsExpanded = savedState.mIsExpanded;
Walter Jangd6753152015-10-02 09:23:13 -0700402 if (mIsExpanded) {
403 showAllFields();
Walter Jang79658e12015-09-24 10:36:26 -0700404 }
405 }
406
Walter Jang3efae4a2015-02-18 11:12:00 -0800407 /**
Walter Jang31a74ad2015-10-02 19:17:39 -0700408 * Pass through to {@link CompactPhotoEditorView#setListener}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800409 */
Walter Jang31a74ad2015-10-02 19:17:39 -0700410 public void setPhotoListener(CompactPhotoEditorView.Listener listener) {
411 mPhotoView.setListener(listener);
Walter Jang3efae4a2015-02-18 11:12:00 -0800412 }
413
Walter Jang31a74ad2015-10-02 19:17:39 -0700414 public void removePhoto() {
415 mPhotoValuesDelta.setFromTemplate(false);
416 mPhotoValuesDelta.put(Photo.PHOTO, (byte[]) null);
417
418 mPhotoView.removePhoto();
Walter Jang3efae4a2015-02-18 11:12:00 -0800419 }
420
421 /**
Walter Jang41b3ea12015-03-09 17:30:06 -0700422 * Pass through to {@link CompactPhotoEditorView#setFullSizedPhoto(Uri)}.
423 */
424 public void setFullSizePhoto(Uri photoUri) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700425 mPhotoView.setFullSizedPhoto(photoUri);
Walter Jang41b3ea12015-03-09 17:30:06 -0700426 }
427
Walter Jang31a74ad2015-10-02 19:17:39 -0700428 public void updatePhoto(Uri photoUri) {
429 // Even though high-res photos cannot be saved by passing them via
430 // an EntityDeltaList (since they cause the Bundle size limit to be
431 // exceeded), we still pass a low-res thumbnail. This simplifies
432 // code all over the place, because we don't have to test whether
433 // there is a change in EITHER the delta-list OR a changed photo...
434 // this way, there is always a change in the delta-list.
435 mPhotoValuesDelta.setFromTemplate(false);
436 mPhotoValuesDelta.setSuperPrimary(true);
437 try {
438 final byte[] bytes = EditorUiUtils.getCompressedThumbnailBitmapBytes(
439 getContext(), photoUri);
440 if (bytes != null) {
441 mPhotoValuesDelta.setPhoto(bytes);
442 }
443 } catch (FileNotFoundException e) {
444 elog("Failed to get bitmap from photo Uri");
445 }
446
447 mPhotoView.setFullSizedPhoto(photoUri);
448 }
449
Walter Jang41b3ea12015-03-09 17:30:06 -0700450 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800451 * Pass through to {@link CompactPhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800452 */
453 public boolean isWritablePhotoSet() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700454 return mPhotoView.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800455 }
456
457 /**
Walter Jang3efae4a2015-02-18 11:12:00 -0800458 * Get the raw contact ID for the CompactHeaderView photo.
459 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800460 public long getPhotoRawContactId() {
461 return mPhotoRawContactId;
462 }
463
Walter Jang31a74ad2015-10-02 19:17:39 -0700464 /**
465 * Returns a data holder for every non-default/non-empty photo from each raw contact, whether
466 * the raw contact is writable or not.
467 */
468 public ArrayList<CompactPhotoSelectionFragment.Photo> getPhotos() {
469 final ArrayList<CompactPhotoSelectionFragment.Photo> photos = new ArrayList<>();
470
471 final List<KindSectionData> kindSectionDataList =
472 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
473 for (int i = 0; i < kindSectionDataList.size(); i++) {
474 final KindSectionData kindSectionData = kindSectionDataList.get(i);
475 final AccountType accountType = kindSectionData.getAccountType();
476 final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
477 if (valuesDeltaList == null || valuesDeltaList.isEmpty()) continue;
478 for (int j = 0; j < valuesDeltaList.size(); j++) {
479 final ValuesDelta valuesDelta = valuesDeltaList.get(j);
480 final Bitmap bitmap = EditorUiUtils.getPhotoBitmap(valuesDelta);
481 if (bitmap == null) continue;
482
483 final CompactPhotoSelectionFragment.Photo photo =
484 new CompactPhotoSelectionFragment.Photo();
485 photo.titleRes = accountType.titleRes;
486 photo.iconRes = accountType.iconRes;
487 photo.syncAdapterPackageName = accountType.syncAdapterPackageName;
488 photo.valuesDelta = valuesDelta;
489 photo.primary = valuesDelta.isSuperPrimary();
490 photo.kindSectionDataListIndex = i;
491 photo.valuesDeltaListIndex = j;
492 photos.add(photo);
493 }
494 }
495
496 return photos;
497 }
498
499 /**
500 * Marks the raw contact photo given as primary for the aggregate contact and updates the
501 * UI.
502 */
503 public void setPrimaryPhoto(CompactPhotoSelectionFragment.Photo photo) {
504 // Unset primary for all other photos
505 final List<KindSectionData> kindSectionDataList =
506 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
507 for (KindSectionData kindSectionData : kindSectionDataList) {
508 final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
509 for (ValuesDelta valuesDelta : valuesDeltaList) {
510 valuesDelta.setSuperPrimary(false);
511 }
512 }
513
514 // Find the values delta to mark as primary
515 if (photo.kindSectionDataListIndex < 0
516 || photo.kindSectionDataListIndex >= kindSectionDataList.size()) {
517 wlog("Invalid kind section data list index");
518 return;
519 }
520 final KindSectionData kindSectionData =
521 kindSectionDataList.get(photo.kindSectionDataListIndex);
522 final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
523 if (photo.valuesDeltaListIndex >= valuesDeltaList.size()) {
524 wlog("Invalid values delta list index");
525 return;
526 }
527 final ValuesDelta valuesDelta = valuesDeltaList.get(photo.valuesDeltaListIndex);
528 valuesDelta.setFromTemplate(false);
529 valuesDelta.setSuperPrimary(true);
530
531 // Update the UI
532 mPhotoView.setPhoto(valuesDelta, mMaterialPalette);
533 }
534
Walter Jangd35e5ef2015-02-24 09:18:16 -0800535 public View getAggregationAnchorView() {
Walter Jangf10ca152015-09-22 15:23:55 -0700536 final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
Walter Jang192a01c2015-09-22 15:23:55 -0700537 StructuredName.CONTENT_ITEM_TYPE);
Walter Jangf10ca152015-09-22 15:23:55 -0700538 if (!kindSectionViews.isEmpty()) {
Walter Jang192a01c2015-09-22 15:23:55 -0700539 return mKindSectionViews.getChildAt(0).findViewById(R.id.anchor_view);
Walter Jangd35e5ef2015-02-24 09:18:16 -0800540 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700541 return null;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800542 }
543
Walter Jangf10ca152015-09-22 15:23:55 -0700544 public void setGroupMetaData(Cursor groupMetaData) {
545 final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
546 GroupMembership.CONTENT_ITEM_TYPE);
547 for (CompactKindSectionView kindSectionView : kindSectionViews) {
548 kindSectionView.setGroupMetaData(groupMetaData);
Walter Jangd6753152015-10-02 09:23:13 -0700549 if (mIsExpanded) {
550 kindSectionView.setHideWhenEmpty(false);
551 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
552 }
Walter Jangf10ca152015-09-22 15:23:55 -0700553 }
554 }
555
Walter Jangf46abd82015-02-20 16:52:04 -0800556 public void setState(RawContactDeltaList rawContactDeltas,
Walter Jang06f73a12015-06-17 11:15:48 -0700557 MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700558 long photoId, boolean hasNewContact, boolean isUserProfile,
559 AccountWithDataSet primaryAccount) {
Walter Jang363d3fd2015-09-16 10:29:07 -0700560 mKindSectionDataMap.clear();
Walter Jangf5dfea42015-09-16 12:30:36 -0700561 mKindSectionViews.removeAllViews();
Walter Jang363d3fd2015-09-16 10:29:07 -0700562 mMoreFields.setVisibility(View.VISIBLE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800563
Walter Jangf46abd82015-02-20 16:52:04 -0800564 mMaterialPalette = materialPalette;
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700565 mViewIdGenerator = viewIdGenerator;
Walter Jang708ea9e2015-09-10 15:42:05 -0700566 mPhotoId = photoId;
Walter Jang708ea9e2015-09-10 15:42:05 -0700567 mHasNewContact = hasNewContact;
568 mIsUserProfile = isUserProfile;
569 mPrimaryAccount = primaryAccount;
570 if (mPrimaryAccount == null) {
571 mPrimaryAccount = ContactEditorUtils.getInstance(getContext()).getDefaultAccount();
572 }
573 vlog("state: primary " + mPrimaryAccount);
Walter Jangcab3dce2015-02-09 17:48:03 -0800574
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700575 // Parse the given raw contact deltas
576 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
577 elog("No raw contact deltas");
Walter Jang79658e12015-09-24 10:36:26 -0700578 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jangf5dfea42015-09-16 12:30:36 -0700579 return;
580 }
Walter Jang192a01c2015-09-22 15:23:55 -0700581 parseRawContactDeltas(rawContactDeltas, mPrimaryAccount);
Walter Jang79658e12015-09-24 10:36:26 -0700582 if (mKindSectionDataMap.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700583 elog("No kind section data parsed from RawContactDelta(s)");
Walter Jang79658e12015-09-24 10:36:26 -0700584 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700585 return;
586 }
587
588 // Setup the view
Walter Jang708ea9e2015-09-10 15:42:05 -0700589 addAccountInfo();
Walter Jang363d3fd2015-09-16 10:29:07 -0700590 addPhotoView();
Walter Jangf5dfea42015-09-16 12:30:36 -0700591 addKindSectionViews();
Walter Jang79658e12015-09-24 10:36:26 -0700592
Walter Jangd6753152015-10-02 09:23:13 -0700593 if (mIsExpanded) {
594 showAllFields();
595 }
596
597 if (mListener != null) mListener.onEditorsBound();
Walter Jangcab3dce2015-02-09 17:48:03 -0800598 }
599
Walter Jang192a01c2015-09-22 15:23:55 -0700600 private void parseRawContactDeltas(RawContactDeltaList rawContactDeltas,
601 AccountWithDataSet primaryAccount) {
602 if (primaryAccount != null) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700603 // Use the first writable contact that matches the primary account
604 for (RawContactDelta rawContactDelta : rawContactDeltas) {
605 if (!rawContactDelta.isVisible()) continue;
606 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700607 if (accountType == null || !accountType.areContactsWritable()) continue;
Walter Jang192a01c2015-09-22 15:23:55 -0700608 if (matchesAccount(primaryAccount, rawContactDelta)) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700609 vlog("parse: matched primary account raw contact");
Walter Jang708ea9e2015-09-10 15:42:05 -0700610 mPrimaryRawContactDelta = rawContactDelta;
611 break;
612 }
Walter Jang2d3f31c2015-06-18 23:15:31 -0700613 }
614 }
Walter Jang708ea9e2015-09-10 15:42:05 -0700615 if (mPrimaryRawContactDelta == null) {
616 // Fall back to the first writable raw contact
617 for (RawContactDelta rawContactDelta : rawContactDeltas) {
618 if (!rawContactDelta.isVisible()) continue;
619 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
620 if (accountType != null && accountType.areContactsWritable()) {
621 vlog("parse: falling back to the first writable raw contact as primary");
622 mPrimaryRawContactDelta = rawContactDelta;
623 break;
624 }
625 }
Walter Jang2d3f31c2015-06-18 23:15:31 -0700626 }
Walter Jang192a01c2015-09-22 15:23:55 -0700627
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700628 if (mPrimaryRawContactDelta != null) {
629 RawContactModifier.ensureKindExists(mPrimaryRawContactDelta,
630 mPrimaryRawContactDelta.getAccountType(mAccountTypeManager),
631 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700632 RawContactModifier.ensureKindExists(mPrimaryRawContactDelta,
633 mPrimaryRawContactDelta.getAccountType(mAccountTypeManager),
634 Photo.CONTENT_ITEM_TYPE);
635 }
Walter Jangfa127a12015-06-18 09:48:18 -0700636
Walter Jang363d3fd2015-09-16 10:29:07 -0700637 // Build the kind section data list map
Walter Jang192a01c2015-09-22 15:23:55 -0700638 vlog("parse: " + rawContactDeltas.size() + " rawContactDelta(s)");
639 for (int j = 0; j < rawContactDeltas.size(); j++) {
640 final RawContactDelta rawContactDelta = rawContactDeltas.get(j);
641 vlog("parse: " + j + " rawContactDelta" + rawContactDelta);
Walter Jang363d3fd2015-09-16 10:29:07 -0700642 if (rawContactDelta == null || !rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800643 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jang363d3fd2015-09-16 10:29:07 -0700644 if (accountType == null) continue;
645 final List<DataKind> dataKinds = accountType.getSortedDataKinds();
646 final int dataKindSize = dataKinds == null ? 0 : dataKinds.size();
647 vlog("parse: " + dataKindSize + " dataKinds(s)");
648 for (int i = 0; i < dataKindSize; i++) {
649 final DataKind dataKind = dataKinds.get(i);
Walter Jang192a01c2015-09-22 15:23:55 -0700650 if (dataKind == null || !dataKind.editable) {
651 vlog("parse: " + i + " " + dataKind.mimeType + " dropped read-only");
652 continue;
653 }
654 final String mimeType = dataKind.mimeType;
655
656 // Skip psuedo mime types
657 if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
658 || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
659 vlog("parse: " + i + " " + dataKind.mimeType + " dropped pseudo type");
660 continue;
661 }
662
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700663 final List<KindSectionData> kindSectionDataList =
Walter Jang192a01c2015-09-22 15:23:55 -0700664 getKindSectionDataList(mimeType);
Walter Jang363d3fd2015-09-16 10:29:07 -0700665 final KindSectionData kindSectionData =
666 new KindSectionData(accountType, dataKind, rawContactDelta);
667 kindSectionDataList.add(kindSectionData);
Walter Jang192a01c2015-09-22 15:23:55 -0700668
Walter Jang79658e12015-09-24 10:36:26 -0700669 // Note we must create nickname entries
Walter Jang192a01c2015-09-22 15:23:55 -0700670 if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jang79658e12015-09-24 10:36:26 -0700671 && kindSectionData.getValuesDeltas().isEmpty()) {
Walter Jang192a01c2015-09-22 15:23:55 -0700672 RawContactModifier.insertChild(rawContactDelta, dataKind);
673 }
674
Walter Jang363d3fd2015-09-16 10:29:07 -0700675 vlog("parse: " + i + " " + dataKind.mimeType + " " +
Walter Jang192a01c2015-09-22 15:23:55 -0700676 kindSectionData.getValuesDeltas().size() + " value(s)");
Walter Jangac679af2015-06-01 12:17:06 -0700677 }
678 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800679 }
680
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700681 private List<KindSectionData> getKindSectionDataList(String mimeType) {
Walter Jang192a01c2015-09-22 15:23:55 -0700682 // Put structured names and nicknames together
683 mimeType = Nickname.CONTENT_ITEM_TYPE.equals(mimeType)
684 ? StructuredName.CONTENT_ITEM_TYPE : mimeType;
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700685 List<KindSectionData> kindSectionDataList = mKindSectionDataMap.get(mimeType);
686 if (kindSectionDataList == null) {
687 kindSectionDataList = new ArrayList<>();
688 mKindSectionDataMap.put(mimeType, kindSectionDataList);
689 }
690 return kindSectionDataList;
691 }
692
Walter Jang192a01c2015-09-22 15:23:55 -0700693 /** Whether the given RawContactDelta belong to the given account. */
694 private boolean matchesAccount(AccountWithDataSet accountWithDataSet,
695 RawContactDelta rawContactDelta) {
696 if (accountWithDataSet == null) return false;
697 return Objects.equals(accountWithDataSet.name, rawContactDelta.getAccountName())
698 && Objects.equals(accountWithDataSet.type, rawContactDelta.getAccountType())
699 && Objects.equals(accountWithDataSet.dataSet, rawContactDelta.getDataSet());
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700700 }
701
Walter Jang708ea9e2015-09-10 15:42:05 -0700702 private void addAccountInfo() {
703 if (mPrimaryRawContactDelta == null) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700704 mAccountHeaderContainer.setVisibility(View.GONE);
705 mAccountSelectorContainer.setVisibility(View.GONE);
706 return;
707 }
708
709 // Get the account information for the primary raw contact delta
710 final Pair<String,String> accountInfo = EditorUiUtils.getAccountInfo(getContext(),
711 mIsUserProfile, mPrimaryRawContactDelta.getAccountName(),
712 mPrimaryRawContactDelta.getAccountType(mAccountTypeManager));
713
714 // The account header and selector show the same information so both shouldn't be visible
715 // at the same time
716 final List<AccountWithDataSet> accounts =
717 AccountTypeManager.getInstance(getContext()).getAccounts(true);
718 if (mHasNewContact && !mIsUserProfile && accounts.size() > 1) {
719 mAccountHeaderContainer.setVisibility(View.GONE);
720 addAccountSelector(accountInfo);
721 } else {
722 addAccountHeader(accountInfo);
723 mAccountSelectorContainer.setVisibility(View.GONE);
724 }
725 }
726
727 private void addAccountHeader(Pair<String,String> accountInfo) {
Walter Jang03cea2e2015-09-18 17:04:21 -0700728 if (TextUtils.isEmpty(accountInfo.first)) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700729 // Hide this view so the other text view will be centered vertically
730 mAccountHeaderName.setVisibility(View.GONE);
731 } else {
732 mAccountHeaderName.setVisibility(View.VISIBLE);
733 mAccountHeaderName.setText(accountInfo.first);
734 }
735 mAccountHeaderType.setText(accountInfo.second);
736
737 mAccountHeaderContainer.setContentDescription(
738 EditorUiUtils.getAccountInfoContentDescription(
739 accountInfo.first, accountInfo.second));
740 }
741
742 private void addAccountSelector(Pair<String,String> accountInfo) {
743 mAccountSelectorContainer.setVisibility(View.VISIBLE);
744
Walter Jang03cea2e2015-09-18 17:04:21 -0700745 if (TextUtils.isEmpty(accountInfo.first)) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700746 // Hide this view so the other text view will be centered vertically
747 mAccountSelectorName.setVisibility(View.GONE);
748 } else {
749 mAccountSelectorName.setVisibility(View.VISIBLE);
750 mAccountSelectorName.setText(accountInfo.first);
751 }
752 mAccountSelectorType.setText(accountInfo.second);
753
754 mAccountSelectorContainer.setContentDescription(
755 EditorUiUtils.getAccountInfoContentDescription(
756 accountInfo.first, accountInfo.second));
757
758 mAccountSelector.setOnClickListener(new View.OnClickListener() {
759 @Override
760 public void onClick(View v) {
761 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
762 final AccountsListAdapter adapter =
763 new AccountsListAdapter(getContext(),
764 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE,
765 mPrimaryAccount);
766 popup.setWidth(mAccountSelectorContainer.getWidth());
767 popup.setAnchorView(mAccountSelectorContainer);
768 popup.setAdapter(adapter);
769 popup.setModal(true);
770 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
771 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
772 @Override
773 public void onItemClick(AdapterView<?> parent, View view, int position,
774 long id) {
775 UiClosables.closeQuietly(popup);
776 final AccountWithDataSet newAccount = adapter.getItem(position);
777 if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
778 mListener.onRebindEditorsForNewContact(
779 mPrimaryRawContactDelta,
780 mPrimaryAccount,
781 newAccount);
782 }
783 }
784 });
785 popup.show();
786 }
787 });
788 }
789
Walter Jang363d3fd2015-09-16 10:29:07 -0700790 private void addPhotoView() {
Walter Jang31a74ad2015-10-02 19:17:39 -0700791 // Get the kind section data and values delta that we will display in the photo view
792 Pair<KindSectionData,ValuesDelta> pair = getPrimaryPhotoKindSectionData(mPhotoId);
Walter Jang363d3fd2015-09-16 10:29:07 -0700793 if (pair == null) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700794 wlog("photo: no kind section data parsed");
Walter Jang31a74ad2015-10-02 19:17:39 -0700795 mPhotoView.setReadOnly(true);
Walter Jang363d3fd2015-09-16 10:29:07 -0700796 return;
797 }
Walter Jang06f73a12015-06-17 11:15:48 -0700798
Walter Jang31a74ad2015-10-02 19:17:39 -0700799 // Set the photo view
800 final ValuesDelta primaryValuesDelta = pair.second;
801 mPhotoView.setPhoto(primaryValuesDelta, mMaterialPalette);
802
803 // Find the raw contact ID and values delta that will be written when the photo is edited
804 final KindSectionData primaryKindSectionData = pair.first;
805 if (mHasNewContact && mPrimaryRawContactDelta != null
806 && !primaryKindSectionData.getValuesDeltas().isEmpty()) {
807 // If we're editing a read-only contact we want to display the photo from the
808 // read-only contact in a photo editor view, but update the new raw contact
809 // that was created.
810 mPhotoRawContactId = mPrimaryRawContactDelta.getRawContactId();
811 mPhotoValuesDelta = primaryKindSectionData.getValuesDeltas().get(0);
812 mPhotoView.setReadOnly(false);
813 return;
814 }
815 if (primaryKindSectionData.getAccountType().areContactsWritable() &&
816 !primaryKindSectionData.getValuesDeltas().isEmpty()) {
817 mPhotoRawContactId = primaryKindSectionData.getRawContactDelta().getRawContactId();
818 mPhotoValuesDelta = primaryKindSectionData.getValuesDeltas().get(0);
819 mPhotoView.setReadOnly(false);
820 return;
Walter Jang363d3fd2015-09-16 10:29:07 -0700821 }
Walter Jang06f73a12015-06-17 11:15:48 -0700822
Walter Jang31a74ad2015-10-02 19:17:39 -0700823 final KindSectionData writableKindSectionData = getFirstWritablePhotoKindSectionData();
824 if (writableKindSectionData == null
825 || writableKindSectionData.getValuesDeltas().isEmpty()) {
826 mPhotoView.setReadOnly(true);
827 return;
828 }
829 mPhotoRawContactId = writableKindSectionData.getRawContactDelta().getRawContactId();
830 mPhotoValuesDelta = writableKindSectionData.getValuesDeltas().get(0);
831 mPhotoView.setReadOnly(false);
Walter Jang363d3fd2015-09-16 10:29:07 -0700832 }
833
Walter Jang31a74ad2015-10-02 19:17:39 -0700834 private Pair<KindSectionData,ValuesDelta> getPrimaryPhotoKindSectionData(long id) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700835 final String mimeType = Photo.CONTENT_ITEM_TYPE;
Walter Jang363d3fd2015-09-16 10:29:07 -0700836 final List<KindSectionData> kindSectionDataList = mKindSectionDataMap.get(mimeType);
Walter Jang151f3e62015-02-26 15:29:40 -0800837
Walter Jang363d3fd2015-09-16 10:29:07 -0700838 KindSectionData resultKindSectionData = null;
839 ValuesDelta resultValuesDelta = null;
840 if (id > 0) {
841 // Look for a match for the ID that was passed in
842 for (KindSectionData kindSectionData : kindSectionDataList) {
843 resultValuesDelta = kindSectionData.getValuesDeltaById(id);
844 if (resultValuesDelta != null) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700845 vlog("photo: matched kind section data by ID");
Walter Jang363d3fd2015-09-16 10:29:07 -0700846 resultKindSectionData = kindSectionData;
847 break;
Walter Jang398cd4b2015-06-16 11:17:53 -0700848 }
849 }
850 }
Walter Jang363d3fd2015-09-16 10:29:07 -0700851 if (resultKindSectionData == null) {
852 // Look for a super primary photo
853 for (KindSectionData kindSectionData : kindSectionDataList) {
854 resultValuesDelta = kindSectionData.getSuperPrimaryValuesDelta();
855 if (resultValuesDelta != null) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700856 wlog("photo: matched super primary kind section data");
Walter Jang363d3fd2015-09-16 10:29:07 -0700857 resultKindSectionData = kindSectionData;
858 break;
859 }
Walter Jang151f3e62015-02-26 15:29:40 -0800860 }
861 }
Walter Jang363d3fd2015-09-16 10:29:07 -0700862 if (resultKindSectionData == null) {
863 // Fall back to the first non-empty value
864 for (KindSectionData kindSectionData : kindSectionDataList) {
865 resultValuesDelta = kindSectionData.getFirstNonEmptyValuesDelta();
866 if (resultValuesDelta != null) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700867 vlog("photo: using first non empty value");
Walter Jang363d3fd2015-09-16 10:29:07 -0700868 resultKindSectionData = kindSectionData;
869 break;
870 }
Walter Jang151f3e62015-02-26 15:29:40 -0800871 }
872 }
Walter Jang363d3fd2015-09-16 10:29:07 -0700873 if (resultKindSectionData == null || resultValuesDelta == null) {
874 final List<ValuesDelta> valuesDeltaList = kindSectionDataList.get(0).getValuesDeltas();
875 if (valuesDeltaList != null && !valuesDeltaList.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700876 vlog("photo: falling back to first empty entry");
Walter Jang363d3fd2015-09-16 10:29:07 -0700877 resultValuesDelta = valuesDeltaList.get(0);
878 resultKindSectionData = kindSectionDataList.get(0);
Walter Jang10446452015-02-20 13:51:16 -0800879 }
880 }
Walter Jang363d3fd2015-09-16 10:29:07 -0700881 return resultKindSectionData != null && resultValuesDelta != null
882 ? new Pair<>(resultKindSectionData, resultValuesDelta) : null;
Walter Jang10446452015-02-20 13:51:16 -0800883 }
884
Walter Jang31a74ad2015-10-02 19:17:39 -0700885 private KindSectionData getFirstWritablePhotoKindSectionData() {
886 final String mimeType = Photo.CONTENT_ITEM_TYPE;
887 final List<KindSectionData> kindSectionDataList = mKindSectionDataMap.get(mimeType);
888 for (KindSectionData kindSectionData : kindSectionDataList) {
889 if (kindSectionData.getAccountType().areContactsWritable()) {
890 return kindSectionData;
891 }
892 }
893 return null;
894 }
895
Walter Jangf5dfea42015-09-16 12:30:36 -0700896 private void addKindSectionViews() {
897 // Sort the kinds
898 final TreeSet<Map.Entry<String,List<KindSectionData>>> entries =
Walter Jang192a01c2015-09-22 15:23:55 -0700899 new TreeSet<>(KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR);
Walter Jangf5dfea42015-09-16 12:30:36 -0700900 entries.addAll(mKindSectionDataMap.entrySet());
Walter Jang3efae4a2015-02-18 11:12:00 -0800901
Walter Jangf5dfea42015-09-16 12:30:36 -0700902 vlog("kind: " + entries.size() + " kindSection(s)");
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700903 int i = -1;
Walter Jangf5dfea42015-09-16 12:30:36 -0700904 for (Map.Entry<String, List<KindSectionData>> entry : entries) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700905 i++;
906
Walter Jangf5dfea42015-09-16 12:30:36 -0700907 final String mimeType = entry.getKey();
908 final List<KindSectionData> kindSectionDataList = entry.getValue();
Walter Jangab50e6f2015-06-15 08:57:22 -0700909
Walter Jangf5dfea42015-09-16 12:30:36 -0700910 // Ignore mime types that we've already handled
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700911 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
912 vlog("kind: " + i + " " + mimeType + " dropped");
913 continue;
914 }
Walter Jangf5dfea42015-09-16 12:30:36 -0700915
Walter Jang192a01c2015-09-22 15:23:55 -0700916 if (kindSectionDataList != null && !kindSectionDataList.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700917 vlog("kind: " + i + " " + mimeType + ": " + kindSectionDataList.size() +
918 " kindSectionData(s)");
919
Walter Jangf5dfea42015-09-16 12:30:36 -0700920 final CompactKindSectionView kindSectionView = inflateKindSectionView(
921 mKindSectionViews, kindSectionDataList, mimeType);
Walter Jangf5dfea42015-09-16 12:30:36 -0700922 mKindSectionViews.addView(kindSectionView);
Walter Jangf10ca152015-09-22 15:23:55 -0700923
924 // Keep a pointer to all the KindSectionsViews for each mimeType
925 getKindSectionViews(mimeType).add(kindSectionView);
Walter Jangcab3dce2015-02-09 17:48:03 -0800926 }
927 }
928 }
929
Walter Jangf10ca152015-09-22 15:23:55 -0700930 private List<CompactKindSectionView> getKindSectionViews(String mimeType) {
931 List<CompactKindSectionView> kindSectionViews = mKindSectionViewsMap.get(mimeType);
932 if (kindSectionViews == null) {
933 kindSectionViews = new ArrayList<>();
934 mKindSectionViewsMap.put(mimeType, kindSectionViews);
935 }
936 return kindSectionViews;
937 }
938
Walter Jangf5dfea42015-09-16 12:30:36 -0700939 private CompactKindSectionView inflateKindSectionView(ViewGroup viewGroup,
940 List<KindSectionData> kindSectionDataList, String mimeType) {
941 final CompactKindSectionView kindSectionView = (CompactKindSectionView)
942 mLayoutInflater.inflate(R.layout.compact_item_kind_section, viewGroup,
943 /* attachToRoot =*/ false);
944
Walter Jang192a01c2015-09-22 15:23:55 -0700945 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangf5dfea42015-09-16 12:30:36 -0700946 || Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -0700947 // Phone numbers and email addresses are always displayed,
948 // even if they are empty
Walter Jangf5dfea42015-09-16 12:30:36 -0700949 kindSectionView.setHideWhenEmpty(false);
950 }
Walter Jangf5dfea42015-09-16 12:30:36 -0700951
Walter Jang192a01c2015-09-22 15:23:55 -0700952 // Since phone numbers and email addresses displayed even if they are empty,
953 // they will be the only types you add new values to initially for new contacts
954 kindSectionView.setShowOneEmptyEditor(true);
955
956 // Sort so the editors wind up in the order we want
957 if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
958 Collections.sort(kindSectionDataList, new NameEditorComparator(getContext(),
959 mPrimaryRawContactDelta));
960 } else {
961 Collections.sort(kindSectionDataList, new EditorComparator(getContext()));
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700962 }
963
Walter Jang79658e12015-09-24 10:36:26 -0700964 kindSectionView.setState(kindSectionDataList, mViewIdGenerator, mListener);
Walter Jangf5dfea42015-09-16 12:30:36 -0700965
966 return kindSectionView;
Walter Jangcab3dce2015-02-09 17:48:03 -0800967 }
968
Walter Jangd6753152015-10-02 09:23:13 -0700969 private void showAllFields() {
Walter Jang79658e12015-09-24 10:36:26 -0700970 // Stop hiding empty editors and allow the user to enter values for all kinds now
971 for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
972 final CompactKindSectionView kindSectionView =
973 (CompactKindSectionView) mKindSectionViews.getChildAt(i);
974 kindSectionView.setHideWhenEmpty(false);
975 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
976 }
977 mIsExpanded = true;
Walter Jang79658e12015-09-24 10:36:26 -0700978
Walter Jangf5dfea42015-09-16 12:30:36 -0700979 // Hide the more fields button
980 mMoreFields.setVisibility(View.GONE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800981 }
982
Walter Jangbf63a6d2015-05-05 09:14:35 -0700983 private static void vlog(String message) {
984 if (Log.isLoggable(TAG, Log.VERBOSE)) {
985 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -0800986 }
987 }
Walter Jang363d3fd2015-09-16 10:29:07 -0700988
989 private static void wlog(String message) {
990 if (Log.isLoggable(TAG, Log.WARN)) {
991 Log.w(TAG, message);
992 }
993 }
994
995 private static void elog(String message) {
996 Log.e(TAG, message);
997 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800998}