blob: c38fa3e35c1ffd4f6197edc6a86f61fa212de944 [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;
Walter Jang2d3f31c2015-06-18 23:15:31 -070027import com.android.contacts.common.model.account.AccountWithDataSet;
Walter Jangcab3dce2015-02-09 17:48:03 -080028import com.android.contacts.common.model.dataitem.DataKind;
Walter Jang708ea9e2015-09-10 15:42:05 -070029import com.android.contacts.common.util.AccountsListAdapter;
Walter Jangf46abd82015-02-20 16:52:04 -080030import com.android.contacts.common.util.MaterialColorMapUtils;
Walter Jang3efae4a2015-02-18 11:12:00 -080031import com.android.contacts.editor.CompactContactEditorFragment.PhotoHandler;
Walter Jang708ea9e2015-09-10 15:42:05 -070032import com.android.contacts.util.UiClosables;
Walter Jangcab3dce2015-02-09 17:48:03 -080033
34import android.content.Context;
Walter Jang3efae4a2015-02-18 11:12:00 -080035import android.graphics.Bitmap;
Walter Jang41b3ea12015-03-09 17:30:06 -070036import android.net.Uri;
Walter Jangcab3dce2015-02-09 17:48:03 -080037import android.provider.ContactsContract.CommonDataKinds.Email;
38import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Walter Jang3efae4a2015-02-18 11:12:00 -080039import android.provider.ContactsContract.CommonDataKinds.Nickname;
Walter Jangcab3dce2015-02-09 17:48:03 -080040import android.provider.ContactsContract.CommonDataKinds.Phone;
41import android.provider.ContactsContract.CommonDataKinds.Photo;
Walter Jangcab3dce2015-02-09 17:48:03 -080042import android.provider.ContactsContract.CommonDataKinds.StructuredName;
43import android.text.TextUtils;
44import android.util.AttributeSet;
45import android.util.Log;
Walter Jang2d3f31c2015-06-18 23:15:31 -070046import android.util.Pair;
Walter Jangcab3dce2015-02-09 17:48:03 -080047import android.view.LayoutInflater;
Walter Jangb1c87622015-02-13 17:51:38 -080048import android.view.View;
Walter Jangcab3dce2015-02-09 17:48:03 -080049import android.view.ViewGroup;
Walter Jang708ea9e2015-09-10 15:42:05 -070050import android.widget.AdapterView;
Walter Jangcab3dce2015-02-09 17:48:03 -080051import android.widget.LinearLayout;
Walter Jang708ea9e2015-09-10 15:42:05 -070052import android.widget.ListPopupWindow;
Walter Jang2d3f31c2015-06-18 23:15:31 -070053import android.widget.TextView;
Walter Jangcab3dce2015-02-09 17:48:03 -080054
55import java.util.ArrayList;
Walter Jang19d985f2015-07-01 13:36:27 -070056import java.util.HashMap;
Walter Jangcab3dce2015-02-09 17:48:03 -080057import java.util.List;
Walter Jang19d985f2015-07-01 13:36:27 -070058import java.util.Map;
Walter Jang708ea9e2015-09-10 15:42:05 -070059import java.util.Objects;
Walter Jangcab3dce2015-02-09 17:48:03 -080060
61/**
62 * View to display information from multiple {@link RawContactDelta}s grouped together
63 * (e.g. all the phone numbers from a {@link com.android.contacts.common.model.Contact} together.
64 */
Walter Jangb6ca2722015-02-20 11:10:25 -080065public class CompactRawContactsEditorView extends LinearLayout implements View.OnClickListener {
Walter Jangcab3dce2015-02-09 17:48:03 -080066
67 private static final String TAG = "CompactEditorView";
68
Walter Jangb6ca2722015-02-20 11:10:25 -080069 /**
70 * Callbacks for hosts of {@link CompactRawContactsEditorView}s.
71 */
72 public interface Listener {
73
74 /**
75 * Invoked when the compact editor should be expanded to show all fields.
76 */
77 public void onExpandEditor();
Walter Jang151f3e62015-02-26 15:29:40 -080078
79 /**
80 * Invoked when the structured name editor field has changed.
81 *
82 * @param rawContactId The raw contact ID from the underlying {@link RawContactDelta}.
83 * @param valuesDelta The values from the underlying {@link RawContactDelta}.
84 */
85 public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta);
Walter Jang708ea9e2015-09-10 15:42:05 -070086
87 /**
88 * Invoked when the compact editor should rebind editors for a new account.
89 *
90 * @param oldState Old data being edited.
91 * @param oldAccount Old account associated with oldState.
92 * @param newAccount New account to be used.
93 */
94 public void onRebindEditorsForNewContact(RawContactDelta oldState,
95 AccountWithDataSet oldAccount, AccountWithDataSet newAccount);
Walter Jang151f3e62015-02-26 15:29:40 -080096 }
97
98 /**
99 * Marks a name as super primary when it is changed.
100 *
101 * This is for the case when two or more raw contacts with names are joined where neither is
102 * marked as super primary. If the user hits back (which causes a save) after changing the
103 * name that was arbitrarily displayed, we want that to be the name that is used.
104 *
105 * Should only be set when a super primary name does not already exist since we only show
106 * one name field.
107 */
108 static final class NameEditorListener implements Editor.EditorListener {
109
110 private final ValuesDelta mValuesDelta;
111 private final long mRawContactId;
112 private final Listener mListener;
113
114 public NameEditorListener(ValuesDelta valuesDelta, long rawContactId,
115 Listener listener) {
116 mValuesDelta = valuesDelta;
117 mRawContactId = rawContactId;
118 mListener = listener;
119 }
120
121 @Override
122 public void onRequest(int request) {
123 if (request == Editor.EditorListener.FIELD_CHANGED) {
124 mValuesDelta.setSuperPrimary(true);
125 if (mListener != null) {
126 mListener.onNameFieldChanged(mRawContactId, mValuesDelta);
127 }
128 } else if (request == Editor.EditorListener.FIELD_TURNED_EMPTY) {
129 mValuesDelta.setSuperPrimary(false);
130 }
131 }
132
133 @Override
134 public void onDeleteRequested(Editor editor) {
135 }
Walter Jangb6ca2722015-02-20 11:10:25 -0800136 }
137
138 private Listener mListener;
139
Walter Jangcab3dce2015-02-09 17:48:03 -0800140 private AccountTypeManager mAccountTypeManager;
141 private LayoutInflater mLayoutInflater;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800142
Walter Jangcab3dce2015-02-09 17:48:03 -0800143 private ViewIdGenerator mViewIdGenerator;
Walter Jangf46abd82015-02-20 16:52:04 -0800144 private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
Walter Jang708ea9e2015-09-10 15:42:05 -0700145 private long mPhotoId;
146 private long mNameId;
147 private String mReadOnlyDisplayName;
148 private boolean mHasNewContact;
149 private boolean mIsUserProfile;
150 private AccountWithDataSet mPrimaryAccount;
151 private RawContactDelta mPrimaryRawContactDelta;
Walter Jangcab3dce2015-02-09 17:48:03 -0800152
Walter Jang708ea9e2015-09-10 15:42:05 -0700153 // Account header
154 private View mAccountHeaderContainer;
155 private TextView mAccountHeaderType;
156 private TextView mAccountHeaderName;
157
158 // Account selector
159 private View mAccountSelectorContainer;
160 private View mAccountSelector;
161 private TextView mAccountSelectorType;
162 private TextView mAccountSelectorName;
163
Walter Janga5e4bb22015-02-24 13:08:16 -0800164 private CompactPhotoEditorView mPhoto;
Walter Jangcab3dce2015-02-09 17:48:03 -0800165 private ViewGroup mNames;
Walter Jangb1c87622015-02-13 17:51:38 -0800166 private ViewGroup mPhoneticNames;
167 private ViewGroup mNicknames;
Walter Jangcab3dce2015-02-09 17:48:03 -0800168 private ViewGroup mPhoneNumbers;
169 private ViewGroup mEmails;
Walter Jang995bb4a2015-07-06 12:21:36 -0700170 private ViewGroup mOtherTypes;
171 private Map<String,LinearLayout> mOtherTypesMap = new HashMap<>();
Walter Jangb6ca2722015-02-20 11:10:25 -0800172 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -0800173
Walter Jang151f3e62015-02-26 15:29:40 -0800174 // The ValuesDelta for the non super primary name that was displayed to the user.
175 private ValuesDelta mNameValuesDelta;
176
Walter Jang3efae4a2015-02-18 11:12:00 -0800177 private long mPhotoRawContactId;
178
Walter Jang06f73a12015-06-17 11:15:48 -0700179 private StructuredNameEditorView mDefaultNameEditorView;
180
Walter Jangcab3dce2015-02-09 17:48:03 -0800181 public CompactRawContactsEditorView(Context context) {
182 super(context);
183 }
184
185 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
186 super(context, attrs);
187 }
188
Walter Jangb6ca2722015-02-20 11:10:25 -0800189 /**
190 * Sets the receiver for {@link CompactRawContactsEditorView} callbacks.
191 */
192 public void setListener(Listener listener) {
193 mListener = listener;
194 }
195
Walter Jangcab3dce2015-02-09 17:48:03 -0800196 @Override
197 protected void onFinishInflate() {
198 super.onFinishInflate();
199
200 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
201 mLayoutInflater = (LayoutInflater)
202 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
203
Walter Jang708ea9e2015-09-10 15:42:05 -0700204 // Account header
205 mAccountHeaderContainer = findViewById(R.id.account_container);
206 mAccountHeaderType = (TextView) findViewById(R.id.account_type);
207 mAccountHeaderName = (TextView) findViewById(R.id.account_name);
208
209 // Account selector
210 mAccountSelectorContainer = findViewById(R.id.account_selector_container);
211 mAccountSelector = findViewById(R.id.account);
212 mAccountSelectorType = (TextView) findViewById(R.id.account_type_selector);
213 mAccountSelectorName = (TextView) findViewById(R.id.account_name_selector);
214
Walter Janga5e4bb22015-02-24 13:08:16 -0800215 mPhoto = (CompactPhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangcab3dce2015-02-09 17:48:03 -0800216 mNames = (LinearLayout) findViewById(R.id.names);
Walter Jangb1c87622015-02-13 17:51:38 -0800217 mPhoneticNames = (LinearLayout) findViewById(R.id.phonetic_names);
218 mNicknames = (LinearLayout) findViewById(R.id.nicknames);
Walter Jangcab3dce2015-02-09 17:48:03 -0800219 mPhoneNumbers = (LinearLayout) findViewById(R.id.phone_numbers);
220 mEmails = (LinearLayout) findViewById(R.id.emails);
Walter Jang995bb4a2015-07-06 12:21:36 -0700221 mOtherTypes = (LinearLayout) findViewById(R.id.other);
Walter Jangb6ca2722015-02-20 11:10:25 -0800222 mMoreFields = findViewById(R.id.more_fields);
223 mMoreFields.setOnClickListener(this);
224 }
225
Walter Jangb6ca2722015-02-20 11:10:25 -0800226 @Override
227 public void onClick(View view) {
228 if (view.getId() == R.id.more_fields && mListener != null ) {
229 mListener.onExpandEditor();
230 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800231 }
232
233 @Override
234 public void setEnabled(boolean enabled) {
235 super.setEnabled(enabled);
236 setEnabled(enabled, mNames);
Walter Jangb1c87622015-02-13 17:51:38 -0800237 setEnabled(enabled, mPhoneticNames);
238 setEnabled(enabled, mNicknames);
Walter Jangcab3dce2015-02-09 17:48:03 -0800239 setEnabled(enabled, mPhoneNumbers);
240 setEnabled(enabled, mEmails);
Walter Jang995bb4a2015-07-06 12:21:36 -0700241 for (Map.Entry<String,LinearLayout> otherType : mOtherTypesMap.entrySet()) {
242 setEnabled(enabled, otherType.getValue());
Walter Jang19d985f2015-07-01 13:36:27 -0700243 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800244 }
245
246 private void setEnabled(boolean enabled, ViewGroup viewGroup) {
247 if (viewGroup != null) {
248 final int childCount = viewGroup.getChildCount();
249 for (int i = 0; i < childCount; i++) {
250 viewGroup.getChildAt(i).setEnabled(enabled);
251 }
252 }
253 }
254
Walter Jang3efae4a2015-02-18 11:12:00 -0800255 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800256 * Pass through to {@link CompactPhotoEditorView#setPhotoHandler}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800257 */
258 public void setPhotoHandler(PhotoHandler photoHandler) {
Walter Janga5e4bb22015-02-24 13:08:16 -0800259 mPhoto.setPhotoHandler(photoHandler);
Walter Jang3efae4a2015-02-18 11:12:00 -0800260 }
261
262 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800263 * Pass through to {@link CompactPhotoEditorView#setPhoto}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800264 */
265 public void setPhoto(Bitmap bitmap) {
Walter Janga5e4bb22015-02-24 13:08:16 -0800266 mPhoto.setPhoto(bitmap);
Walter Jang3efae4a2015-02-18 11:12:00 -0800267 }
268
269 /**
Walter Jang41b3ea12015-03-09 17:30:06 -0700270 * Pass through to {@link CompactPhotoEditorView#setFullSizedPhoto(Uri)}.
271 */
272 public void setFullSizePhoto(Uri photoUri) {
273 mPhoto.setFullSizedPhoto(photoUri);
274 }
275
276 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800277 * Pass through to {@link CompactPhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800278 */
279 public boolean isWritablePhotoSet() {
Walter Janga5e4bb22015-02-24 13:08:16 -0800280 return mPhoto.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800281 }
282
283 /**
Walter Jang3efae4a2015-02-18 11:12:00 -0800284 * Get the raw contact ID for the CompactHeaderView photo.
285 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800286 public long getPhotoRawContactId() {
287 return mPhotoRawContactId;
288 }
289
Walter Jang06f73a12015-06-17 11:15:48 -0700290 public StructuredNameEditorView getDefaultNameEditorView() {
291 return mDefaultNameEditorView;
292 }
293
Walter Jangd35e5ef2015-02-24 09:18:16 -0800294 public StructuredNameEditorView getStructuredNameEditorView() {
295 // We only ever show one StructuredName
296 return mNames.getChildCount() == 0
297 ? null : (StructuredNameEditorView) mNames.getChildAt(0);
298 }
299
Walter Jangbf63a6d2015-05-05 09:14:35 -0700300 public PhoneticNameEditorView getFirstPhoneticNameEditorView() {
301 // There should only ever be one phonetic name
302 return mPhoneticNames.getChildCount() == 0
303 ? null : (PhoneticNameEditorView) mPhoneticNames.getChildAt(0);
304 }
305
Walter Jangd35e5ef2015-02-24 09:18:16 -0800306 public View getAggregationAnchorView() {
307 // Since there is only one structured name we can just return it as the anchor for
308 // the aggregation suggestions popup
309 if (mNames.getChildCount() == 0) {
310 return null;
311 }
312 return mNames.getChildAt(0).findViewById(R.id.anchor_view);
313 }
314
Walter Jang06f73a12015-06-17 11:15:48 -0700315 /**
316 * @param readOnlyDisplayName The display name to set on the new raw contact created in order
317 * to edit a read-only contact.
318 */
Walter Jangf46abd82015-02-20 16:52:04 -0800319 public void setState(RawContactDeltaList rawContactDeltas,
Walter Jang06f73a12015-06-17 11:15:48 -0700320 MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
Walter Jang2d3f31c2015-06-18 23:15:31 -0700321 long photoId, long nameId, String readOnlyDisplayName, boolean hasNewContact,
Walter Jang708ea9e2015-09-10 15:42:05 -0700322 boolean isUserProfile, AccountWithDataSet primaryAccount) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800323 mNames.removeAllViews();
Walter Jangb1c87622015-02-13 17:51:38 -0800324 mPhoneticNames.removeAllViews();
325 mNicknames.removeAllViews();
Walter Jangcab3dce2015-02-09 17:48:03 -0800326 mPhoneNumbers.removeAllViews();
327 mEmails.removeAllViews();
Walter Jang995bb4a2015-07-06 12:21:36 -0700328 mOtherTypes.removeAllViews();
329 mOtherTypesMap.clear();
Walter Jangcab3dce2015-02-09 17:48:03 -0800330
331 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
332 return;
333 }
334
335 mViewIdGenerator = viewIdGenerator;
Walter Jangcab3dce2015-02-09 17:48:03 -0800336 setId(mViewIdGenerator.getId(rawContactDeltas.get(0), /* dataKind =*/ null,
337 /* valuesDelta =*/ null, ViewIdGenerator.NO_VIEW_INDEX));
Walter Jangf46abd82015-02-20 16:52:04 -0800338 mMaterialPalette = materialPalette;
Walter Jang708ea9e2015-09-10 15:42:05 -0700339 mPhotoId = photoId;
340 mNameId = nameId;
341 mReadOnlyDisplayName = readOnlyDisplayName;
342 mHasNewContact = hasNewContact;
343 mIsUserProfile = isUserProfile;
344 mPrimaryAccount = primaryAccount;
345 if (mPrimaryAccount == null) {
346 mPrimaryAccount = ContactEditorUtils.getInstance(getContext()).getDefaultAccount();
347 }
348 vlog("state: primary " + mPrimaryAccount);
Walter Jangcab3dce2015-02-09 17:48:03 -0800349
Walter Jang708ea9e2015-09-10 15:42:05 -0700350 vlog("state: setting compact editor state from " + rawContactDeltas);
351 parseRawContactDeltas(rawContactDeltas);
352 addAccountInfo();
Walter Jangfa127a12015-06-18 09:48:18 -0700353 addPhotoView(rawContactDeltas, viewIdGenerator, photoId, readOnlyDisplayName);
Walter Jang06f73a12015-06-17 11:15:48 -0700354 addStructuredNameView(rawContactDeltas, nameId, readOnlyDisplayName);
Walter Jang3efae4a2015-02-18 11:12:00 -0800355 addEditorViews(rawContactDeltas);
Walter Jang19d985f2015-07-01 13:36:27 -0700356 updateKindEditorEmptyFields(mPhoneNumbers);
357 updateKindEditorIcons(mPhoneNumbers);
358 updateKindEditorEmptyFields(mEmails);
359 updateKindEditorIcons(mEmails);
Walter Jang995bb4a2015-07-06 12:21:36 -0700360 for (Map.Entry<String,LinearLayout> otherTypes : mOtherTypesMap.entrySet()) {
361 updateKindEditorIcons(otherTypes.getValue());
Walter Jang19d985f2015-07-01 13:36:27 -0700362 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800363 }
364
Walter Jang708ea9e2015-09-10 15:42:05 -0700365 private void parseRawContactDeltas(RawContactDeltaList rawContactDeltas) {
366 // Get the raw contact delta for the primary account (the one displayed at the top)
367 if (mPrimaryAccount == null || mPrimaryAccount.name == null
368 || mReadOnlyDisplayName != null) {
369 // Use the first writable contact if this is 1) a phone local contact or 2) an insert,
370 // for a read-only contact. For the last case, we can assume the first writable
371 // raw contact is the newly created one because inserts have a raw contact delta list
372 // of size 1 and read-only contacts have a list of size 2
373 for (RawContactDelta rawContactDelta : rawContactDeltas) {
374 if (!rawContactDelta.isVisible()) continue;
375 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
376 if (accountType != null && accountType.areContactsWritable()) {
377 vlog("parse: using first writable raw contact as primary");
378 mPrimaryRawContactDelta = rawContactDelta;
379 break;
380 }
381 }
382 } else {
383 // Use the first writable contact that matches the primary account
384 for (RawContactDelta rawContactDelta : rawContactDeltas) {
385 if (!rawContactDelta.isVisible()) continue;
386 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
387 if (accountType != null && accountType.areContactsWritable()
388 && Objects.equals(mPrimaryAccount.name, rawContactDelta.getAccountName())
389 && Objects.equals(mPrimaryAccount.type, rawContactDelta.getAccountType())
390 && Objects.equals(mPrimaryAccount.dataSet, rawContactDelta.getDataSet())) {
391 vlog("parse: matched the primary account raw contact");
392 mPrimaryRawContactDelta = rawContactDelta;
393 break;
394 }
Walter Jang2d3f31c2015-06-18 23:15:31 -0700395 }
396 }
Walter Jang708ea9e2015-09-10 15:42:05 -0700397 if (mPrimaryRawContactDelta == null) {
398 // Fall back to the first writable raw contact
399 for (RawContactDelta rawContactDelta : rawContactDeltas) {
400 if (!rawContactDelta.isVisible()) continue;
401 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
402 if (accountType != null && accountType.areContactsWritable()) {
403 vlog("parse: falling back to the first writable raw contact as primary");
404 mPrimaryRawContactDelta = rawContactDelta;
405 break;
406 }
407 }
Walter Jang2d3f31c2015-06-18 23:15:31 -0700408 }
Walter Jang2d3f31c2015-06-18 23:15:31 -0700409 }
410
Walter Jang151f3e62015-02-26 15:29:40 -0800411 private void addPhotoView(RawContactDeltaList rawContactDeltas,
Walter Jangfa127a12015-06-18 09:48:18 -0700412 ViewIdGenerator viewIdGenerator, long photoId, String readOnlyDisplayName) {
413 // If we're editing a read-only contact, the display name from the read-only
414 // contact is non empty and we can use it determine whether to back the photo editor with
415 // the empty new raw contact delta. See go/editing-read-only-contacts
416 final boolean readOnlyContact = !TextUtils.isEmpty(readOnlyDisplayName);
417 if (readOnlyContact) {
418 for (RawContactDelta rawContactDelta : rawContactDeltas) {
419 if (!rawContactDelta.isVisible()) continue;
420 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
421
422 // Make sure we have a photo
423 RawContactModifier.ensureKindExists(
424 rawContactDelta, accountType, Photo.CONTENT_ITEM_TYPE);
425
426 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
427 if (accountType.areContactsWritable()) {
428 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(
429 Photo.CONTENT_ITEM_TYPE)) {
430 if (valuesDelta != null) {
431 // Break the loop but don't return because we need to keep going to
432 // in order to show the photo from the read-only contact.
433 mPhotoRawContactId = rawContactDelta.getRawContactId();
434 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
435 /* readOnly =*/ false, mMaterialPalette, viewIdGenerator);
436 break;
437 }
438 }
439 }
440 }
441 }
442
Walter Jangac679af2015-06-01 12:17:06 -0700443 // Look for a match for the photo ID that was passed in
Walter Jangcab3dce2015-02-09 17:48:03 -0800444 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang3b210272015-03-04 11:34:11 -0800445 if (!rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800446 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jange720fde2015-02-17 10:54:14 -0800447
Walter Jang3efae4a2015-02-18 11:12:00 -0800448 // Make sure we have a photo
449 RawContactModifier.ensureKindExists(
450 rawContactDelta, accountType, Photo.CONTENT_ITEM_TYPE);
451
452 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
Walter Jangab50e6f2015-06-15 08:57:22 -0700453 if (dataKind != null && dataKind.editable) {
Walter Jangac679af2015-06-01 12:17:06 -0700454 for (ValuesDelta valuesDelta
455 : rawContactDelta.getMimeEntries(Photo.CONTENT_ITEM_TYPE)) {
456 if (valuesDelta != null && valuesDelta.getId() != null
457 && valuesDelta.getId().equals(photoId)) {
Walter Jangfa127a12015-06-18 09:48:18 -0700458 if (readOnlyContact) {
459 mPhoto.setPhoto(valuesDelta);
460 } else {
461 mPhotoRawContactId = rawContactDelta.getRawContactId();
462 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
463 !accountType.areContactsWritable(),
464 mMaterialPalette, viewIdGenerator);
465 }
Walter Jangac679af2015-06-01 12:17:06 -0700466 return;
467 }
468 }
469 }
470 }
471
472 // Look for a non-empty super primary photo
473 for (RawContactDelta rawContactDelta : rawContactDeltas) {
474 if (!rawContactDelta.isVisible()) continue;
475 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
476 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
Walter Jangab50e6f2015-06-15 08:57:22 -0700477 if (dataKind != null && dataKind.editable) {
Walter Jang3b210272015-03-04 11:34:11 -0800478 final ValuesDelta valuesDelta = getNonEmptySuperPrimaryValuesDeltas(
479 rawContactDelta, Photo.CONTENT_ITEM_TYPE, dataKind);
480 if (valuesDelta != null) {
Walter Jangfa127a12015-06-18 09:48:18 -0700481 if (readOnlyContact) {
482 mPhoto.setPhoto(valuesDelta);
483 } else {
484 mPhotoRawContactId = rawContactDelta.getRawContactId();
485 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
486 !accountType.areContactsWritable(), mMaterialPalette,
487 viewIdGenerator);
488 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800489 return;
490 }
491 }
492 }
Walter Jang3b210272015-03-04 11:34:11 -0800493 // We didn't find a non-empty super primary photo, use the first non-empty one
494 for (RawContactDelta rawContactDelta : rawContactDeltas) {
495 if (!rawContactDelta.isVisible()) continue;
496 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
497 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
Walter Jangab50e6f2015-06-15 08:57:22 -0700498 if (dataKind != null && dataKind.editable) {
Walter Jang3b210272015-03-04 11:34:11 -0800499 final List<ValuesDelta> valuesDeltas = getNonEmptyValuesDeltas(
500 rawContactDelta, Photo.CONTENT_ITEM_TYPE, dataKind);
501 if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
Walter Jangfa127a12015-06-18 09:48:18 -0700502 if (readOnlyContact) {
503 mPhoto.setPhoto(valuesDeltas.get(0));
504 } else {
505 mPhotoRawContactId = rawContactDelta.getRawContactId();
506 mPhoto.setValues(dataKind, valuesDeltas.get(0), rawContactDelta,
507 !accountType.areContactsWritable(), mMaterialPalette,
508 viewIdGenerator);
509 }
Walter Jang3b210272015-03-04 11:34:11 -0800510 return;
511 }
512 }
513 }
514 // No suitable non-empty photo
515 for (RawContactDelta rawContactDelta : rawContactDeltas) {
516 if (!rawContactDelta.isVisible()) continue;
517 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
518 final DataKind dataKind = accountType.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
Walter Jangab50e6f2015-06-15 08:57:22 -0700519 if (dataKind != null && dataKind.editable) {
Walter Jang3b210272015-03-04 11:34:11 -0800520 final ValuesDelta valuesDelta = rawContactDelta.getSuperPrimaryEntry(
521 dataKind.mimeType, /* forceSelection =*/ true);
522 if (valuesDelta != null) {
Walter Jangfa127a12015-06-18 09:48:18 -0700523 if (readOnlyContact) {
524 mPhoto.setPhoto(valuesDelta);
525 } else {
526 mPhotoRawContactId = rawContactDelta.getRawContactId();
527 mPhoto.setValues(dataKind, valuesDelta, rawContactDelta,
528 !accountType.areContactsWritable(), mMaterialPalette,
529 viewIdGenerator);
530 }
Walter Jang3b210272015-03-04 11:34:11 -0800531 return;
532 }
533 }
534 }
535 // Should not happen since we ensure the kind exists but if we unexpectedly get here
536 // we must remove the photo section so that it does not take up the entire view
537 mPhoto.setVisibility(View.GONE);
Walter Jang3efae4a2015-02-18 11:12:00 -0800538 }
539
Walter Jang708ea9e2015-09-10 15:42:05 -0700540 private void addAccountInfo() {
541 if (mPrimaryRawContactDelta == null) {
542 vlog("account info: hidden because no raw contact delta");
543 mAccountHeaderContainer.setVisibility(View.GONE);
544 mAccountSelectorContainer.setVisibility(View.GONE);
545 return;
546 }
547
548 // Get the account information for the primary raw contact delta
549 final Pair<String,String> accountInfo = EditorUiUtils.getAccountInfo(getContext(),
550 mIsUserProfile, mPrimaryRawContactDelta.getAccountName(),
551 mPrimaryRawContactDelta.getAccountType(mAccountTypeManager));
552
553 // The account header and selector show the same information so both shouldn't be visible
554 // at the same time
555 final List<AccountWithDataSet> accounts =
556 AccountTypeManager.getInstance(getContext()).getAccounts(true);
557 if (mHasNewContact && !mIsUserProfile && accounts.size() > 1) {
558 mAccountHeaderContainer.setVisibility(View.GONE);
559 addAccountSelector(accountInfo);
560 } else {
561 addAccountHeader(accountInfo);
562 mAccountSelectorContainer.setVisibility(View.GONE);
563 }
564 }
565
566 private void addAccountHeader(Pair<String,String> accountInfo) {
567 if (accountInfo.first == null) {
568 // Hide this view so the other text view will be centered vertically
569 mAccountHeaderName.setVisibility(View.GONE);
570 } else {
571 mAccountHeaderName.setVisibility(View.VISIBLE);
572 mAccountHeaderName.setText(accountInfo.first);
573 }
574 mAccountHeaderType.setText(accountInfo.second);
575
576 mAccountHeaderContainer.setContentDescription(
577 EditorUiUtils.getAccountInfoContentDescription(
578 accountInfo.first, accountInfo.second));
579 }
580
581 private void addAccountSelector(Pair<String,String> accountInfo) {
582 mAccountSelectorContainer.setVisibility(View.VISIBLE);
583
584 if (accountInfo.first == null) {
585 // Hide this view so the other text view will be centered vertically
586 mAccountSelectorName.setVisibility(View.GONE);
587 } else {
588 mAccountSelectorName.setVisibility(View.VISIBLE);
589 mAccountSelectorName.setText(accountInfo.first);
590 }
591 mAccountSelectorType.setText(accountInfo.second);
592
593 mAccountSelectorContainer.setContentDescription(
594 EditorUiUtils.getAccountInfoContentDescription(
595 accountInfo.first, accountInfo.second));
596
597 mAccountSelector.setOnClickListener(new View.OnClickListener() {
598 @Override
599 public void onClick(View v) {
600 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
601 final AccountsListAdapter adapter =
602 new AccountsListAdapter(getContext(),
603 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE,
604 mPrimaryAccount);
605 popup.setWidth(mAccountSelectorContainer.getWidth());
606 popup.setAnchorView(mAccountSelectorContainer);
607 popup.setAdapter(adapter);
608 popup.setModal(true);
609 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
610 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
611 @Override
612 public void onItemClick(AdapterView<?> parent, View view, int position,
613 long id) {
614 UiClosables.closeQuietly(popup);
615 final AccountWithDataSet newAccount = adapter.getItem(position);
616 if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
617 mListener.onRebindEditorsForNewContact(
618 mPrimaryRawContactDelta,
619 mPrimaryAccount,
620 newAccount);
621 }
622 }
623 });
624 popup.show();
625 }
626 });
627 }
628
Walter Jang06f73a12015-06-17 11:15:48 -0700629 private void addStructuredNameView(RawContactDeltaList rawContactDeltas, long nameId,
630 String readOnlyDisplayName) {
631 // If we're editing a read-only contact we want to display the name from the read-only
632 // contact in a structured name editor backed by the new raw contact that was created.
633 // The new raw contact is writable and merging it with the read-only contact allows us
634 // to edit the read-only contact. See go/editing-read-only-contacts
635 if (!TextUtils.isEmpty(readOnlyDisplayName)) {
636 for (RawContactDelta rawContactDelta : rawContactDeltas) {
637 if (!rawContactDelta.isVisible()) continue;
638 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
639
640 // Make sure we have a structured name
641 RawContactModifier.ensureKindExists(
642 rawContactDelta, accountType, StructuredName.CONTENT_ITEM_TYPE);
643
644 if (accountType.areContactsWritable()) {
645 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(
646 StructuredName.CONTENT_ITEM_TYPE)) {
647 if (valuesDelta != null) {
648 mNameValuesDelta = valuesDelta;
649 final NameEditorListener nameEditorListener = new NameEditorListener(
650 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
651 final StructuredNameEditorView nameEditorView =
652 inflateStructuredNameEditorView(mNames, accountType,
653 mNameValuesDelta, rawContactDelta, nameEditorListener,
654 !accountType.areContactsWritable());
655 nameEditorView.setDisplayName(readOnlyDisplayName);
656 mNames.addView(nameEditorView);
657 mDefaultNameEditorView = nameEditorView;
658 return;
659 }
660 }
661 }
662 }
663 }
664
Walter Jang65d35202015-06-16 13:08:34 -0700665 // Look for a match for the name ID that was passed in
Walter Jang10446452015-02-20 13:51:16 -0800666 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang151f3e62015-02-26 15:29:40 -0800667 if (!rawContactDelta.isVisible()) continue;
Walter Jang10446452015-02-20 13:51:16 -0800668 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
669
670 // Make sure we have a structured name
671 RawContactModifier.ensureKindExists(
672 rawContactDelta, accountType, StructuredName.CONTENT_ITEM_TYPE);
673
Walter Jang151f3e62015-02-26 15:29:40 -0800674 // Note use of pseudo mime type to get the DataKind and StructuredName to get value
Walter Jang10446452015-02-20 13:51:16 -0800675 final DataKind dataKind = accountType.getKindForMimetype(
Walter Jang151f3e62015-02-26 15:29:40 -0800676 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
677 if (dataKind == null || !dataKind.editable) continue;
678
Walter Jang398cd4b2015-06-16 11:17:53 -0700679 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(
680 StructuredName.CONTENT_ITEM_TYPE)) {
681 if (valuesDelta != null && valuesDelta.getId() != null
682 && valuesDelta.getId().equals(nameId)) {
683 mNameValuesDelta = valuesDelta;
684 final NameEditorListener nameEditorListener = new NameEditorListener(
685 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
686 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
Walter Jang65d35202015-06-16 13:08:34 -0700687 mNameValuesDelta, rawContactDelta, nameEditorListener,
688 !accountType.areContactsWritable()));
Walter Jang398cd4b2015-06-16 11:17:53 -0700689 return;
690 }
691 }
692 }
693 // Look for a super primary name
694 for (RawContactDelta rawContactDelta : rawContactDeltas) {
695 if (!rawContactDelta.isVisible()) continue;
696 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
697
698 final DataKind dataKind = accountType.getKindForMimetype(
699 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
700 if (dataKind == null || !dataKind.editable) continue;
701
Walter Jang151f3e62015-02-26 15:29:40 -0800702 final ValuesDelta superPrimaryValuesDelta = getNonEmptySuperPrimaryValuesDeltas(
703 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
704 if (superPrimaryValuesDelta != null) {
705 // Our first preference is for a non-empty super primary name
Walter Janga26490b2015-05-27 13:08:24 -0700706 final NameEditorListener nameEditorListener = new NameEditorListener(
707 superPrimaryValuesDelta, rawContactDelta.getRawContactId(), mListener);
Walter Jang151f3e62015-02-26 15:29:40 -0800708 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
Walter Jang65d35202015-06-16 13:08:34 -0700709 superPrimaryValuesDelta, rawContactDelta, nameEditorListener,
710 !accountType.areContactsWritable()));
Walter Jang151f3e62015-02-26 15:29:40 -0800711 return;
712 }
713 }
714 // We didn't find a super primary name
715 for (RawContactDelta rawContactDelta : rawContactDeltas) {
716 if (!rawContactDelta.isVisible()) continue;
717 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
718
719 final DataKind dataKind = accountType.getKindForMimetype(
720 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
721 if (dataKind == null || !dataKind.editable) continue;
722
723 final List<ValuesDelta> nonEmptyValuesDeltas = getNonEmptyValuesDeltas(
724 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
725 if (nonEmptyValuesDeltas != null && !nonEmptyValuesDeltas.isEmpty()) {
Walter Jang65d35202015-06-16 13:08:34 -0700726 // Take the first non-empty name
Walter Jang151f3e62015-02-26 15:29:40 -0800727 mNameValuesDelta = nonEmptyValuesDeltas.get(0);
728 final NameEditorListener nameEditorListener = new NameEditorListener(
729 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
730 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
Walter Jang65d35202015-06-16 13:08:34 -0700731 mNameValuesDelta, rawContactDelta, nameEditorListener,
732 !accountType.areContactsWritable()));
Walter Jang151f3e62015-02-26 15:29:40 -0800733 return;
734 }
735 }
736 for (RawContactDelta rawContactDelta : rawContactDeltas) {
737 if (!rawContactDelta.isVisible()) continue;
738 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
739
740 final DataKind dataKind = accountType.getKindForMimetype(
741 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
742 if (dataKind == null || !dataKind.editable) continue;
743
744 // Fall back to the first entry
745 final ArrayList<ValuesDelta> valuesDeltas = rawContactDelta.getMimeEntries(
Walter Jang10446452015-02-20 13:51:16 -0800746 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang151f3e62015-02-26 15:29:40 -0800747 if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
748 mNameValuesDelta = valuesDeltas.get(0);
749 final NameEditorListener nameEditorListener = new NameEditorListener(
750 mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
751 mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
Walter Jang65d35202015-06-16 13:08:34 -0700752 mNameValuesDelta, rawContactDelta, nameEditorListener,
753 !accountType.areContactsWritable()));
Walter Jang151f3e62015-02-26 15:29:40 -0800754 return;
Walter Jang10446452015-02-20 13:51:16 -0800755 }
756 }
757 }
758
Walter Jang3efae4a2015-02-18 11:12:00 -0800759 private void addEditorViews(RawContactDeltaList rawContactDeltas) {
760 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jangab50e6f2015-06-15 08:57:22 -0700761 if (!rawContactDelta.isVisible()) continue;
Walter Jang3efae4a2015-02-18 11:12:00 -0800762 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
763
Walter Jangcab3dce2015-02-09 17:48:03 -0800764 for (DataKind dataKind : accountType.getSortedDataKinds()) {
Walter Jangab50e6f2015-06-15 08:57:22 -0700765 if (!dataKind.editable) continue;
766
Walter Jangcab3dce2015-02-09 17:48:03 -0800767 final String mimeType = dataKind.mimeType;
Walter Jangbf63a6d2015-05-05 09:14:35 -0700768 vlog(mimeType + " " + dataKind.fieldList.size() + " field(s)");
Walter Jangcab3dce2015-02-09 17:48:03 -0800769 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jang10446452015-02-20 13:51:16 -0800770 || StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangcab3dce2015-02-09 17:48:03 -0800771 || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800772 // Photos and structured names are handled separately and
773 // group membership is not supported
Walter Jangcab3dce2015-02-09 17:48:03 -0800774 continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800775 } else if (DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
Walter Jang151f3e62015-02-26 15:29:40 -0800776 // Only add phonetic names if there is a non-empty one. Note the use of
777 // StructuredName mimeType below, even though we matched a pseudo mime type.
Walter Jang3a37a1a2015-03-04 07:41:32 -0800778 final ValuesDelta valuesDelta = rawContactDelta.getSuperPrimaryEntry(
779 StructuredName.CONTENT_ITEM_TYPE, /* forceSelection =*/ true);
780 if (hasNonEmptyValue(dataKind, valuesDelta)) {
781 mPhoneticNames.addView(inflatePhoneticNameEditorView(
782 mPhoneticNames, accountType, valuesDelta, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800783 }
784 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang995bb4a2015-07-06 12:21:36 -0700785 // Add all non-empty nicknames
786 final List<ValuesDelta> valuesDeltas = getNonEmptyValuesDeltas(
787 rawContactDelta, Nickname.CONTENT_ITEM_TYPE, dataKind);
788 if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
Walter Jangae9c9ec2015-07-07 16:10:41 -0700789 for (ValuesDelta valuesDelta : valuesDeltas) {
Walter Jang995bb4a2015-07-06 12:21:36 -0700790 mNicknames.addView(inflateNicknameEditorView(
791 mNicknames, dataKind, valuesDelta, rawContactDelta));
792 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800793 }
794 } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang4a9351d2015-06-30 14:09:10 -0700795 final KindSectionView kindSectionView =
796 inflateKindSectionView(mPhoneNumbers, dataKind, rawContactDelta);
797 kindSectionView.setListener(new KindSectionView.Listener() {
798 @Override
Walter Jang995bb4a2015-07-06 12:21:36 -0700799 public void onDeleteRequested(Editor editor) {
800 if (kindSectionView.getEditorCount() == 1) {
801 kindSectionView.markForRemoval();
802 EditorAnimator.getInstance().removeEditorView(kindSectionView);
803 } else {
804 editor.deleteEditor();
805 }
Walter Jang19d985f2015-07-01 13:36:27 -0700806 updateKindEditorEmptyFields(mPhoneNumbers);
807 updateKindEditorIcons(mPhoneNumbers);
Walter Jang4a9351d2015-06-30 14:09:10 -0700808 }
809 });
810 mPhoneNumbers.addView(kindSectionView);
Walter Jangcab3dce2015-02-09 17:48:03 -0800811 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang4a9351d2015-06-30 14:09:10 -0700812 final KindSectionView kindSectionView =
813 inflateKindSectionView(mEmails, dataKind, rawContactDelta);
814 kindSectionView.setListener(new KindSectionView.Listener() {
815 @Override
Walter Jang995bb4a2015-07-06 12:21:36 -0700816 public void onDeleteRequested(Editor editor) {
817 if (kindSectionView.getEditorCount() == 1) {
818 kindSectionView.markForRemoval();
819 EditorAnimator.getInstance().removeEditorView(kindSectionView);
820 } else {
821 editor.deleteEditor();
822 }
Walter Jang19d985f2015-07-01 13:36:27 -0700823 updateKindEditorEmptyFields(mEmails);
824 updateKindEditorIcons(mEmails);
Walter Jang4a9351d2015-06-30 14:09:10 -0700825 }
826 });
827 mEmails.addView(kindSectionView);
Walter Jangcab3dce2015-02-09 17:48:03 -0800828 } else if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
Walter Jang995bb4a2015-07-06 12:21:36 -0700829 final LinearLayout otherTypeViewGroup;
830 if (mOtherTypesMap.containsKey(mimeType)) {
831 otherTypeViewGroup = mOtherTypesMap.get(mimeType);
Walter Jang19d985f2015-07-01 13:36:27 -0700832 } else {
Walter Jang995bb4a2015-07-06 12:21:36 -0700833 otherTypeViewGroup = new LinearLayout(getContext());
834 otherTypeViewGroup.setOrientation(LinearLayout.VERTICAL);
835 mOtherTypes.addView(otherTypeViewGroup);
836 mOtherTypesMap.put(mimeType, otherTypeViewGroup);
Walter Jang19d985f2015-07-01 13:36:27 -0700837 }
838 final KindSectionView kindSectionView =
Walter Jang995bb4a2015-07-06 12:21:36 -0700839 inflateKindSectionView(mOtherTypes, dataKind, rawContactDelta);
Walter Jang19d985f2015-07-01 13:36:27 -0700840 kindSectionView.setListener(new KindSectionView.Listener() {
841 @Override
Walter Jang995bb4a2015-07-06 12:21:36 -0700842 public void onDeleteRequested(Editor editor) {
843 if (kindSectionView.getEditorCount() == 1) {
844 kindSectionView.markForRemoval();
845 EditorAnimator.getInstance().removeEditorView(kindSectionView);
846 } else {
847 editor.deleteEditor();
Walter Jang19d985f2015-07-01 13:36:27 -0700848 }
Walter Jang995bb4a2015-07-06 12:21:36 -0700849 updateKindEditorIcons(otherTypeViewGroup);
Walter Jang19d985f2015-07-01 13:36:27 -0700850 }
851 });
Walter Jang995bb4a2015-07-06 12:21:36 -0700852 otherTypeViewGroup.addView(kindSectionView);
Walter Jangcab3dce2015-02-09 17:48:03 -0800853 }
854 }
855 }
856 }
857
Walter Jang19d985f2015-07-01 13:36:27 -0700858 private static void updateKindEditorEmptyFields(ViewGroup viewGroup) {
859 KindSectionView lastVisibleKindSectionView = null;
860 for (int i = 0; i < viewGroup.getChildCount(); i++) {
861 if (viewGroup.getChildAt(i).getVisibility() == View.VISIBLE) {
862 lastVisibleKindSectionView = (KindSectionView) viewGroup.getChildAt(i);
Walter Jangb1c87622015-02-13 17:51:38 -0800863 }
Walter Jang4a9351d2015-06-30 14:09:10 -0700864 }
Walter Jang19d985f2015-07-01 13:36:27 -0700865 // Only the last editor should show an empty editor
866 if (lastVisibleKindSectionView != null) {
Walter Jangae9c9ec2015-07-07 16:10:41 -0700867 // Hide all empty kind sections except the last one
868 for (int i = 0; i < viewGroup.getChildCount(); i++) {
869 final KindSectionView kindSectionView = (KindSectionView) viewGroup.getChildAt(i);
870 if (kindSectionView != lastVisibleKindSectionView
871 && kindSectionView.areAllEditorsEmpty()) {
872 kindSectionView.setVisibility(View.GONE);
873 }
874 }
875 // Set the last editor to show empty editor fields
Walter Jang19d985f2015-07-01 13:36:27 -0700876 lastVisibleKindSectionView.setShowOneEmptyEditor(true);
877 lastVisibleKindSectionView.updateEmptyEditors(/* shouldAnimate =*/ false);
878 }
879 }
Walter Jang4a9351d2015-06-30 14:09:10 -0700880
Walter Jang19d985f2015-07-01 13:36:27 -0700881 private static void updateKindEditorIcons(ViewGroup viewGroup) {
Walter Jang4a9351d2015-06-30 14:09:10 -0700882 // Show the icon on the first visible kind editor
883 boolean iconVisible = false;
884 for (int i = 0; i < viewGroup.getChildCount(); i++) {
885 final KindSectionView kindSectionView = (KindSectionView) viewGroup.getChildAt(i);
Walter Jang995bb4a2015-07-06 12:21:36 -0700886 if (kindSectionView.getVisibility() != View.VISIBLE
887 || kindSectionView.isMarkedForRemoval()) {
Walter Jang4a9351d2015-06-30 14:09:10 -0700888 continue;
889 }
890 if (!iconVisible) {
891 kindSectionView.setIconVisibility(true);
892 iconVisible = true;
893 } else {
894 kindSectionView.setIconVisibility(false);
Walter Jangb1c87622015-02-13 17:51:38 -0800895 }
896 }
897 }
898
Walter Jangcab3dce2015-02-09 17:48:03 -0800899 private static boolean hasNonEmptyValuesDelta(RawContactDelta rawContactDelta,
900 String mimeType, DataKind dataKind) {
901 return !getNonEmptyValuesDeltas(rawContactDelta, mimeType, dataKind).isEmpty();
902 }
903
Walter Jang151f3e62015-02-26 15:29:40 -0800904 private static ValuesDelta getNonEmptySuperPrimaryValuesDeltas(RawContactDelta rawContactDelta,
905 String mimeType, DataKind dataKind) {
906 for (ValuesDelta valuesDelta : getNonEmptyValuesDeltas(
907 rawContactDelta, mimeType, dataKind)) {
908 if (valuesDelta.isSuperPrimary()) {
909 return valuesDelta;
910 }
911 }
912 return null;
913 }
914
915 static List<ValuesDelta> getNonEmptyValuesDeltas(RawContactDelta rawContactDelta,
Walter Jangcab3dce2015-02-09 17:48:03 -0800916 String mimeType, DataKind dataKind) {
917 final List<ValuesDelta> result = new ArrayList<>();
918 if (rawContactDelta == null) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700919 vlog("Null RawContactDelta");
Walter Jangcab3dce2015-02-09 17:48:03 -0800920 return result;
921 }
922 if (!rawContactDelta.hasMimeEntries(mimeType)) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700923 vlog("No ValueDeltas");
Walter Jangcab3dce2015-02-09 17:48:03 -0800924 return result;
925 }
926 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(mimeType)) {
Walter Jang3a37a1a2015-03-04 07:41:32 -0800927 if (hasNonEmptyValue(dataKind, valuesDelta)) {
928 result.add(valuesDelta);
Walter Jangcab3dce2015-02-09 17:48:03 -0800929 }
930 }
931 return result;
932 }
933
Walter Jang3a37a1a2015-03-04 07:41:32 -0800934 private static boolean hasNonEmptyValue(DataKind dataKind, ValuesDelta valuesDelta) {
935 if (valuesDelta == null) {
Walter Jangbf63a6d2015-05-05 09:14:35 -0700936 vlog("Null valuesDelta");
Walter Jang3a37a1a2015-03-04 07:41:32 -0800937 return false;
938 }
939 for (EditField editField : dataKind.fieldList) {
940 final String column = editField.column;
941 final String value = valuesDelta == null ? null : valuesDelta.getAsString(column);
Walter Jangbf63a6d2015-05-05 09:14:35 -0700942 vlog("Field " + column + " empty=" + TextUtils.isEmpty(value) + " value=" + value);
Walter Jang3a37a1a2015-03-04 07:41:32 -0800943 if (!TextUtils.isEmpty(value)) {
944 return true;
945 }
946 }
947 return false;
948 }
949
Walter Jangcab3dce2015-02-09 17:48:03 -0800950 private StructuredNameEditorView inflateStructuredNameEditorView(ViewGroup viewGroup,
Walter Jang151f3e62015-02-26 15:29:40 -0800951 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta,
Walter Jang65d35202015-06-16 13:08:34 -0700952 NameEditorListener nameEditorListener, boolean readOnly) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800953 final StructuredNameEditorView result = (StructuredNameEditorView) mLayoutInflater.inflate(
954 R.layout.structured_name_editor_view, viewGroup, /* attachToRoot =*/ false);
Walter Jang151f3e62015-02-26 15:29:40 -0800955 if (nameEditorListener != null) {
956 result.setEditorListener(nameEditorListener);
957 }
Walter Jang3a37a1a2015-03-04 07:41:32 -0800958 result.setDeletable(false);
Walter Jangcab3dce2015-02-09 17:48:03 -0800959 result.setValues(
960 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME),
961 valuesDelta,
962 rawContactDelta,
Walter Jang65d35202015-06-16 13:08:34 -0700963 readOnly,
Walter Jangcab3dce2015-02-09 17:48:03 -0800964 mViewIdGenerator);
965 return result;
966 }
967
968 private PhoneticNameEditorView inflatePhoneticNameEditorView(ViewGroup viewGroup,
969 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta) {
970 final PhoneticNameEditorView result = (PhoneticNameEditorView) mLayoutInflater.inflate(
971 R.layout.phonetic_name_editor_view, viewGroup, /* attachToRoot =*/ false);
Walter Jang3a37a1a2015-03-04 07:41:32 -0800972 result.setDeletable(false);
Walter Jangcab3dce2015-02-09 17:48:03 -0800973 result.setValues(
974 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME),
975 valuesDelta,
976 rawContactDelta,
977 /* readOnly =*/ false,
978 mViewIdGenerator);
979 return result;
980 }
981
Walter Jang995bb4a2015-07-06 12:21:36 -0700982 private TextFieldsEditorView inflateNicknameEditorView(ViewGroup viewGroup, DataKind dataKind,
983 ValuesDelta valuesDelta, RawContactDelta rawContactDelta) {
984 final TextFieldsEditorView result = (TextFieldsEditorView) mLayoutInflater.inflate(
985 R.layout.nick_name_editor_view, viewGroup, /* attachToRoot =*/ false);
986 result.setDeletable(false);
987 result.setValues(
988 dataKind,
989 valuesDelta,
990 rawContactDelta,
991 /* readOnly =*/ false,
992 mViewIdGenerator);
993 return result;
994 }
995
996
Walter Jangcab3dce2015-02-09 17:48:03 -0800997 private KindSectionView inflateKindSectionView(ViewGroup viewGroup, DataKind dataKind,
Walter Jang88b0a8c2015-06-08 13:15:56 -0700998 RawContactDelta rawContactDelta) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800999 final KindSectionView result = (KindSectionView) mLayoutInflater.inflate(
1000 R.layout.item_kind_section, viewGroup, /* attachToRoot =*/ false);
1001 result.setState(
1002 dataKind,
1003 rawContactDelta,
1004 /* readOnly =*/ false,
Walter Jangcab3dce2015-02-09 17:48:03 -08001005 mViewIdGenerator);
1006 return result;
1007 }
1008
Walter Jangbf63a6d2015-05-05 09:14:35 -07001009 private static void vlog(String message) {
1010 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1011 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -08001012 }
1013 }
1014}