blob: 36502e2a2d4603bb6764f3744e213ae1e711fd8c [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;
20import com.android.contacts.common.model.AccountTypeManager;
21import com.android.contacts.common.model.RawContactDelta;
22import com.android.contacts.common.model.RawContactDeltaList;
Walter Jange720fde2015-02-17 10:54:14 -080023import com.android.contacts.common.model.RawContactModifier;
Walter Jangcab3dce2015-02-09 17:48:03 -080024import com.android.contacts.common.model.ValuesDelta;
25import com.android.contacts.common.model.account.AccountType;
26import com.android.contacts.common.model.account.AccountType.EditField;
27import com.android.contacts.common.model.dataitem.DataKind;
Walter Jangf46abd82015-02-20 16:52:04 -080028import com.android.contacts.common.util.MaterialColorMapUtils;
Walter Jang3efae4a2015-02-18 11:12:00 -080029import com.android.contacts.editor.CompactContactEditorFragment.PhotoHandler;
Walter Jangcab3dce2015-02-09 17:48:03 -080030
31import android.content.Context;
Walter Jang3efae4a2015-02-18 11:12:00 -080032import android.graphics.Bitmap;
Walter Jang41b3ea12015-03-09 17:30:06 -070033import android.net.Uri;
Walter Jangcab3dce2015-02-09 17:48:03 -080034import android.provider.ContactsContract.CommonDataKinds.Email;
35import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Walter Jang3efae4a2015-02-18 11:12:00 -080036import android.provider.ContactsContract.CommonDataKinds.Nickname;
Walter Jangcab3dce2015-02-09 17:48:03 -080037import android.provider.ContactsContract.CommonDataKinds.Phone;
38import android.provider.ContactsContract.CommonDataKinds.Photo;
Walter Jangcab3dce2015-02-09 17:48:03 -080039import android.provider.ContactsContract.CommonDataKinds.StructuredName;
40import android.text.TextUtils;
41import android.util.AttributeSet;
42import android.util.Log;
43import android.view.LayoutInflater;
Walter Jangb1c87622015-02-13 17:51:38 -080044import android.view.View;
Walter Jangcab3dce2015-02-09 17:48:03 -080045import android.view.ViewGroup;
46import android.widget.LinearLayout;
47
48import java.util.ArrayList;
49import java.util.List;
50
51/**
52 * View to display information from multiple {@link RawContactDelta}s grouped together
53 * (e.g. all the phone numbers from a {@link com.android.contacts.common.model.Contact} together.
54 */
Walter Jangb6ca2722015-02-20 11:10:25 -080055public class CompactRawContactsEditorView extends LinearLayout implements View.OnClickListener {
Walter Jangcab3dce2015-02-09 17:48:03 -080056
57 private static final String TAG = "CompactEditorView";
58
Walter Jangb6ca2722015-02-20 11:10:25 -080059 /**
60 * Callbacks for hosts of {@link CompactRawContactsEditorView}s.
61 */
62 public interface Listener {
63
64 /**
65 * Invoked when the compact editor should be expanded to show all fields.
66 */
67 public void onExpandEditor();
Walter Jang151f3e62015-02-26 15:29:40 -080068
69 /**
70 * Invoked when the structured name editor field has changed.
71 *
72 * @param rawContactId The raw contact ID from the underlying {@link RawContactDelta}.
73 * @param valuesDelta The values from the underlying {@link RawContactDelta}.
74 */
75 public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta);
76 }
77
78 /**
79 * Marks a name as super primary when it is changed.
80 *
81 * This is for the case when two or more raw contacts with names are joined where neither is
82 * marked as super primary. If the user hits back (which causes a save) after changing the
83 * name that was arbitrarily displayed, we want that to be the name that is used.
84 *
85 * Should only be set when a super primary name does not already exist since we only show
86 * one name field.
87 */
88 static final class NameEditorListener implements Editor.EditorListener {
89
90 private final ValuesDelta mValuesDelta;
91 private final long mRawContactId;
92 private final Listener mListener;
93
94 public NameEditorListener(ValuesDelta valuesDelta, long rawContactId,
95 Listener listener) {
96 mValuesDelta = valuesDelta;
97 mRawContactId = rawContactId;
98 mListener = listener;
99 }
100
101 @Override
102 public void onRequest(int request) {
103 if (request == Editor.EditorListener.FIELD_CHANGED) {
104 mValuesDelta.setSuperPrimary(true);
105 if (mListener != null) {
106 mListener.onNameFieldChanged(mRawContactId, mValuesDelta);
107 }
108 } else if (request == Editor.EditorListener.FIELD_TURNED_EMPTY) {
109 mValuesDelta.setSuperPrimary(false);
110 }
111 }
112
113 @Override
114 public void onDeleteRequested(Editor editor) {
115 }
Walter Jangb6ca2722015-02-20 11:10:25 -0800116 }
117
118 private Listener mListener;
119
Walter Jangcab3dce2015-02-09 17:48:03 -0800120 private AccountTypeManager mAccountTypeManager;
121 private LayoutInflater mLayoutInflater;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800122
Walter Jangcab3dce2015-02-09 17:48:03 -0800123 private ViewIdGenerator mViewIdGenerator;
Walter Jangf46abd82015-02-20 16:52:04 -0800124 private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
Walter Jangcab3dce2015-02-09 17:48:03 -0800125
Walter Janga5e4bb22015-02-24 13:08:16 -0800126 private CompactPhotoEditorView mPhoto;
Walter Jangcab3dce2015-02-09 17:48:03 -0800127 private ViewGroup mNames;
Walter Jangb1c87622015-02-13 17:51:38 -0800128 private ViewGroup mPhoneticNames;
129 private ViewGroup mNicknames;
Walter Jangcab3dce2015-02-09 17:48:03 -0800130 private ViewGroup mPhoneNumbers;
131 private ViewGroup mEmails;
132 private ViewGroup mOther;
Walter Jangb6ca2722015-02-20 11:10:25 -0800133 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -0800134
Walter Jang151f3e62015-02-26 15:29:40 -0800135 // The ValuesDelta for the non super primary name that was displayed to the user.
136 private ValuesDelta mNameValuesDelta;
137
Walter Jang3efae4a2015-02-18 11:12:00 -0800138 private long mPhotoRawContactId;
139
Walter Jangcab3dce2015-02-09 17:48:03 -0800140 public CompactRawContactsEditorView(Context context) {
141 super(context);
142 }
143
144 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
145 super(context, attrs);
146 }
147
Walter Jangb6ca2722015-02-20 11:10:25 -0800148 /**
149 * Sets the receiver for {@link CompactRawContactsEditorView} callbacks.
150 */
151 public void setListener(Listener listener) {
152 mListener = listener;
153 }
154
Walter Jangcab3dce2015-02-09 17:48:03 -0800155 @Override
156 protected void onFinishInflate() {
157 super.onFinishInflate();
158
159 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
160 mLayoutInflater = (LayoutInflater)
161 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
162
Walter Janga5e4bb22015-02-24 13:08:16 -0800163 mPhoto = (CompactPhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangcab3dce2015-02-09 17:48:03 -0800164 mNames = (LinearLayout) findViewById(R.id.names);
Walter Jangb1c87622015-02-13 17:51:38 -0800165 mPhoneticNames = (LinearLayout) findViewById(R.id.phonetic_names);
166 mNicknames = (LinearLayout) findViewById(R.id.nicknames);
Walter Jangcab3dce2015-02-09 17:48:03 -0800167 mPhoneNumbers = (LinearLayout) findViewById(R.id.phone_numbers);
168 mEmails = (LinearLayout) findViewById(R.id.emails);
169 mOther = (LinearLayout) findViewById(R.id.other);
Walter Jangb6ca2722015-02-20 11:10:25 -0800170 mMoreFields = findViewById(R.id.more_fields);
171 mMoreFields.setOnClickListener(this);
172 }
173
Walter Jangb6ca2722015-02-20 11:10:25 -0800174 @Override
175 public void onClick(View view) {
176 if (view.getId() == R.id.more_fields && mListener != null ) {
Walter Jang151f3e62015-02-26 15:29:40 -0800177 // We mark the name that was displayed as super primary before expanding
178 // so that a save on the expanded editor (without a name change) does not
179 // cause the displayed name to change.
180 if (mNameValuesDelta != null) {
181 mNameValuesDelta.setSuperPrimary(true);
182 }
183
Walter Jangb6ca2722015-02-20 11:10:25 -0800184 mListener.onExpandEditor();
185 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800186 }
187
188 @Override
189 public void setEnabled(boolean enabled) {
190 super.setEnabled(enabled);
191 setEnabled(enabled, mNames);
Walter Jangb1c87622015-02-13 17:51:38 -0800192 setEnabled(enabled, mPhoneticNames);
193 setEnabled(enabled, mNicknames);
Walter Jangcab3dce2015-02-09 17:48:03 -0800194 setEnabled(enabled, mPhoneNumbers);
195 setEnabled(enabled, mEmails);
196 setEnabled(enabled, mOther);
197 }
198
199 private void setEnabled(boolean enabled, ViewGroup viewGroup) {
200 if (viewGroup != null) {
201 final int childCount = viewGroup.getChildCount();
202 for (int i = 0; i < childCount; i++) {
203 viewGroup.getChildAt(i).setEnabled(enabled);
204 }
205 }
206 }
207
Walter Jang3efae4a2015-02-18 11:12:00 -0800208 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800209 * Pass through to {@link CompactPhotoEditorView#setPhotoHandler}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800210 */
211 public void setPhotoHandler(PhotoHandler photoHandler) {
Walter Janga5e4bb22015-02-24 13:08:16 -0800212 mPhoto.setPhotoHandler(photoHandler);
Walter Jang3efae4a2015-02-18 11:12:00 -0800213 }
214
215 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800216 * Pass through to {@link CompactPhotoEditorView#setPhoto}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800217 */
218 public void setPhoto(Bitmap bitmap) {
Walter Janga5e4bb22015-02-24 13:08:16 -0800219 mPhoto.setPhoto(bitmap);
Walter Jang3efae4a2015-02-18 11:12:00 -0800220 }
221
222 /**
Walter Jang41b3ea12015-03-09 17:30:06 -0700223 * Pass through to {@link CompactPhotoEditorView#setFullSizedPhoto(Uri)}.
224 */
225 public void setFullSizePhoto(Uri photoUri) {
226 mPhoto.setFullSizedPhoto(photoUri);
227 }
228
229 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800230 * Pass through to {@link CompactPhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800231 */
232 public boolean isWritablePhotoSet() {
Walter Janga5e4bb22015-02-24 13:08:16 -0800233 return mPhoto.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800234 }
235
236 /**
Walter Jang3efae4a2015-02-18 11:12:00 -0800237 * Get the raw contact ID for the CompactHeaderView photo.
238 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800239 public long getPhotoRawContactId() {
240 return mPhotoRawContactId;
241 }
242
Walter Jangd35e5ef2015-02-24 09:18:16 -0800243 public StructuredNameEditorView getStructuredNameEditorView() {
244 // We only ever show one StructuredName
245 return mNames.getChildCount() == 0
246 ? null : (StructuredNameEditorView) mNames.getChildAt(0);
247 }
248
Walter Jangbf63a6d2015-05-05 09:14:35 -0700249 public PhoneticNameEditorView getFirstPhoneticNameEditorView() {
250 // There should only ever be one phonetic name
251 return mPhoneticNames.getChildCount() == 0
252 ? null : (PhoneticNameEditorView) mPhoneticNames.getChildAt(0);
253 }
254
Walter Jangd35e5ef2015-02-24 09:18:16 -0800255 public View getAggregationAnchorView() {
256 // Since there is only one structured name we can just return it as the anchor for
257 // the aggregation suggestions popup
258 if (mNames.getChildCount() == 0) {
259 return null;
260 }
261 return mNames.getChildAt(0).findViewById(R.id.anchor_view);
262 }
263
Walter Jangf46abd82015-02-20 16:52:04 -0800264 public void setState(RawContactDeltaList rawContactDeltas,
265 MaterialColorMapUtils.MaterialPalette materialPalette,
266 ViewIdGenerator viewIdGenerator) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800267 mNames.removeAllViews();
Walter Jangb1c87622015-02-13 17:51:38 -0800268 mPhoneticNames.removeAllViews();
269 mNicknames.removeAllViews();
Walter Jangcab3dce2015-02-09 17:48:03 -0800270 mPhoneNumbers.removeAllViews();
271 mEmails.removeAllViews();
272 mOther.removeAllViews();
273
274 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
275 return;
276 }
277
278 mViewIdGenerator = viewIdGenerator;
Walter Jangcab3dce2015-02-09 17:48:03 -0800279 setId(mViewIdGenerator.getId(rawContactDeltas.get(0), /* dataKind =*/ null,
280 /* valuesDelta =*/ null, ViewIdGenerator.NO_VIEW_INDEX));
Walter Jangf46abd82015-02-20 16:52:04 -0800281 mMaterialPalette = materialPalette;
Walter Jangcab3dce2015-02-09 17:48:03 -0800282
Walter Jangbf63a6d2015-05-05 09:14:35 -0700283 vlog("Setting compact editor state from " + rawContactDeltas);
Walter Jang151f3e62015-02-26 15:29:40 -0800284 addPhotoView(rawContactDeltas, viewIdGenerator);
Walter Jang10446452015-02-20 13:51:16 -0800285 addStructuredNameView(rawContactDeltas);
Walter Jang3efae4a2015-02-18 11:12:00 -0800286 addEditorViews(rawContactDeltas);
Walter Jange720fde2015-02-17 10:54:14 -0800287 removeExtraEmptyTextFields(mPhoneNumbers);
288 removeExtraEmptyTextFields(mEmails);
Walter Jangcab3dce2015-02-09 17:48:03 -0800289 }
290
Walter Jang151f3e62015-02-26 15:29:40 -0800291 private void addPhotoView(RawContactDeltaList rawContactDeltas,
Walter Jange720fde2015-02-17 10:54:14 -0800292 ViewIdGenerator viewIdGenerator) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800293 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang3b210272015-03-04 11:34:11 -0800294 if (!rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800295 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jange720fde2015-02-17 10:54:14 -0800296
Walter Jang3efae4a2015-02-18 11:12:00 -0800297 // Make sure we have a photo
298 RawContactModifier.ensureKindExists(
299 rawContactDelta, accountType, Photo.CONTENT_ITEM_TYPE);
300
Walter Jang3b210272015-03-04 11:34:11 -0800301 // Look for a non-empty super primary photo
Walter Jang3efae4a2015-02-18 11:12:00 -0800302 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
303 if (dataKind != null) {
Walter Jang3b210272015-03-04 11:34:11 -0800304 final ValuesDelta valuesDelta = getNonEmptySuperPrimaryValuesDeltas(
305 rawContactDelta, Photo.CONTENT_ITEM_TYPE, dataKind);
306 if (valuesDelta != null) {
Walter Jang3efae4a2015-02-18 11:12:00 -0800307 mPhotoRawContactId = rawContactDelta.getRawContactId();
Walter Janga5e4bb22015-02-24 13:08:16 -0800308 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
Walter Jang3b210272015-03-04 11:34:11 -0800309 /* readOnly =*/ !dataKind.editable, mMaterialPalette,
310 viewIdGenerator);
Walter Jang3efae4a2015-02-18 11:12:00 -0800311 return;
312 }
313 }
314 }
Walter Jang3b210272015-03-04 11:34:11 -0800315 // We didn't find a non-empty super primary photo, use the first non-empty one
316 for (RawContactDelta rawContactDelta : rawContactDeltas) {
317 if (!rawContactDelta.isVisible()) continue;
318 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
319 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
320 if (dataKind != null) {
321 final List<ValuesDelta> valuesDeltas = getNonEmptyValuesDeltas(
322 rawContactDelta, Photo.CONTENT_ITEM_TYPE, dataKind);
323 if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
324 mPhotoRawContactId = rawContactDelta.getRawContactId();
325 mPhoto.setValues(dataKind, valuesDeltas.get(0), rawContactDelta,
326 /* readOnly =*/ !dataKind.editable, mMaterialPalette,
327 viewIdGenerator);
328 return;
329 }
330 }
331 }
332 // No suitable non-empty photo
333 for (RawContactDelta rawContactDelta : rawContactDeltas) {
334 if (!rawContactDelta.isVisible()) continue;
335 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
336 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
337 if (dataKind != null) {
338 final ValuesDelta valuesDelta = rawContactDelta.getSuperPrimaryEntry(
339 dataKind.mimeType, /* forceSelection =*/ true);
340 if (valuesDelta != null) {
341 mPhotoRawContactId = rawContactDelta.getRawContactId();
342 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
343 /* readOnly =*/ !dataKind.editable, mMaterialPalette,
344 viewIdGenerator);
345 return;
346 }
347 }
348 }
349 // Should not happen since we ensure the kind exists but if we unexpectedly get here
350 // we must remove the photo section so that it does not take up the entire view
351 mPhoto.setVisibility(View.GONE);
Walter Jang3efae4a2015-02-18 11:12:00 -0800352 }
353
Walter Jang10446452015-02-20 13:51:16 -0800354 private void addStructuredNameView(RawContactDeltaList rawContactDeltas) {
355 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang151f3e62015-02-26 15:29:40 -0800356 if (!rawContactDelta.isVisible()) continue;
Walter Jang10446452015-02-20 13:51:16 -0800357 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
358
359 // Make sure we have a structured name
360 RawContactModifier.ensureKindExists(
361 rawContactDelta, accountType, StructuredName.CONTENT_ITEM_TYPE);
362
Walter Jang151f3e62015-02-26 15:29:40 -0800363 // Note use of pseudo mime type to get the DataKind and StructuredName to get value
Walter Jang10446452015-02-20 13:51:16 -0800364 final DataKind dataKind = accountType.getKindForMimetype(
Walter Jang151f3e62015-02-26 15:29:40 -0800365 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
366 if (dataKind == null || !dataKind.editable) continue;
367
368 final ValuesDelta superPrimaryValuesDelta = getNonEmptySuperPrimaryValuesDeltas(
369 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
370 if (superPrimaryValuesDelta != null) {
371 // Our first preference is for a non-empty super primary name
372 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
373 superPrimaryValuesDelta, rawContactDelta, /* nameEditorListener =*/ null));
374 return;
375 }
376 }
377 // We didn't find a super primary name
378 for (RawContactDelta rawContactDelta : rawContactDeltas) {
379 if (!rawContactDelta.isVisible()) continue;
380 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
381
382 final DataKind dataKind = accountType.getKindForMimetype(
383 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
384 if (dataKind == null || !dataKind.editable) continue;
385
386 final List<ValuesDelta> nonEmptyValuesDeltas = getNonEmptyValuesDeltas(
387 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
388 if (nonEmptyValuesDeltas != null && !nonEmptyValuesDeltas.isEmpty()) {
389 // Take the first non-empty name, also make it super primary before expanding to the
390 // full editor (see #onCLick) so that name does not change if the user saves from
391 // the fully expanded editor.
392 mNameValuesDelta = nonEmptyValuesDeltas.get(0);
393 final NameEditorListener nameEditorListener = new NameEditorListener(
394 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
395 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
396 mNameValuesDelta, rawContactDelta, nameEditorListener));
397 return;
398 }
399 }
400 for (RawContactDelta rawContactDelta : rawContactDeltas) {
401 if (!rawContactDelta.isVisible()) continue;
402 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
403
404 final DataKind dataKind = accountType.getKindForMimetype(
405 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
406 if (dataKind == null || !dataKind.editable) continue;
407
408 // Fall back to the first entry
409 final ArrayList<ValuesDelta> valuesDeltas = rawContactDelta.getMimeEntries(
Walter Jang10446452015-02-20 13:51:16 -0800410 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang151f3e62015-02-26 15:29:40 -0800411 if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
412 mNameValuesDelta = valuesDeltas.get(0);
413 final NameEditorListener nameEditorListener = new NameEditorListener(
414 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
415 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
416 mNameValuesDelta, rawContactDelta, nameEditorListener));
417 return;
Walter Jang10446452015-02-20 13:51:16 -0800418 }
419 }
420 }
421
Walter Jang3efae4a2015-02-18 11:12:00 -0800422 private void addEditorViews(RawContactDeltaList rawContactDeltas) {
423 for (RawContactDelta rawContactDelta : rawContactDeltas) {
424 if (!rawContactDelta.isVisible()) {
425 continue;
426 }
427 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
428
Walter Jangcab3dce2015-02-09 17:48:03 -0800429 for (DataKind dataKind : accountType.getSortedDataKinds()) {
430 if (!dataKind.editable) {
431 continue;
432 }
433 final String mimeType = dataKind.mimeType;
Walter Jangbf63a6d2015-05-05 09:14:35 -0700434 vlog(mimeType + " " + dataKind.fieldList.size() + " field(s)");
Walter Jangcab3dce2015-02-09 17:48:03 -0800435 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jang10446452015-02-20 13:51:16 -0800436 || StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangcab3dce2015-02-09 17:48:03 -0800437 || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800438 // Photos and structured names are handled separately and
439 // group membership is not supported
Walter Jangcab3dce2015-02-09 17:48:03 -0800440 continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800441 } else if (DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800442 // Only add phonetic names if there is a non-empty one. Note the use of
443 // StructuredName mimeType below, even though we matched a pseudo mime type.
Walter Jang3a37a1a2015-03-04 07:41:32 -0800444 final ValuesDelta valuesDelta = rawContactDelta.getSuperPrimaryEntry(
445 StructuredName.CONTENT_ITEM_TYPE, /* forceSelection =*/ true);
446 if (hasNonEmptyValue(dataKind, valuesDelta)) {
447 mPhoneticNames.addView(inflatePhoneticNameEditorView(
448 mPhoneticNames, accountType, valuesDelta, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800449 }
450 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800451 // Only add nicknames if there is a non-empty one
Walter Jangcab3dce2015-02-09 17:48:03 -0800452 if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800453 mNicknames.addView(inflateKindSectionView(
Walter Jangb1c87622015-02-13 17:51:38 -0800454 mNicknames, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800455 }
456 } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jange720fde2015-02-17 10:54:14 -0800457 mPhoneNumbers.addView(inflateKindSectionView(
458 mPhoneNumbers, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800459 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jange720fde2015-02-17 10:54:14 -0800460 mEmails.addView(inflateKindSectionView(
461 mEmails, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800462 } else if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
463 mOther.addView(inflateKindSectionView(
464 mOther, dataKind, rawContactDelta));
465 }
466 }
467 }
468 }
469
Walter Jange720fde2015-02-17 10:54:14 -0800470 // TODO: avoid inflating extra views and deleting them
471 private void removeExtraEmptyTextFields(ViewGroup viewGroup) {
472 // If there is one (or less) editors, leave it whether it is empty or not
473 if (viewGroup.getChildCount() <= 1) {
Walter Jangb1c87622015-02-13 17:51:38 -0800474 return;
475 }
Walter Jange720fde2015-02-17 10:54:14 -0800476 // Determine if there are any non-empty editors
477 boolean hasAtLeastOneNonEmptyEditorView = false;
478 for (int i = 0; i < viewGroup.getChildCount(); i++) {
479 if (!isEmptyEditorView(viewGroup.getChildAt(i))) {
480 hasAtLeastOneNonEmptyEditorView = true;
Walter Jangb1c87622015-02-13 17:51:38 -0800481 break;
482 }
483 }
Walter Jange720fde2015-02-17 10:54:14 -0800484 if (hasAtLeastOneNonEmptyEditorView) {
485 // There is at least one non-empty editor, remove all the empty ones
486 for (int i = 0; i < viewGroup.getChildCount(); i++) {
487 if (isEmptyEditorView(viewGroup.getChildAt(i))) {
488 viewGroup.getChildAt(i).setVisibility(View.GONE);
Walter Jangb1c87622015-02-13 17:51:38 -0800489 }
490 }
491 } else {
Walter Jange720fde2015-02-17 10:54:14 -0800492 // There is no non-empty editor, keep the first empty view and remove the rest
493 for (int i = 1; i < viewGroup.getChildCount(); i++) {
494 viewGroup.getChildAt(i).setVisibility(View.GONE);
Walter Jangb1c87622015-02-13 17:51:38 -0800495 }
496 }
497 }
498
Walter Jange720fde2015-02-17 10:54:14 -0800499 private static boolean isEmptyEditorView(View view) {
500 if (view instanceof TextFieldsEditorView) {
501 final TextFieldsEditorView textFieldsEditorView = (TextFieldsEditorView) view;
502 return textFieldsEditorView.isEmpty();
503 }
504 if (view instanceof KindSectionView) {
505 final KindSectionView kindSectionView = (KindSectionView) view;
506 return kindSectionView.hasEmptyEditor();
507 }
508 return false;
509 }
510
Walter Jangcab3dce2015-02-09 17:48:03 -0800511 private static boolean hasNonEmptyValuesDelta(RawContactDelta rawContactDelta,
512 String mimeType, DataKind dataKind) {
513 return !getNonEmptyValuesDeltas(rawContactDelta, mimeType, dataKind).isEmpty();
514 }
515
Walter Jang151f3e62015-02-26 15:29:40 -0800516 private static ValuesDelta getNonEmptySuperPrimaryValuesDeltas(RawContactDelta rawContactDelta,
517 String mimeType, DataKind dataKind) {
518 for (ValuesDelta valuesDelta : getNonEmptyValuesDeltas(
519 rawContactDelta, mimeType, dataKind)) {
520 if (valuesDelta.isSuperPrimary()) {
521 return valuesDelta;
522 }
523 }
524 return null;
525 }
526
527 static List<ValuesDelta> getNonEmptyValuesDeltas(RawContactDelta rawContactDelta,
Walter Jangcab3dce2015-02-09 17:48:03 -0800528 String mimeType, DataKind dataKind) {
529 final List<ValuesDelta> result = new ArrayList<>();
530 if (rawContactDelta == null) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700531 vlog("Null RawContactDelta");
Walter Jangcab3dce2015-02-09 17:48:03 -0800532 return result;
533 }
534 if (!rawContactDelta.hasMimeEntries(mimeType)) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700535 vlog("No ValueDeltas");
Walter Jangcab3dce2015-02-09 17:48:03 -0800536 return result;
537 }
538 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(mimeType)) {
Walter Jang3a37a1a2015-03-04 07:41:32 -0800539 if (hasNonEmptyValue(dataKind, valuesDelta)) {
540 result.add(valuesDelta);
Walter Jangcab3dce2015-02-09 17:48:03 -0800541 }
542 }
543 return result;
544 }
545
Walter Jang3a37a1a2015-03-04 07:41:32 -0800546 private static boolean hasNonEmptyValue(DataKind dataKind, ValuesDelta valuesDelta) {
547 if (valuesDelta == null) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700548 vlog("Null valuesDelta");
Walter Jang3a37a1a2015-03-04 07:41:32 -0800549 return false;
550 }
551 for (EditField editField : dataKind.fieldList) {
552 final String column = editField.column;
553 final String value = valuesDelta == null ? null : valuesDelta.getAsString(column);
Walter Jangbf63a6d2015-05-05 09:14:35 -0700554 vlog("Field " + column + " empty=" + TextUtils.isEmpty(value) + " value=" + value);
Walter Jang3a37a1a2015-03-04 07:41:32 -0800555 if (!TextUtils.isEmpty(value)) {
556 return true;
557 }
558 }
559 return false;
560 }
561
Walter Jangcab3dce2015-02-09 17:48:03 -0800562 private StructuredNameEditorView inflateStructuredNameEditorView(ViewGroup viewGroup,
Walter Jang151f3e62015-02-26 15:29:40 -0800563 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta,
564 NameEditorListener nameEditorListener) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800565 final StructuredNameEditorView result = (StructuredNameEditorView) mLayoutInflater.inflate(
566 R.layout.structured_name_editor_view, viewGroup, /* attachToRoot =*/ false);
Walter Jang151f3e62015-02-26 15:29:40 -0800567 if (nameEditorListener != null) {
568 result.setEditorListener(nameEditorListener);
569 }
Walter Jang3a37a1a2015-03-04 07:41:32 -0800570 result.setDeletable(false);
Walter Jangcab3dce2015-02-09 17:48:03 -0800571 result.setValues(
572 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME),
573 valuesDelta,
574 rawContactDelta,
575 /* readOnly =*/ false,
576 mViewIdGenerator);
577 return result;
578 }
579
580 private PhoneticNameEditorView inflatePhoneticNameEditorView(ViewGroup viewGroup,
581 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta) {
582 final PhoneticNameEditorView result = (PhoneticNameEditorView) mLayoutInflater.inflate(
583 R.layout.phonetic_name_editor_view, viewGroup, /* attachToRoot =*/ false);
Walter Jang3a37a1a2015-03-04 07:41:32 -0800584 result.setDeletable(false);
Walter Jangcab3dce2015-02-09 17:48:03 -0800585 result.setValues(
586 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME),
587 valuesDelta,
588 rawContactDelta,
589 /* readOnly =*/ false,
590 mViewIdGenerator);
591 return result;
592 }
593
Walter Jangcab3dce2015-02-09 17:48:03 -0800594 private KindSectionView inflateKindSectionView(ViewGroup viewGroup, DataKind dataKind,
595 RawContactDelta rawContactDelta) {
596 final KindSectionView result = (KindSectionView) mLayoutInflater.inflate(
597 R.layout.item_kind_section, viewGroup, /* attachToRoot =*/ false);
598 result.setState(
599 dataKind,
600 rawContactDelta,
601 /* readOnly =*/ false,
Walter Jang10446452015-02-20 13:51:16 -0800602 /* showOneEmptyEditor =*/ false,
Walter Jangcab3dce2015-02-09 17:48:03 -0800603 mViewIdGenerator);
604 return result;
605 }
606
Walter Jangbf63a6d2015-05-05 09:14:35 -0700607 private static void vlog(String message) {
608 if (Log.isLoggable(TAG, Log.VERBOSE)) {
609 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -0800610 }
611 }
612}