blob: 49cd2a2350fb64bd891c234a6ae9ddf8c8f27e2a [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 Jang06f73a12015-06-17 11:15:48 -0700140 private StructuredNameEditorView mDefaultNameEditorView;
141
Walter Jangcab3dce2015-02-09 17:48:03 -0800142 public CompactRawContactsEditorView(Context context) {
143 super(context);
144 }
145
146 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
147 super(context, attrs);
148 }
149
Walter Jangb6ca2722015-02-20 11:10:25 -0800150 /**
151 * Sets the receiver for {@link CompactRawContactsEditorView} callbacks.
152 */
153 public void setListener(Listener listener) {
154 mListener = listener;
155 }
156
Walter Jangcab3dce2015-02-09 17:48:03 -0800157 @Override
158 protected void onFinishInflate() {
159 super.onFinishInflate();
160
161 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
162 mLayoutInflater = (LayoutInflater)
163 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
164
Walter Janga5e4bb22015-02-24 13:08:16 -0800165 mPhoto = (CompactPhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangcab3dce2015-02-09 17:48:03 -0800166 mNames = (LinearLayout) findViewById(R.id.names);
Walter Jangb1c87622015-02-13 17:51:38 -0800167 mPhoneticNames = (LinearLayout) findViewById(R.id.phonetic_names);
168 mNicknames = (LinearLayout) findViewById(R.id.nicknames);
Walter Jangcab3dce2015-02-09 17:48:03 -0800169 mPhoneNumbers = (LinearLayout) findViewById(R.id.phone_numbers);
170 mEmails = (LinearLayout) findViewById(R.id.emails);
171 mOther = (LinearLayout) findViewById(R.id.other);
Walter Jangb6ca2722015-02-20 11:10:25 -0800172 mMoreFields = findViewById(R.id.more_fields);
173 mMoreFields.setOnClickListener(this);
174 }
175
Walter Jangb6ca2722015-02-20 11:10:25 -0800176 @Override
177 public void onClick(View view) {
178 if (view.getId() == R.id.more_fields && mListener != null ) {
179 mListener.onExpandEditor();
180 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800181 }
182
183 @Override
184 public void setEnabled(boolean enabled) {
185 super.setEnabled(enabled);
186 setEnabled(enabled, mNames);
Walter Jangb1c87622015-02-13 17:51:38 -0800187 setEnabled(enabled, mPhoneticNames);
188 setEnabled(enabled, mNicknames);
Walter Jangcab3dce2015-02-09 17:48:03 -0800189 setEnabled(enabled, mPhoneNumbers);
190 setEnabled(enabled, mEmails);
191 setEnabled(enabled, mOther);
192 }
193
194 private void setEnabled(boolean enabled, ViewGroup viewGroup) {
195 if (viewGroup != null) {
196 final int childCount = viewGroup.getChildCount();
197 for (int i = 0; i < childCount; i++) {
198 viewGroup.getChildAt(i).setEnabled(enabled);
199 }
200 }
201 }
202
Walter Jang3efae4a2015-02-18 11:12:00 -0800203 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800204 * Pass through to {@link CompactPhotoEditorView#setPhotoHandler}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800205 */
206 public void setPhotoHandler(PhotoHandler photoHandler) {
Walter Janga5e4bb22015-02-24 13:08:16 -0800207 mPhoto.setPhotoHandler(photoHandler);
Walter Jang3efae4a2015-02-18 11:12:00 -0800208 }
209
210 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800211 * Pass through to {@link CompactPhotoEditorView#setPhoto}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800212 */
213 public void setPhoto(Bitmap bitmap) {
Walter Janga5e4bb22015-02-24 13:08:16 -0800214 mPhoto.setPhoto(bitmap);
Walter Jang3efae4a2015-02-18 11:12:00 -0800215 }
216
217 /**
Walter Jang41b3ea12015-03-09 17:30:06 -0700218 * Pass through to {@link CompactPhotoEditorView#setFullSizedPhoto(Uri)}.
219 */
220 public void setFullSizePhoto(Uri photoUri) {
221 mPhoto.setFullSizedPhoto(photoUri);
222 }
223
224 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800225 * Pass through to {@link CompactPhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800226 */
227 public boolean isWritablePhotoSet() {
Walter Janga5e4bb22015-02-24 13:08:16 -0800228 return mPhoto.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800229 }
230
231 /**
Walter Jang3efae4a2015-02-18 11:12:00 -0800232 * Get the raw contact ID for the CompactHeaderView photo.
233 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800234 public long getPhotoRawContactId() {
235 return mPhotoRawContactId;
236 }
237
Walter Jang06f73a12015-06-17 11:15:48 -0700238 public StructuredNameEditorView getDefaultNameEditorView() {
239 return mDefaultNameEditorView;
240 }
241
Walter Jangd35e5ef2015-02-24 09:18:16 -0800242 public StructuredNameEditorView getStructuredNameEditorView() {
243 // We only ever show one StructuredName
244 return mNames.getChildCount() == 0
245 ? null : (StructuredNameEditorView) mNames.getChildAt(0);
246 }
247
Walter Jangbf63a6d2015-05-05 09:14:35 -0700248 public PhoneticNameEditorView getFirstPhoneticNameEditorView() {
249 // There should only ever be one phonetic name
250 return mPhoneticNames.getChildCount() == 0
251 ? null : (PhoneticNameEditorView) mPhoneticNames.getChildAt(0);
252 }
253
Walter Jangd35e5ef2015-02-24 09:18:16 -0800254 public View getAggregationAnchorView() {
255 // Since there is only one structured name we can just return it as the anchor for
256 // the aggregation suggestions popup
257 if (mNames.getChildCount() == 0) {
258 return null;
259 }
260 return mNames.getChildAt(0).findViewById(R.id.anchor_view);
261 }
262
Walter Jang06f73a12015-06-17 11:15:48 -0700263 /**
264 * @param readOnlyDisplayName The display name to set on the new raw contact created in order
265 * to edit a read-only contact.
266 */
Walter Jangf46abd82015-02-20 16:52:04 -0800267 public void setState(RawContactDeltaList rawContactDeltas,
Walter Jang06f73a12015-06-17 11:15:48 -0700268 MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
269 long photoId, long nameId, String readOnlyDisplayName) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800270 mNames.removeAllViews();
Walter Jangb1c87622015-02-13 17:51:38 -0800271 mPhoneticNames.removeAllViews();
272 mNicknames.removeAllViews();
Walter Jangcab3dce2015-02-09 17:48:03 -0800273 mPhoneNumbers.removeAllViews();
274 mEmails.removeAllViews();
275 mOther.removeAllViews();
276
277 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
278 return;
279 }
280
281 mViewIdGenerator = viewIdGenerator;
Walter Jangcab3dce2015-02-09 17:48:03 -0800282 setId(mViewIdGenerator.getId(rawContactDeltas.get(0), /* dataKind =*/ null,
283 /* valuesDelta =*/ null, ViewIdGenerator.NO_VIEW_INDEX));
Walter Jangf46abd82015-02-20 16:52:04 -0800284 mMaterialPalette = materialPalette;
Walter Jangcab3dce2015-02-09 17:48:03 -0800285
Walter Jangbf63a6d2015-05-05 09:14:35 -0700286 vlog("Setting compact editor state from " + rawContactDeltas);
Walter Jangac679af2015-06-01 12:17:06 -0700287 addPhotoView(rawContactDeltas, viewIdGenerator, photoId);
Walter Jang06f73a12015-06-17 11:15:48 -0700288 addStructuredNameView(rawContactDeltas, nameId, readOnlyDisplayName);
Walter Jang3efae4a2015-02-18 11:12:00 -0800289 addEditorViews(rawContactDeltas);
Walter Jange720fde2015-02-17 10:54:14 -0800290 removeExtraEmptyTextFields(mPhoneNumbers);
291 removeExtraEmptyTextFields(mEmails);
Walter Jangcab3dce2015-02-09 17:48:03 -0800292 }
293
Walter Jang151f3e62015-02-26 15:29:40 -0800294 private void addPhotoView(RawContactDeltaList rawContactDeltas,
Walter Jangac679af2015-06-01 12:17:06 -0700295 ViewIdGenerator viewIdGenerator, long photoId) {
296 // Look for a match for the photo ID that was passed in
Walter Jangcab3dce2015-02-09 17:48:03 -0800297 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang3b210272015-03-04 11:34:11 -0800298 if (!rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800299 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jange720fde2015-02-17 10:54:14 -0800300
Walter Jang3efae4a2015-02-18 11:12:00 -0800301 // Make sure we have a photo
302 RawContactModifier.ensureKindExists(
303 rawContactDelta, accountType, Photo.CONTENT_ITEM_TYPE);
304
305 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
Walter Jangab50e6f2015-06-15 08:57:22 -0700306 if (dataKind != null && dataKind.editable) {
Walter Jangac679af2015-06-01 12:17:06 -0700307 for (ValuesDelta valuesDelta
308 : rawContactDelta.getMimeEntries(Photo.CONTENT_ITEM_TYPE)) {
309 if (valuesDelta != null && valuesDelta.getId() != null
310 && valuesDelta.getId().equals(photoId)) {
311 mPhotoRawContactId = rawContactDelta.getRawContactId();
312 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
Walter Jangab50e6f2015-06-15 08:57:22 -0700313 !accountType.areContactsWritable(),
314 mMaterialPalette, viewIdGenerator);
Walter Jangac679af2015-06-01 12:17:06 -0700315 return;
316 }
317 }
318 }
319 }
320
321 // Look for a non-empty super primary photo
322 for (RawContactDelta rawContactDelta : rawContactDeltas) {
323 if (!rawContactDelta.isVisible()) continue;
324 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
325 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
Walter Jangab50e6f2015-06-15 08:57:22 -0700326 if (dataKind != null && dataKind.editable) {
Walter Jang3b210272015-03-04 11:34:11 -0800327 final ValuesDelta valuesDelta = getNonEmptySuperPrimaryValuesDeltas(
328 rawContactDelta, Photo.CONTENT_ITEM_TYPE, dataKind);
329 if (valuesDelta != null) {
Walter Jang3efae4a2015-02-18 11:12:00 -0800330 mPhotoRawContactId = rawContactDelta.getRawContactId();
Walter Janga5e4bb22015-02-24 13:08:16 -0800331 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
Walter Jangab50e6f2015-06-15 08:57:22 -0700332 !accountType.areContactsWritable(), mMaterialPalette,
Walter Jang3b210272015-03-04 11:34:11 -0800333 viewIdGenerator);
Walter Jang3efae4a2015-02-18 11:12:00 -0800334 return;
335 }
336 }
337 }
Walter Jang3b210272015-03-04 11:34:11 -0800338 // We didn't find a non-empty super primary photo, use the first non-empty one
339 for (RawContactDelta rawContactDelta : rawContactDeltas) {
340 if (!rawContactDelta.isVisible()) continue;
341 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
342 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
Walter Jangab50e6f2015-06-15 08:57:22 -0700343 if (dataKind != null && dataKind.editable) {
Walter Jang3b210272015-03-04 11:34:11 -0800344 final List<ValuesDelta> valuesDeltas = getNonEmptyValuesDeltas(
345 rawContactDelta, Photo.CONTENT_ITEM_TYPE, dataKind);
346 if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
347 mPhotoRawContactId = rawContactDelta.getRawContactId();
348 mPhoto.setValues(dataKind, valuesDeltas.get(0), rawContactDelta,
Walter Jangab50e6f2015-06-15 08:57:22 -0700349 !accountType.areContactsWritable(), mMaterialPalette,
Walter Jang3b210272015-03-04 11:34:11 -0800350 viewIdGenerator);
351 return;
352 }
353 }
354 }
355 // No suitable non-empty photo
356 for (RawContactDelta rawContactDelta : rawContactDeltas) {
357 if (!rawContactDelta.isVisible()) continue;
358 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
359 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
Walter Jangab50e6f2015-06-15 08:57:22 -0700360 if (dataKind != null && dataKind.editable) {
Walter Jang3b210272015-03-04 11:34:11 -0800361 final ValuesDelta valuesDelta = rawContactDelta.getSuperPrimaryEntry(
362 dataKind.mimeType, /* forceSelection =*/ true);
363 if (valuesDelta != null) {
364 mPhotoRawContactId = rawContactDelta.getRawContactId();
365 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
Walter Jangab50e6f2015-06-15 08:57:22 -0700366 !accountType.areContactsWritable(), mMaterialPalette,
Walter Jang3b210272015-03-04 11:34:11 -0800367 viewIdGenerator);
368 return;
369 }
370 }
371 }
372 // Should not happen since we ensure the kind exists but if we unexpectedly get here
373 // we must remove the photo section so that it does not take up the entire view
374 mPhoto.setVisibility(View.GONE);
Walter Jang3efae4a2015-02-18 11:12:00 -0800375 }
376
Walter Jang06f73a12015-06-17 11:15:48 -0700377 private void addStructuredNameView(RawContactDeltaList rawContactDeltas, long nameId,
378 String readOnlyDisplayName) {
379 // If we're editing a read-only contact we want to display the name from the read-only
380 // contact in a structured name editor backed by the new raw contact that was created.
381 // The new raw contact is writable and merging it with the read-only contact allows us
382 // to edit the read-only contact. See go/editing-read-only-contacts
383 if (!TextUtils.isEmpty(readOnlyDisplayName)) {
384 for (RawContactDelta rawContactDelta : rawContactDeltas) {
385 if (!rawContactDelta.isVisible()) continue;
386 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
387
388 // Make sure we have a structured name
389 RawContactModifier.ensureKindExists(
390 rawContactDelta, accountType, StructuredName.CONTENT_ITEM_TYPE);
391
392 if (accountType.areContactsWritable()) {
393 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(
394 StructuredName.CONTENT_ITEM_TYPE)) {
395 if (valuesDelta != null) {
396 mNameValuesDelta = valuesDelta;
397 final NameEditorListener nameEditorListener = new NameEditorListener(
398 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
399 final StructuredNameEditorView nameEditorView =
400 inflateStructuredNameEditorView(mNames, accountType,
401 mNameValuesDelta, rawContactDelta, nameEditorListener,
402 !accountType.areContactsWritable());
403 nameEditorView.setDisplayName(readOnlyDisplayName);
404 mNames.addView(nameEditorView);
405 mDefaultNameEditorView = nameEditorView;
406 return;
407 }
408 }
409 }
410 }
411 }
412
Walter Jang65d35202015-06-16 13:08:34 -0700413 // Look for a match for the name ID that was passed in
Walter Jang10446452015-02-20 13:51:16 -0800414 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang151f3e62015-02-26 15:29:40 -0800415 if (!rawContactDelta.isVisible()) continue;
Walter Jang10446452015-02-20 13:51:16 -0800416 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
417
418 // Make sure we have a structured name
419 RawContactModifier.ensureKindExists(
420 rawContactDelta, accountType, StructuredName.CONTENT_ITEM_TYPE);
421
Walter Jang151f3e62015-02-26 15:29:40 -0800422 // Note use of pseudo mime type to get the DataKind and StructuredName to get value
Walter Jang10446452015-02-20 13:51:16 -0800423 final DataKind dataKind = accountType.getKindForMimetype(
Walter Jang151f3e62015-02-26 15:29:40 -0800424 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
425 if (dataKind == null || !dataKind.editable) continue;
426
Walter Jang398cd4b2015-06-16 11:17:53 -0700427 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(
428 StructuredName.CONTENT_ITEM_TYPE)) {
429 if (valuesDelta != null && valuesDelta.getId() != null
430 && valuesDelta.getId().equals(nameId)) {
431 mNameValuesDelta = valuesDelta;
432 final NameEditorListener nameEditorListener = new NameEditorListener(
433 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
434 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
Walter Jang65d35202015-06-16 13:08:34 -0700435 mNameValuesDelta, rawContactDelta, nameEditorListener,
436 !accountType.areContactsWritable()));
Walter Jang398cd4b2015-06-16 11:17:53 -0700437 return;
438 }
439 }
440 }
441 // Look for a super primary name
442 for (RawContactDelta rawContactDelta : rawContactDeltas) {
443 if (!rawContactDelta.isVisible()) continue;
444 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
445
446 final DataKind dataKind = accountType.getKindForMimetype(
447 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
448 if (dataKind == null || !dataKind.editable) continue;
449
Walter Jang151f3e62015-02-26 15:29:40 -0800450 final ValuesDelta superPrimaryValuesDelta = getNonEmptySuperPrimaryValuesDeltas(
451 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
452 if (superPrimaryValuesDelta != null) {
453 // Our first preference is for a non-empty super primary name
Walter Janga26490b2015-05-27 13:08:24 -0700454 final NameEditorListener nameEditorListener = new NameEditorListener(
455 superPrimaryValuesDelta, rawContactDelta.getRawContactId(), mListener);
Walter Jang151f3e62015-02-26 15:29:40 -0800456 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
Walter Jang65d35202015-06-16 13:08:34 -0700457 superPrimaryValuesDelta, rawContactDelta, nameEditorListener,
458 !accountType.areContactsWritable()));
Walter Jang151f3e62015-02-26 15:29:40 -0800459 return;
460 }
461 }
462 // We didn't find a super primary name
463 for (RawContactDelta rawContactDelta : rawContactDeltas) {
464 if (!rawContactDelta.isVisible()) continue;
465 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
466
467 final DataKind dataKind = accountType.getKindForMimetype(
468 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
469 if (dataKind == null || !dataKind.editable) continue;
470
471 final List<ValuesDelta> nonEmptyValuesDeltas = getNonEmptyValuesDeltas(
472 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
473 if (nonEmptyValuesDeltas != null && !nonEmptyValuesDeltas.isEmpty()) {
Walter Jang65d35202015-06-16 13:08:34 -0700474 // Take the first non-empty name
Walter Jang151f3e62015-02-26 15:29:40 -0800475 mNameValuesDelta = nonEmptyValuesDeltas.get(0);
476 final NameEditorListener nameEditorListener = new NameEditorListener(
477 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
478 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
Walter Jang65d35202015-06-16 13:08:34 -0700479 mNameValuesDelta, rawContactDelta, nameEditorListener,
480 !accountType.areContactsWritable()));
Walter Jang151f3e62015-02-26 15:29:40 -0800481 return;
482 }
483 }
484 for (RawContactDelta rawContactDelta : rawContactDeltas) {
485 if (!rawContactDelta.isVisible()) continue;
486 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
487
488 final DataKind dataKind = accountType.getKindForMimetype(
489 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
490 if (dataKind == null || !dataKind.editable) continue;
491
492 // Fall back to the first entry
493 final ArrayList<ValuesDelta> valuesDeltas = rawContactDelta.getMimeEntries(
Walter Jang10446452015-02-20 13:51:16 -0800494 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang151f3e62015-02-26 15:29:40 -0800495 if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
496 mNameValuesDelta = valuesDeltas.get(0);
497 final NameEditorListener nameEditorListener = new NameEditorListener(
498 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
499 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
Walter Jang65d35202015-06-16 13:08:34 -0700500 mNameValuesDelta, rawContactDelta, nameEditorListener,
501 !accountType.areContactsWritable()));
Walter Jang151f3e62015-02-26 15:29:40 -0800502 return;
Walter Jang10446452015-02-20 13:51:16 -0800503 }
504 }
505 }
506
Walter Jang3efae4a2015-02-18 11:12:00 -0800507 private void addEditorViews(RawContactDeltaList rawContactDeltas) {
508 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jangab50e6f2015-06-15 08:57:22 -0700509 if (!rawContactDelta.isVisible()) continue;
Walter Jang3efae4a2015-02-18 11:12:00 -0800510 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
511
Walter Jangcab3dce2015-02-09 17:48:03 -0800512 for (DataKind dataKind : accountType.getSortedDataKinds()) {
Walter Jangab50e6f2015-06-15 08:57:22 -0700513 if (!dataKind.editable) continue;
514
Walter Jangcab3dce2015-02-09 17:48:03 -0800515 final String mimeType = dataKind.mimeType;
Walter Jangbf63a6d2015-05-05 09:14:35 -0700516 vlog(mimeType + " " + dataKind.fieldList.size() + " field(s)");
Walter Jangcab3dce2015-02-09 17:48:03 -0800517 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jang10446452015-02-20 13:51:16 -0800518 || StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangcab3dce2015-02-09 17:48:03 -0800519 || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800520 // Photos and structured names are handled separately and
521 // group membership is not supported
Walter Jangcab3dce2015-02-09 17:48:03 -0800522 continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800523 } else if (DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800524 // Only add phonetic names if there is a non-empty one. Note the use of
525 // StructuredName mimeType below, even though we matched a pseudo mime type.
Walter Jang3a37a1a2015-03-04 07:41:32 -0800526 final ValuesDelta valuesDelta = rawContactDelta.getSuperPrimaryEntry(
527 StructuredName.CONTENT_ITEM_TYPE, /* forceSelection =*/ true);
528 if (hasNonEmptyValue(dataKind, valuesDelta)) {
529 mPhoneticNames.addView(inflatePhoneticNameEditorView(
530 mPhoneticNames, accountType, valuesDelta, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800531 }
532 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800533 // Only add nicknames if there is a non-empty one
Walter Jangcab3dce2015-02-09 17:48:03 -0800534 if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
Walter Jang28c98b72015-06-01 15:56:37 -0700535 mNicknames.addView(inflateKindSectionView(mNicknames, dataKind,
Walter Jang88b0a8c2015-06-08 13:15:56 -0700536 rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800537 }
538 } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang28c98b72015-06-01 15:56:37 -0700539 mPhoneNumbers.addView(inflateKindSectionView(mPhoneNumbers, dataKind,
Walter Jang88b0a8c2015-06-08 13:15:56 -0700540 rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800541 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang88b0a8c2015-06-08 13:15:56 -0700542 mEmails.addView(inflateKindSectionView(mEmails, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800543 } else if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
Walter Jang88b0a8c2015-06-08 13:15:56 -0700544 mOther.addView(inflateKindSectionView(mOther, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800545 }
546 }
547 }
548 }
549
Walter Jange720fde2015-02-17 10:54:14 -0800550 // TODO: avoid inflating extra views and deleting them
551 private void removeExtraEmptyTextFields(ViewGroup viewGroup) {
552 // If there is one (or less) editors, leave it whether it is empty or not
553 if (viewGroup.getChildCount() <= 1) {
Walter Jangb1c87622015-02-13 17:51:38 -0800554 return;
555 }
Walter Jange720fde2015-02-17 10:54:14 -0800556 // Determine if there are any non-empty editors
557 boolean hasAtLeastOneNonEmptyEditorView = false;
558 for (int i = 0; i < viewGroup.getChildCount(); i++) {
559 if (!isEmptyEditorView(viewGroup.getChildAt(i))) {
560 hasAtLeastOneNonEmptyEditorView = true;
Walter Jangb1c87622015-02-13 17:51:38 -0800561 break;
562 }
563 }
Walter Jange720fde2015-02-17 10:54:14 -0800564 if (hasAtLeastOneNonEmptyEditorView) {
565 // There is at least one non-empty editor, remove all the empty ones
566 for (int i = 0; i < viewGroup.getChildCount(); i++) {
567 if (isEmptyEditorView(viewGroup.getChildAt(i))) {
568 viewGroup.getChildAt(i).setVisibility(View.GONE);
Walter Jangb1c87622015-02-13 17:51:38 -0800569 }
570 }
571 } else {
Walter Jange720fde2015-02-17 10:54:14 -0800572 // There is no non-empty editor, keep the first empty view and remove the rest
573 for (int i = 1; i < viewGroup.getChildCount(); i++) {
574 viewGroup.getChildAt(i).setVisibility(View.GONE);
Walter Jangb1c87622015-02-13 17:51:38 -0800575 }
576 }
577 }
578
Walter Jange720fde2015-02-17 10:54:14 -0800579 private static boolean isEmptyEditorView(View view) {
580 if (view instanceof TextFieldsEditorView) {
581 final TextFieldsEditorView textFieldsEditorView = (TextFieldsEditorView) view;
582 return textFieldsEditorView.isEmpty();
583 }
584 if (view instanceof KindSectionView) {
585 final KindSectionView kindSectionView = (KindSectionView) view;
586 return kindSectionView.hasEmptyEditor();
587 }
588 return false;
589 }
590
Walter Jangcab3dce2015-02-09 17:48:03 -0800591 private static boolean hasNonEmptyValuesDelta(RawContactDelta rawContactDelta,
592 String mimeType, DataKind dataKind) {
593 return !getNonEmptyValuesDeltas(rawContactDelta, mimeType, dataKind).isEmpty();
594 }
595
Walter Jang151f3e62015-02-26 15:29:40 -0800596 private static ValuesDelta getNonEmptySuperPrimaryValuesDeltas(RawContactDelta rawContactDelta,
597 String mimeType, DataKind dataKind) {
598 for (ValuesDelta valuesDelta : getNonEmptyValuesDeltas(
599 rawContactDelta, mimeType, dataKind)) {
600 if (valuesDelta.isSuperPrimary()) {
601 return valuesDelta;
602 }
603 }
604 return null;
605 }
606
607 static List<ValuesDelta> getNonEmptyValuesDeltas(RawContactDelta rawContactDelta,
Walter Jangcab3dce2015-02-09 17:48:03 -0800608 String mimeType, DataKind dataKind) {
609 final List<ValuesDelta> result = new ArrayList<>();
610 if (rawContactDelta == null) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700611 vlog("Null RawContactDelta");
Walter Jangcab3dce2015-02-09 17:48:03 -0800612 return result;
613 }
614 if (!rawContactDelta.hasMimeEntries(mimeType)) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700615 vlog("No ValueDeltas");
Walter Jangcab3dce2015-02-09 17:48:03 -0800616 return result;
617 }
618 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(mimeType)) {
Walter Jang3a37a1a2015-03-04 07:41:32 -0800619 if (hasNonEmptyValue(dataKind, valuesDelta)) {
620 result.add(valuesDelta);
Walter Jangcab3dce2015-02-09 17:48:03 -0800621 }
622 }
623 return result;
624 }
625
Walter Jang3a37a1a2015-03-04 07:41:32 -0800626 private static boolean hasNonEmptyValue(DataKind dataKind, ValuesDelta valuesDelta) {
627 if (valuesDelta == null) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700628 vlog("Null valuesDelta");
Walter Jang3a37a1a2015-03-04 07:41:32 -0800629 return false;
630 }
631 for (EditField editField : dataKind.fieldList) {
632 final String column = editField.column;
633 final String value = valuesDelta == null ? null : valuesDelta.getAsString(column);
Walter Jangbf63a6d2015-05-05 09:14:35 -0700634 vlog("Field " + column + " empty=" + TextUtils.isEmpty(value) + " value=" + value);
Walter Jang3a37a1a2015-03-04 07:41:32 -0800635 if (!TextUtils.isEmpty(value)) {
636 return true;
637 }
638 }
639 return false;
640 }
641
Walter Jangcab3dce2015-02-09 17:48:03 -0800642 private StructuredNameEditorView inflateStructuredNameEditorView(ViewGroup viewGroup,
Walter Jang151f3e62015-02-26 15:29:40 -0800643 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta,
Walter Jang65d35202015-06-16 13:08:34 -0700644 NameEditorListener nameEditorListener, boolean readOnly) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800645 final StructuredNameEditorView result = (StructuredNameEditorView) mLayoutInflater.inflate(
646 R.layout.structured_name_editor_view, viewGroup, /* attachToRoot =*/ false);
Walter Jang151f3e62015-02-26 15:29:40 -0800647 if (nameEditorListener != null) {
648 result.setEditorListener(nameEditorListener);
649 }
Walter Jang3a37a1a2015-03-04 07:41:32 -0800650 result.setDeletable(false);
Walter Jangcab3dce2015-02-09 17:48:03 -0800651 result.setValues(
652 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME),
653 valuesDelta,
654 rawContactDelta,
Walter Jang65d35202015-06-16 13:08:34 -0700655 readOnly,
Walter Jangcab3dce2015-02-09 17:48:03 -0800656 mViewIdGenerator);
657 return result;
658 }
659
660 private PhoneticNameEditorView inflatePhoneticNameEditorView(ViewGroup viewGroup,
661 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta) {
662 final PhoneticNameEditorView result = (PhoneticNameEditorView) mLayoutInflater.inflate(
663 R.layout.phonetic_name_editor_view, viewGroup, /* attachToRoot =*/ false);
Walter Jang3a37a1a2015-03-04 07:41:32 -0800664 result.setDeletable(false);
Walter Jangcab3dce2015-02-09 17:48:03 -0800665 result.setValues(
666 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME),
667 valuesDelta,
668 rawContactDelta,
669 /* readOnly =*/ false,
670 mViewIdGenerator);
671 return result;
672 }
673
Walter Jangcab3dce2015-02-09 17:48:03 -0800674 private KindSectionView inflateKindSectionView(ViewGroup viewGroup, DataKind dataKind,
Walter Jang88b0a8c2015-06-08 13:15:56 -0700675 RawContactDelta rawContactDelta) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800676 final KindSectionView result = (KindSectionView) mLayoutInflater.inflate(
677 R.layout.item_kind_section, viewGroup, /* attachToRoot =*/ false);
678 result.setState(
679 dataKind,
680 rawContactDelta,
681 /* readOnly =*/ false,
Walter Jangcab3dce2015-02-09 17:48:03 -0800682 mViewIdGenerator);
683 return result;
684 }
685
Walter Jangbf63a6d2015-05-05 09:14:35 -0700686 private static void vlog(String message) {
687 if (Log.isLoggable(TAG, Log.VERBOSE)) {
688 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -0800689 }
690 }
691}