blob: 704fe9d79feec6a23e581a3828f1e5daf807654b [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
249 public View getAggregationAnchorView() {
250 // Since there is only one structured name we can just return it as the anchor for
251 // the aggregation suggestions popup
252 if (mNames.getChildCount() == 0) {
253 return null;
254 }
255 return mNames.getChildAt(0).findViewById(R.id.anchor_view);
256 }
257
Walter Jangf46abd82015-02-20 16:52:04 -0800258 public void setState(RawContactDeltaList rawContactDeltas,
259 MaterialColorMapUtils.MaterialPalette materialPalette,
260 ViewIdGenerator viewIdGenerator) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800261 mNames.removeAllViews();
Walter Jangb1c87622015-02-13 17:51:38 -0800262 mPhoneticNames.removeAllViews();
263 mNicknames.removeAllViews();
Walter Jangcab3dce2015-02-09 17:48:03 -0800264 mPhoneNumbers.removeAllViews();
265 mEmails.removeAllViews();
266 mOther.removeAllViews();
267
268 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
269 return;
270 }
271
272 mViewIdGenerator = viewIdGenerator;
Walter Jangcab3dce2015-02-09 17:48:03 -0800273 setId(mViewIdGenerator.getId(rawContactDeltas.get(0), /* dataKind =*/ null,
274 /* valuesDelta =*/ null, ViewIdGenerator.NO_VIEW_INDEX));
Walter Jangf46abd82015-02-20 16:52:04 -0800275 mMaterialPalette = materialPalette;
Walter Jangcab3dce2015-02-09 17:48:03 -0800276
Walter Jang151f3e62015-02-26 15:29:40 -0800277 addPhotoView(rawContactDeltas, viewIdGenerator);
Walter Jang10446452015-02-20 13:51:16 -0800278 addStructuredNameView(rawContactDeltas);
Walter Jang3efae4a2015-02-18 11:12:00 -0800279 addEditorViews(rawContactDeltas);
Walter Jange720fde2015-02-17 10:54:14 -0800280 removeExtraEmptyTextFields(mPhoneNumbers);
281 removeExtraEmptyTextFields(mEmails);
Walter Jangcab3dce2015-02-09 17:48:03 -0800282 }
283
Walter Jang151f3e62015-02-26 15:29:40 -0800284 private void addPhotoView(RawContactDeltaList rawContactDeltas,
Walter Jange720fde2015-02-17 10:54:14 -0800285 ViewIdGenerator viewIdGenerator) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800286 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang3b210272015-03-04 11:34:11 -0800287 if (!rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800288 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jange720fde2015-02-17 10:54:14 -0800289
Walter Jang3efae4a2015-02-18 11:12:00 -0800290 // Make sure we have a photo
291 RawContactModifier.ensureKindExists(
292 rawContactDelta, accountType, Photo.CONTENT_ITEM_TYPE);
293
Walter Jang3b210272015-03-04 11:34:11 -0800294 // Look for a non-empty super primary photo
Walter Jang3efae4a2015-02-18 11:12:00 -0800295 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
296 if (dataKind != null) {
Walter Jang3b210272015-03-04 11:34:11 -0800297 final ValuesDelta valuesDelta = getNonEmptySuperPrimaryValuesDeltas(
298 rawContactDelta, Photo.CONTENT_ITEM_TYPE, dataKind);
299 if (valuesDelta != null) {
Walter Jang3efae4a2015-02-18 11:12:00 -0800300 mPhotoRawContactId = rawContactDelta.getRawContactId();
Walter Janga5e4bb22015-02-24 13:08:16 -0800301 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
Walter Jang3b210272015-03-04 11:34:11 -0800302 /* readOnly =*/ !dataKind.editable, mMaterialPalette,
303 viewIdGenerator);
Walter Jang3efae4a2015-02-18 11:12:00 -0800304 return;
305 }
306 }
307 }
Walter Jang3b210272015-03-04 11:34:11 -0800308 // We didn't find a non-empty super primary photo, use the first non-empty one
309 for (RawContactDelta rawContactDelta : rawContactDeltas) {
310 if (!rawContactDelta.isVisible()) continue;
311 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
312 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
313 if (dataKind != null) {
314 final List<ValuesDelta> valuesDeltas = getNonEmptyValuesDeltas(
315 rawContactDelta, Photo.CONTENT_ITEM_TYPE, dataKind);
316 if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
317 mPhotoRawContactId = rawContactDelta.getRawContactId();
318 mPhoto.setValues(dataKind, valuesDeltas.get(0), rawContactDelta,
319 /* readOnly =*/ !dataKind.editable, mMaterialPalette,
320 viewIdGenerator);
321 return;
322 }
323 }
324 }
325 // No suitable non-empty photo
326 for (RawContactDelta rawContactDelta : rawContactDeltas) {
327 if (!rawContactDelta.isVisible()) continue;
328 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
329 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
330 if (dataKind != null) {
331 final ValuesDelta valuesDelta = rawContactDelta.getSuperPrimaryEntry(
332 dataKind.mimeType, /* forceSelection =*/ true);
333 if (valuesDelta != null) {
334 mPhotoRawContactId = rawContactDelta.getRawContactId();
335 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
336 /* readOnly =*/ !dataKind.editable, mMaterialPalette,
337 viewIdGenerator);
338 return;
339 }
340 }
341 }
342 // Should not happen since we ensure the kind exists but if we unexpectedly get here
343 // we must remove the photo section so that it does not take up the entire view
344 mPhoto.setVisibility(View.GONE);
Walter Jang3efae4a2015-02-18 11:12:00 -0800345 }
346
Walter Jang10446452015-02-20 13:51:16 -0800347 private void addStructuredNameView(RawContactDeltaList rawContactDeltas) {
348 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang151f3e62015-02-26 15:29:40 -0800349 if (!rawContactDelta.isVisible()) continue;
Walter Jang10446452015-02-20 13:51:16 -0800350 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
351
352 // Make sure we have a structured name
353 RawContactModifier.ensureKindExists(
354 rawContactDelta, accountType, StructuredName.CONTENT_ITEM_TYPE);
355
Walter Jang151f3e62015-02-26 15:29:40 -0800356 // Note use of pseudo mime type to get the DataKind and StructuredName to get value
Walter Jang10446452015-02-20 13:51:16 -0800357 final DataKind dataKind = accountType.getKindForMimetype(
Walter Jang151f3e62015-02-26 15:29:40 -0800358 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
359 if (dataKind == null || !dataKind.editable) continue;
360
361 final ValuesDelta superPrimaryValuesDelta = getNonEmptySuperPrimaryValuesDeltas(
362 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
363 if (superPrimaryValuesDelta != null) {
364 // Our first preference is for a non-empty super primary name
365 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
366 superPrimaryValuesDelta, rawContactDelta, /* nameEditorListener =*/ null));
367 return;
368 }
369 }
370 // We didn't find a super primary name
371 for (RawContactDelta rawContactDelta : rawContactDeltas) {
372 if (!rawContactDelta.isVisible()) continue;
373 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
374
375 final DataKind dataKind = accountType.getKindForMimetype(
376 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
377 if (dataKind == null || !dataKind.editable) continue;
378
379 final List<ValuesDelta> nonEmptyValuesDeltas = getNonEmptyValuesDeltas(
380 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
381 if (nonEmptyValuesDeltas != null && !nonEmptyValuesDeltas.isEmpty()) {
382 // Take the first non-empty name, also make it super primary before expanding to the
383 // full editor (see #onCLick) so that name does not change if the user saves from
384 // the fully expanded editor.
385 mNameValuesDelta = nonEmptyValuesDeltas.get(0);
386 final NameEditorListener nameEditorListener = new NameEditorListener(
387 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
388 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
389 mNameValuesDelta, rawContactDelta, nameEditorListener));
390 return;
391 }
392 }
393 for (RawContactDelta rawContactDelta : rawContactDeltas) {
394 if (!rawContactDelta.isVisible()) continue;
395 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
396
397 final DataKind dataKind = accountType.getKindForMimetype(
398 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
399 if (dataKind == null || !dataKind.editable) continue;
400
401 // Fall back to the first entry
402 final ArrayList<ValuesDelta> valuesDeltas = rawContactDelta.getMimeEntries(
Walter Jang10446452015-02-20 13:51:16 -0800403 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang151f3e62015-02-26 15:29:40 -0800404 if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
405 mNameValuesDelta = valuesDeltas.get(0);
406 final NameEditorListener nameEditorListener = new NameEditorListener(
407 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
408 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
409 mNameValuesDelta, rawContactDelta, nameEditorListener));
410 return;
Walter Jang10446452015-02-20 13:51:16 -0800411 }
412 }
413 }
414
Walter Jang3efae4a2015-02-18 11:12:00 -0800415 private void addEditorViews(RawContactDeltaList rawContactDeltas) {
416 for (RawContactDelta rawContactDelta : rawContactDeltas) {
417 if (!rawContactDelta.isVisible()) {
418 continue;
419 }
420 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
421
Walter Jangcab3dce2015-02-09 17:48:03 -0800422 for (DataKind dataKind : accountType.getSortedDataKinds()) {
423 if (!dataKind.editable) {
424 continue;
425 }
426 final String mimeType = dataKind.mimeType;
427 log(Log.VERBOSE, mimeType + " " + dataKind.fieldList.size() + " field(s)");
428 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jang10446452015-02-20 13:51:16 -0800429 || StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangcab3dce2015-02-09 17:48:03 -0800430 || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800431 // Photos and structured names are handled separately and
432 // group membership is not supported
Walter Jangcab3dce2015-02-09 17:48:03 -0800433 continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800434 } else if (DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800435 // Only add phonetic names if there is a non-empty one. Note the use of
436 // StructuredName mimeType below, even though we matched a pseudo mime type.
Walter Jang3a37a1a2015-03-04 07:41:32 -0800437 final ValuesDelta valuesDelta = rawContactDelta.getSuperPrimaryEntry(
438 StructuredName.CONTENT_ITEM_TYPE, /* forceSelection =*/ true);
439 if (hasNonEmptyValue(dataKind, valuesDelta)) {
440 mPhoneticNames.addView(inflatePhoneticNameEditorView(
441 mPhoneticNames, accountType, valuesDelta, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800442 }
443 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800444 // Only add nicknames if there is a non-empty one
Walter Jangcab3dce2015-02-09 17:48:03 -0800445 if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800446 mNicknames.addView(inflateKindSectionView(
Walter Jangb1c87622015-02-13 17:51:38 -0800447 mNicknames, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800448 }
449 } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jange720fde2015-02-17 10:54:14 -0800450 mPhoneNumbers.addView(inflateKindSectionView(
451 mPhoneNumbers, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800452 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jange720fde2015-02-17 10:54:14 -0800453 mEmails.addView(inflateKindSectionView(
454 mEmails, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800455 } else if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
456 mOther.addView(inflateKindSectionView(
457 mOther, dataKind, rawContactDelta));
458 }
459 }
460 }
461 }
462
Walter Jange720fde2015-02-17 10:54:14 -0800463 // TODO: avoid inflating extra views and deleting them
464 private void removeExtraEmptyTextFields(ViewGroup viewGroup) {
465 // If there is one (or less) editors, leave it whether it is empty or not
466 if (viewGroup.getChildCount() <= 1) {
Walter Jangb1c87622015-02-13 17:51:38 -0800467 return;
468 }
Walter Jange720fde2015-02-17 10:54:14 -0800469 // Determine if there are any non-empty editors
470 boolean hasAtLeastOneNonEmptyEditorView = false;
471 for (int i = 0; i < viewGroup.getChildCount(); i++) {
472 if (!isEmptyEditorView(viewGroup.getChildAt(i))) {
473 hasAtLeastOneNonEmptyEditorView = true;
Walter Jangb1c87622015-02-13 17:51:38 -0800474 break;
475 }
476 }
Walter Jange720fde2015-02-17 10:54:14 -0800477 if (hasAtLeastOneNonEmptyEditorView) {
478 // There is at least one non-empty editor, remove all the empty ones
479 for (int i = 0; i < viewGroup.getChildCount(); i++) {
480 if (isEmptyEditorView(viewGroup.getChildAt(i))) {
481 viewGroup.getChildAt(i).setVisibility(View.GONE);
Walter Jangb1c87622015-02-13 17:51:38 -0800482 }
483 }
484 } else {
Walter Jange720fde2015-02-17 10:54:14 -0800485 // There is no non-empty editor, keep the first empty view and remove the rest
486 for (int i = 1; i < viewGroup.getChildCount(); i++) {
487 viewGroup.getChildAt(i).setVisibility(View.GONE);
Walter Jangb1c87622015-02-13 17:51:38 -0800488 }
489 }
490 }
491
Walter Jange720fde2015-02-17 10:54:14 -0800492 private static boolean isEmptyEditorView(View view) {
493 if (view instanceof TextFieldsEditorView) {
494 final TextFieldsEditorView textFieldsEditorView = (TextFieldsEditorView) view;
495 return textFieldsEditorView.isEmpty();
496 }
497 if (view instanceof KindSectionView) {
498 final KindSectionView kindSectionView = (KindSectionView) view;
499 return kindSectionView.hasEmptyEditor();
500 }
501 return false;
502 }
503
Walter Jangcab3dce2015-02-09 17:48:03 -0800504 private static boolean hasNonEmptyValuesDelta(RawContactDelta rawContactDelta,
505 String mimeType, DataKind dataKind) {
506 return !getNonEmptyValuesDeltas(rawContactDelta, mimeType, dataKind).isEmpty();
507 }
508
Walter Jang151f3e62015-02-26 15:29:40 -0800509 private static ValuesDelta getNonEmptySuperPrimaryValuesDeltas(RawContactDelta rawContactDelta,
510 String mimeType, DataKind dataKind) {
511 for (ValuesDelta valuesDelta : getNonEmptyValuesDeltas(
512 rawContactDelta, mimeType, dataKind)) {
513 if (valuesDelta.isSuperPrimary()) {
514 return valuesDelta;
515 }
516 }
517 return null;
518 }
519
520 static List<ValuesDelta> getNonEmptyValuesDeltas(RawContactDelta rawContactDelta,
Walter Jangcab3dce2015-02-09 17:48:03 -0800521 String mimeType, DataKind dataKind) {
522 final List<ValuesDelta> result = new ArrayList<>();
523 if (rawContactDelta == null) {
524 log(Log.VERBOSE, "Null RawContactDelta");
525 return result;
526 }
527 if (!rawContactDelta.hasMimeEntries(mimeType)) {
528 log(Log.VERBOSE, "No ValueDeltas");
529 return result;
530 }
531 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(mimeType)) {
Walter Jang3a37a1a2015-03-04 07:41:32 -0800532 if (hasNonEmptyValue(dataKind, valuesDelta)) {
533 result.add(valuesDelta);
Walter Jangcab3dce2015-02-09 17:48:03 -0800534 }
535 }
536 return result;
537 }
538
Walter Jang3a37a1a2015-03-04 07:41:32 -0800539 private static boolean hasNonEmptyValue(DataKind dataKind, ValuesDelta valuesDelta) {
540 if (valuesDelta == null) {
541 log(Log.VERBOSE, "Null valuesDelta");
542 return false;
543 }
544 for (EditField editField : dataKind.fieldList) {
545 final String column = editField.column;
546 final String value = valuesDelta == null ? null : valuesDelta.getAsString(column);
547 log(Log.VERBOSE, "Field " + column + " empty=" + TextUtils.isEmpty(value) +
548 " value=" + value);
549 if (!TextUtils.isEmpty(value)) {
550 return true;
551 }
552 }
553 return false;
554 }
555
Walter Jangcab3dce2015-02-09 17:48:03 -0800556 private StructuredNameEditorView inflateStructuredNameEditorView(ViewGroup viewGroup,
Walter Jang151f3e62015-02-26 15:29:40 -0800557 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta,
558 NameEditorListener nameEditorListener) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800559 final StructuredNameEditorView result = (StructuredNameEditorView) mLayoutInflater.inflate(
560 R.layout.structured_name_editor_view, viewGroup, /* attachToRoot =*/ false);
Walter Jang151f3e62015-02-26 15:29:40 -0800561 if (nameEditorListener != null) {
562 result.setEditorListener(nameEditorListener);
563 }
Walter Jang3a37a1a2015-03-04 07:41:32 -0800564 result.setDeletable(false);
Walter Jangcab3dce2015-02-09 17:48:03 -0800565 result.setValues(
566 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME),
567 valuesDelta,
568 rawContactDelta,
569 /* readOnly =*/ false,
570 mViewIdGenerator);
571 return result;
572 }
573
574 private PhoneticNameEditorView inflatePhoneticNameEditorView(ViewGroup viewGroup,
575 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta) {
576 final PhoneticNameEditorView result = (PhoneticNameEditorView) mLayoutInflater.inflate(
577 R.layout.phonetic_name_editor_view, viewGroup, /* attachToRoot =*/ false);
Walter Jang3a37a1a2015-03-04 07:41:32 -0800578 result.setDeletable(false);
Walter Jangcab3dce2015-02-09 17:48:03 -0800579 result.setValues(
580 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME),
581 valuesDelta,
582 rawContactDelta,
583 /* readOnly =*/ false,
584 mViewIdGenerator);
585 return result;
586 }
587
Walter Jangcab3dce2015-02-09 17:48:03 -0800588 private KindSectionView inflateKindSectionView(ViewGroup viewGroup, DataKind dataKind,
589 RawContactDelta rawContactDelta) {
590 final KindSectionView result = (KindSectionView) mLayoutInflater.inflate(
591 R.layout.item_kind_section, viewGroup, /* attachToRoot =*/ false);
592 result.setState(
593 dataKind,
594 rawContactDelta,
595 /* readOnly =*/ false,
Walter Jang10446452015-02-20 13:51:16 -0800596 /* showOneEmptyEditor =*/ false,
Walter Jangcab3dce2015-02-09 17:48:03 -0800597 mViewIdGenerator);
598 return result;
599 }
600
601 private static void log(int level, String message) {
602 log(TAG, level, message);
603 }
604
605 private static void log(String tag, int level, String message) {
606 if (Log.isLoggable(tag, level)) {
607 switch (level) {
608 case Log.VERBOSE:
609 Log.v(tag, message);
610 break;
611 case Log.DEBUG:
612 Log.d(tag, message);
613 break;
614 case Log.INFO:
615 Log.i(tag, message);
616 break;
617 case Log.WARN:
618 Log.w(tag, message);
619 break;
620 case Log.ERROR:
621 Log.e(tag, message);
622 break;
623 default:
624 Log.v(tag, message);
625 break;
626 }
627 }
628 }
629}