blob: b841f62a8eb25090cbf379c2201e939ad47ed258 [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,
Walter Jangac679af2015-06-01 12:17:06 -0700266 ViewIdGenerator viewIdGenerator, long photoId) {
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 Jangac679af2015-06-01 12:17:06 -0700284 addPhotoView(rawContactDeltas, viewIdGenerator, photoId);
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 Jangac679af2015-06-01 12:17:06 -0700292 ViewIdGenerator viewIdGenerator, long photoId) {
293 // Look for a match for the photo ID that was passed in
Walter Jangcab3dce2015-02-09 17:48:03 -0800294 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang3b210272015-03-04 11:34:11 -0800295 if (!rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800296 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jange720fde2015-02-17 10:54:14 -0800297
Walter Jang3efae4a2015-02-18 11:12:00 -0800298 // Make sure we have a photo
299 RawContactModifier.ensureKindExists(
300 rawContactDelta, accountType, Photo.CONTENT_ITEM_TYPE);
301
Walter Jang3b210272015-03-04 11:34:11 -0800302 // Look for a non-empty super primary photo
Walter Jang3efae4a2015-02-18 11:12:00 -0800303 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
304 if (dataKind != null) {
Walter Jangac679af2015-06-01 12:17:06 -0700305 for (ValuesDelta valuesDelta
306 : rawContactDelta.getMimeEntries(Photo.CONTENT_ITEM_TYPE)) {
307 if (valuesDelta != null && valuesDelta.getId() != null
308 && valuesDelta.getId().equals(photoId)) {
309 mPhotoRawContactId = rawContactDelta.getRawContactId();
310 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
311 /* readOnly =*/ !dataKind.editable, mMaterialPalette,
312 viewIdGenerator);
313 return;
314 }
315 }
316 }
317 }
318
319 // Look for a non-empty super primary photo
320 for (RawContactDelta rawContactDelta : rawContactDeltas) {
321 if (!rawContactDelta.isVisible()) continue;
322 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
323 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
324 if (dataKind != null) {
Walter Jang3b210272015-03-04 11:34:11 -0800325 final ValuesDelta valuesDelta = getNonEmptySuperPrimaryValuesDeltas(
326 rawContactDelta, Photo.CONTENT_ITEM_TYPE, dataKind);
327 if (valuesDelta != null) {
Walter Jang3efae4a2015-02-18 11:12:00 -0800328 mPhotoRawContactId = rawContactDelta.getRawContactId();
Walter Janga5e4bb22015-02-24 13:08:16 -0800329 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
Walter Jang3b210272015-03-04 11:34:11 -0800330 /* readOnly =*/ !dataKind.editable, mMaterialPalette,
331 viewIdGenerator);
Walter Jang3efae4a2015-02-18 11:12:00 -0800332 return;
333 }
334 }
335 }
Walter Jang3b210272015-03-04 11:34:11 -0800336 // We didn't find a non-empty super primary photo, use the first non-empty one
337 for (RawContactDelta rawContactDelta : rawContactDeltas) {
338 if (!rawContactDelta.isVisible()) continue;
339 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
340 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
341 if (dataKind != null) {
342 final List<ValuesDelta> valuesDeltas = getNonEmptyValuesDeltas(
343 rawContactDelta, Photo.CONTENT_ITEM_TYPE, dataKind);
344 if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
345 mPhotoRawContactId = rawContactDelta.getRawContactId();
346 mPhoto.setValues(dataKind, valuesDeltas.get(0), rawContactDelta,
347 /* readOnly =*/ !dataKind.editable, mMaterialPalette,
348 viewIdGenerator);
349 return;
350 }
351 }
352 }
353 // No suitable non-empty photo
354 for (RawContactDelta rawContactDelta : rawContactDeltas) {
355 if (!rawContactDelta.isVisible()) continue;
356 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
357 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
358 if (dataKind != null) {
359 final ValuesDelta valuesDelta = rawContactDelta.getSuperPrimaryEntry(
360 dataKind.mimeType, /* forceSelection =*/ true);
361 if (valuesDelta != null) {
362 mPhotoRawContactId = rawContactDelta.getRawContactId();
363 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
364 /* readOnly =*/ !dataKind.editable, mMaterialPalette,
365 viewIdGenerator);
366 return;
367 }
368 }
369 }
370 // Should not happen since we ensure the kind exists but if we unexpectedly get here
371 // we must remove the photo section so that it does not take up the entire view
372 mPhoto.setVisibility(View.GONE);
Walter Jang3efae4a2015-02-18 11:12:00 -0800373 }
374
Walter Jang10446452015-02-20 13:51:16 -0800375 private void addStructuredNameView(RawContactDeltaList rawContactDeltas) {
376 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang151f3e62015-02-26 15:29:40 -0800377 if (!rawContactDelta.isVisible()) continue;
Walter Jang10446452015-02-20 13:51:16 -0800378 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
379
380 // Make sure we have a structured name
381 RawContactModifier.ensureKindExists(
382 rawContactDelta, accountType, StructuredName.CONTENT_ITEM_TYPE);
383
Walter Jang151f3e62015-02-26 15:29:40 -0800384 // Note use of pseudo mime type to get the DataKind and StructuredName to get value
Walter Jang10446452015-02-20 13:51:16 -0800385 final DataKind dataKind = accountType.getKindForMimetype(
Walter Jang151f3e62015-02-26 15:29:40 -0800386 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
387 if (dataKind == null || !dataKind.editable) continue;
388
389 final ValuesDelta superPrimaryValuesDelta = getNonEmptySuperPrimaryValuesDeltas(
390 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
391 if (superPrimaryValuesDelta != null) {
392 // Our first preference is for a non-empty super primary name
Walter Janga26490b2015-05-27 13:08:24 -0700393 final NameEditorListener nameEditorListener = new NameEditorListener(
394 superPrimaryValuesDelta, rawContactDelta.getRawContactId(), mListener);
Walter Jang151f3e62015-02-26 15:29:40 -0800395 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
Walter Janga26490b2015-05-27 13:08:24 -0700396 superPrimaryValuesDelta, rawContactDelta, nameEditorListener));
Walter Jang151f3e62015-02-26 15:29:40 -0800397 return;
398 }
399 }
400 // We didn't find a super primary name
401 for (RawContactDelta rawContactDelta : rawContactDeltas) {
402 if (!rawContactDelta.isVisible()) continue;
403 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
404
405 final DataKind dataKind = accountType.getKindForMimetype(
406 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
407 if (dataKind == null || !dataKind.editable) continue;
408
409 final List<ValuesDelta> nonEmptyValuesDeltas = getNonEmptyValuesDeltas(
410 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
411 if (nonEmptyValuesDeltas != null && !nonEmptyValuesDeltas.isEmpty()) {
412 // Take the first non-empty name, also make it super primary before expanding to the
413 // full editor (see #onCLick) so that name does not change if the user saves from
414 // the fully expanded editor.
415 mNameValuesDelta = nonEmptyValuesDeltas.get(0);
416 final NameEditorListener nameEditorListener = new NameEditorListener(
417 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
418 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
419 mNameValuesDelta, rawContactDelta, nameEditorListener));
420 return;
421 }
422 }
423 for (RawContactDelta rawContactDelta : rawContactDeltas) {
424 if (!rawContactDelta.isVisible()) continue;
425 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
426
427 final DataKind dataKind = accountType.getKindForMimetype(
428 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
429 if (dataKind == null || !dataKind.editable) continue;
430
431 // Fall back to the first entry
432 final ArrayList<ValuesDelta> valuesDeltas = rawContactDelta.getMimeEntries(
Walter Jang10446452015-02-20 13:51:16 -0800433 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang151f3e62015-02-26 15:29:40 -0800434 if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
435 mNameValuesDelta = valuesDeltas.get(0);
436 final NameEditorListener nameEditorListener = new NameEditorListener(
437 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
438 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
439 mNameValuesDelta, rawContactDelta, nameEditorListener));
440 return;
Walter Jang10446452015-02-20 13:51:16 -0800441 }
442 }
443 }
444
Walter Jang3efae4a2015-02-18 11:12:00 -0800445 private void addEditorViews(RawContactDeltaList rawContactDeltas) {
446 for (RawContactDelta rawContactDelta : rawContactDeltas) {
447 if (!rawContactDelta.isVisible()) {
448 continue;
449 }
450 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
451
Walter Jangcab3dce2015-02-09 17:48:03 -0800452 for (DataKind dataKind : accountType.getSortedDataKinds()) {
453 if (!dataKind.editable) {
454 continue;
455 }
456 final String mimeType = dataKind.mimeType;
Walter Jangbf63a6d2015-05-05 09:14:35 -0700457 vlog(mimeType + " " + dataKind.fieldList.size() + " field(s)");
Walter Jangcab3dce2015-02-09 17:48:03 -0800458 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jang10446452015-02-20 13:51:16 -0800459 || StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangcab3dce2015-02-09 17:48:03 -0800460 || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800461 // Photos and structured names are handled separately and
462 // group membership is not supported
Walter Jangcab3dce2015-02-09 17:48:03 -0800463 continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800464 } else if (DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800465 // Only add phonetic names if there is a non-empty one. Note the use of
466 // StructuredName mimeType below, even though we matched a pseudo mime type.
Walter Jang3a37a1a2015-03-04 07:41:32 -0800467 final ValuesDelta valuesDelta = rawContactDelta.getSuperPrimaryEntry(
468 StructuredName.CONTENT_ITEM_TYPE, /* forceSelection =*/ true);
469 if (hasNonEmptyValue(dataKind, valuesDelta)) {
470 mPhoneticNames.addView(inflatePhoneticNameEditorView(
471 mPhoneticNames, accountType, valuesDelta, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800472 }
473 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800474 // Only add nicknames if there is a non-empty one
Walter Jangcab3dce2015-02-09 17:48:03 -0800475 if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
Walter Jang28c98b72015-06-01 15:56:37 -0700476 mNicknames.addView(inflateKindSectionView(mNicknames, dataKind,
477 rawContactDelta, /* showOneEmptyEditor =*/ false));
Walter Jangcab3dce2015-02-09 17:48:03 -0800478 }
479 } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang28c98b72015-06-01 15:56:37 -0700480 mPhoneNumbers.addView(inflateKindSectionView(mPhoneNumbers, dataKind,
481 rawContactDelta, /* showOneEmptyEditor =*/ true));
Walter Jangcab3dce2015-02-09 17:48:03 -0800482 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang28c98b72015-06-01 15:56:37 -0700483 mEmails.addView(inflateKindSectionView(mEmails, dataKind, rawContactDelta,
484 /* showOneEmptyEditor =*/ true));
Walter Jangcab3dce2015-02-09 17:48:03 -0800485 } else if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
Walter Jang28c98b72015-06-01 15:56:37 -0700486 mOther.addView(inflateKindSectionView(mOther, dataKind, rawContactDelta,
487 /* showOneEmptyEditor =*/ false));
Walter Jangcab3dce2015-02-09 17:48:03 -0800488 }
489 }
490 }
491 }
492
Walter Jange720fde2015-02-17 10:54:14 -0800493 // TODO: avoid inflating extra views and deleting them
494 private void removeExtraEmptyTextFields(ViewGroup viewGroup) {
495 // If there is one (or less) editors, leave it whether it is empty or not
496 if (viewGroup.getChildCount() <= 1) {
Walter Jangb1c87622015-02-13 17:51:38 -0800497 return;
498 }
Walter Jange720fde2015-02-17 10:54:14 -0800499 // Determine if there are any non-empty editors
500 boolean hasAtLeastOneNonEmptyEditorView = false;
501 for (int i = 0; i < viewGroup.getChildCount(); i++) {
502 if (!isEmptyEditorView(viewGroup.getChildAt(i))) {
503 hasAtLeastOneNonEmptyEditorView = true;
Walter Jangb1c87622015-02-13 17:51:38 -0800504 break;
505 }
506 }
Walter Jange720fde2015-02-17 10:54:14 -0800507 if (hasAtLeastOneNonEmptyEditorView) {
508 // There is at least one non-empty editor, remove all the empty ones
509 for (int i = 0; i < viewGroup.getChildCount(); i++) {
510 if (isEmptyEditorView(viewGroup.getChildAt(i))) {
511 viewGroup.getChildAt(i).setVisibility(View.GONE);
Walter Jangb1c87622015-02-13 17:51:38 -0800512 }
513 }
514 } else {
Walter Jange720fde2015-02-17 10:54:14 -0800515 // There is no non-empty editor, keep the first empty view and remove the rest
516 for (int i = 1; i < viewGroup.getChildCount(); i++) {
517 viewGroup.getChildAt(i).setVisibility(View.GONE);
Walter Jangb1c87622015-02-13 17:51:38 -0800518 }
519 }
520 }
521
Walter Jange720fde2015-02-17 10:54:14 -0800522 private static boolean isEmptyEditorView(View view) {
523 if (view instanceof TextFieldsEditorView) {
524 final TextFieldsEditorView textFieldsEditorView = (TextFieldsEditorView) view;
525 return textFieldsEditorView.isEmpty();
526 }
527 if (view instanceof KindSectionView) {
528 final KindSectionView kindSectionView = (KindSectionView) view;
529 return kindSectionView.hasEmptyEditor();
530 }
531 return false;
532 }
533
Walter Jangcab3dce2015-02-09 17:48:03 -0800534 private static boolean hasNonEmptyValuesDelta(RawContactDelta rawContactDelta,
535 String mimeType, DataKind dataKind) {
536 return !getNonEmptyValuesDeltas(rawContactDelta, mimeType, dataKind).isEmpty();
537 }
538
Walter Jang151f3e62015-02-26 15:29:40 -0800539 private static ValuesDelta getNonEmptySuperPrimaryValuesDeltas(RawContactDelta rawContactDelta,
540 String mimeType, DataKind dataKind) {
541 for (ValuesDelta valuesDelta : getNonEmptyValuesDeltas(
542 rawContactDelta, mimeType, dataKind)) {
543 if (valuesDelta.isSuperPrimary()) {
544 return valuesDelta;
545 }
546 }
547 return null;
548 }
549
550 static List<ValuesDelta> getNonEmptyValuesDeltas(RawContactDelta rawContactDelta,
Walter Jangcab3dce2015-02-09 17:48:03 -0800551 String mimeType, DataKind dataKind) {
552 final List<ValuesDelta> result = new ArrayList<>();
553 if (rawContactDelta == null) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700554 vlog("Null RawContactDelta");
Walter Jangcab3dce2015-02-09 17:48:03 -0800555 return result;
556 }
557 if (!rawContactDelta.hasMimeEntries(mimeType)) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700558 vlog("No ValueDeltas");
Walter Jangcab3dce2015-02-09 17:48:03 -0800559 return result;
560 }
561 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(mimeType)) {
Walter Jang3a37a1a2015-03-04 07:41:32 -0800562 if (hasNonEmptyValue(dataKind, valuesDelta)) {
563 result.add(valuesDelta);
Walter Jangcab3dce2015-02-09 17:48:03 -0800564 }
565 }
566 return result;
567 }
568
Walter Jang3a37a1a2015-03-04 07:41:32 -0800569 private static boolean hasNonEmptyValue(DataKind dataKind, ValuesDelta valuesDelta) {
570 if (valuesDelta == null) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700571 vlog("Null valuesDelta");
Walter Jang3a37a1a2015-03-04 07:41:32 -0800572 return false;
573 }
574 for (EditField editField : dataKind.fieldList) {
575 final String column = editField.column;
576 final String value = valuesDelta == null ? null : valuesDelta.getAsString(column);
Walter Jangbf63a6d2015-05-05 09:14:35 -0700577 vlog("Field " + column + " empty=" + TextUtils.isEmpty(value) + " value=" + value);
Walter Jang3a37a1a2015-03-04 07:41:32 -0800578 if (!TextUtils.isEmpty(value)) {
579 return true;
580 }
581 }
582 return false;
583 }
584
Walter Jangcab3dce2015-02-09 17:48:03 -0800585 private StructuredNameEditorView inflateStructuredNameEditorView(ViewGroup viewGroup,
Walter Jang151f3e62015-02-26 15:29:40 -0800586 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta,
587 NameEditorListener nameEditorListener) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800588 final StructuredNameEditorView result = (StructuredNameEditorView) mLayoutInflater.inflate(
589 R.layout.structured_name_editor_view, viewGroup, /* attachToRoot =*/ false);
Walter Jang151f3e62015-02-26 15:29:40 -0800590 if (nameEditorListener != null) {
591 result.setEditorListener(nameEditorListener);
592 }
Walter Jang3a37a1a2015-03-04 07:41:32 -0800593 result.setDeletable(false);
Walter Jangcab3dce2015-02-09 17:48:03 -0800594 result.setValues(
595 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME),
596 valuesDelta,
597 rawContactDelta,
598 /* readOnly =*/ false,
599 mViewIdGenerator);
600 return result;
601 }
602
603 private PhoneticNameEditorView inflatePhoneticNameEditorView(ViewGroup viewGroup,
604 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta) {
605 final PhoneticNameEditorView result = (PhoneticNameEditorView) mLayoutInflater.inflate(
606 R.layout.phonetic_name_editor_view, viewGroup, /* attachToRoot =*/ false);
Walter Jang3a37a1a2015-03-04 07:41:32 -0800607 result.setDeletable(false);
Walter Jangcab3dce2015-02-09 17:48:03 -0800608 result.setValues(
609 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME),
610 valuesDelta,
611 rawContactDelta,
612 /* readOnly =*/ false,
613 mViewIdGenerator);
614 return result;
615 }
616
Walter Jangcab3dce2015-02-09 17:48:03 -0800617 private KindSectionView inflateKindSectionView(ViewGroup viewGroup, DataKind dataKind,
Walter Jang28c98b72015-06-01 15:56:37 -0700618 RawContactDelta rawContactDelta, boolean showOneEmptyEditor) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800619 final KindSectionView result = (KindSectionView) mLayoutInflater.inflate(
620 R.layout.item_kind_section, viewGroup, /* attachToRoot =*/ false);
621 result.setState(
622 dataKind,
623 rawContactDelta,
624 /* readOnly =*/ false,
Walter Jang28c98b72015-06-01 15:56:37 -0700625 showOneEmptyEditor,
Walter Jangcab3dce2015-02-09 17:48:03 -0800626 mViewIdGenerator);
627 return result;
628 }
629
Walter Jangbf63a6d2015-05-05 09:14:35 -0700630 private static void vlog(String message) {
631 if (Log.isLoggable(TAG, Log.VERBOSE)) {
632 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -0800633 }
634 }
635}