blob: 1b37dabf177718570c233528fa1b64f50e92048e [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
Walter Jangcab3dce2015-02-09 17:48:03 -080019import android.content.Context;
Gary Mai65971d02016-09-15 15:00:16 -070020import android.content.res.Resources;
Walter Jangf10ca152015-09-22 15:23:55 -070021import android.database.Cursor;
Gary Mai65971d02016-09-15 15:00:16 -070022import android.graphics.drawable.Drawable;
Walter Jang41b3ea12015-03-09 17:30:06 -070023import android.net.Uri;
Walter Jang79658e12015-09-24 10:36:26 -070024import android.os.Parcel;
25import android.os.Parcelable;
Walter Jangcab3dce2015-02-09 17:48:03 -080026import android.provider.ContactsContract.CommonDataKinds.Email;
Walter Jangf5dfea42015-09-16 12:30:36 -070027import android.provider.ContactsContract.CommonDataKinds.Event;
Walter Jangcab3dce2015-02-09 17:48:03 -080028import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Walter Jangf5dfea42015-09-16 12:30:36 -070029import android.provider.ContactsContract.CommonDataKinds.Im;
Walter Jang3efae4a2015-02-18 11:12:00 -080030import android.provider.ContactsContract.CommonDataKinds.Nickname;
Walter Jangf5dfea42015-09-16 12:30:36 -070031import android.provider.ContactsContract.CommonDataKinds.Note;
32import android.provider.ContactsContract.CommonDataKinds.Organization;
Walter Jangcab3dce2015-02-09 17:48:03 -080033import android.provider.ContactsContract.CommonDataKinds.Phone;
34import android.provider.ContactsContract.CommonDataKinds.Photo;
Walter Jangf5dfea42015-09-16 12:30:36 -070035import android.provider.ContactsContract.CommonDataKinds.Relation;
36import android.provider.ContactsContract.CommonDataKinds.SipAddress;
Walter Jangcab3dce2015-02-09 17:48:03 -080037import android.provider.ContactsContract.CommonDataKinds.StructuredName;
Walter Jangf5dfea42015-09-16 12:30:36 -070038import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
39import android.provider.ContactsContract.CommonDataKinds.Website;
Walter Jangcab3dce2015-02-09 17:48:03 -080040import 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;
Walter Jang708ea9e2015-09-10 15:42:05 -070046import android.widget.AdapterView;
Tingting Wang655ad1a2015-10-05 17:51:14 -070047import android.widget.ImageView;
Walter Jangcab3dce2015-02-09 17:48:03 -080048import android.widget.LinearLayout;
Walter Jang708ea9e2015-09-10 15:42:05 -070049import android.widget.ListPopupWindow;
Walter Jang2d3f31c2015-06-18 23:15:31 -070050import android.widget.TextView;
Walter Jangcab3dce2015-02-09 17:48:03 -080051
Gary Maib6e28d22016-09-12 14:04:53 -070052import com.android.contacts.R;
Gary Mai65971d02016-09-15 15:00:16 -070053import com.android.contacts.common.GeoUtil;
54import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
Gary Maib6e28d22016-09-12 14:04:53 -070055import com.android.contacts.common.model.AccountTypeManager;
56import com.android.contacts.common.model.RawContactDelta;
57import com.android.contacts.common.model.RawContactDeltaList;
58import com.android.contacts.common.model.RawContactModifier;
59import com.android.contacts.common.model.ValuesDelta;
60import com.android.contacts.common.model.account.AccountDisplayInfo;
61import com.android.contacts.common.model.account.AccountDisplayInfoFactory;
62import com.android.contacts.common.model.account.AccountType;
63import com.android.contacts.common.model.account.AccountWithDataSet;
Gary Mai98868d32016-09-14 11:55:04 -070064import com.android.contacts.common.model.dataitem.CustomDataItem;
Gary Maib6e28d22016-09-12 14:04:53 -070065import com.android.contacts.common.model.dataitem.DataKind;
66import com.android.contacts.common.util.AccountsListAdapter;
67import com.android.contacts.common.util.MaterialColorMapUtils;
68import com.android.contacts.util.UiClosables;
69
Walter Jang708774a2015-10-16 09:32:06 -070070import java.io.FileNotFoundException;
Walter Jangcab3dce2015-02-09 17:48:03 -080071import java.util.ArrayList;
Walter Jangf5dfea42015-09-16 12:30:36 -070072import java.util.Arrays;
73import java.util.Comparator;
Walter Jang19d985f2015-07-01 13:36:27 -070074import java.util.HashMap;
Walter Jangcab3dce2015-02-09 17:48:03 -080075import java.util.List;
Walter Jang19d985f2015-07-01 13:36:27 -070076import java.util.Map;
Gary Mai98868d32016-09-14 11:55:04 -070077import java.util.Set;
Walter Jangf5dfea42015-09-16 12:30:36 -070078import java.util.TreeSet;
Walter Jangcab3dce2015-02-09 17:48:03 -080079
80/**
Walter Jangf5dfea42015-09-16 12:30:36 -070081 * View to display information from multiple {@link RawContactDelta}s grouped together.
Walter Jangcab3dce2015-02-09 17:48:03 -080082 */
Gary Mai363af602016-09-28 10:01:23 -070083public class RawContactEditorView extends LinearLayout implements View.OnClickListener {
Walter Jangcab3dce2015-02-09 17:48:03 -080084
Gary Mai363af602016-09-28 10:01:23 -070085 static final String TAG = "RawContactEditorView";
Walter Jangcab3dce2015-02-09 17:48:03 -080086
Walter Jangb6ca2722015-02-20 11:10:25 -080087 /**
Gary Mai363af602016-09-28 10:01:23 -070088 * Callbacks for hosts of {@link RawContactEditorView}s.
Walter Jangb6ca2722015-02-20 11:10:25 -080089 */
90 public interface Listener {
91
92 /**
Walter Jang151f3e62015-02-26 15:29:40 -080093 * Invoked when the structured name editor field has changed.
94 *
95 * @param rawContactId The raw contact ID from the underlying {@link RawContactDelta}.
96 * @param valuesDelta The values from the underlying {@link RawContactDelta}.
97 */
98 public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta);
Walter Jang708ea9e2015-09-10 15:42:05 -070099
100 /**
Gary Mai363af602016-09-28 10:01:23 -0700101 * Invoked when the editor should rebind editors for a new account.
Walter Jang708ea9e2015-09-10 15:42:05 -0700102 *
103 * @param oldState Old data being edited.
104 * @param oldAccount Old account associated with oldState.
105 * @param newAccount New account to be used.
106 */
107 public void onRebindEditorsForNewContact(RawContactDelta oldState,
108 AccountWithDataSet oldAccount, AccountWithDataSet newAccount);
Walter Jang79658e12015-09-24 10:36:26 -0700109
110 /**
111 * Invoked when no editors could be bound for the contact.
112 */
113 public void onBindEditorsFailed();
Walter Jangd6753152015-10-02 09:23:13 -0700114
115 /**
116 * Invoked after editors have been bound for the contact.
117 */
118 public void onEditorsBound();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700119 }
Walter Jang192a01c2015-09-22 15:23:55 -0700120 /**
121 * Sorts kinds roughly the same as quick contacts; we diverge in the following ways:
122 * <ol>
123 * <li>All names are together at the top.</li>
124 * <li>IM is moved up after addresses</li>
125 * <li>SIP addresses are moved to below phone numbers</li>
Walter Jang79658e12015-09-24 10:36:26 -0700126 * <li>Group membership is placed at the end</li>
Walter Jang192a01c2015-09-22 15:23:55 -0700127 * </ol>
128 */
129 private static final class MimeTypeComparator implements Comparator<String> {
130
Walter Jangf5dfea42015-09-16 12:30:36 -0700131 private static final List<String> MIME_TYPE_ORDER = Arrays.asList(new String[] {
132 StructuredName.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700133 Nickname.CONTENT_ITEM_TYPE,
Walter Jang2ae21752015-09-25 11:25:38 -0700134 Organization.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700135 Phone.CONTENT_ITEM_TYPE,
136 SipAddress.CONTENT_ITEM_TYPE,
137 Email.CONTENT_ITEM_TYPE,
138 StructuredPostal.CONTENT_ITEM_TYPE,
139 Im.CONTENT_ITEM_TYPE,
140 Website.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700141 Event.CONTENT_ITEM_TYPE,
142 Relation.CONTENT_ITEM_TYPE,
Walter Jangf10ca152015-09-22 15:23:55 -0700143 Note.CONTENT_ITEM_TYPE,
144 GroupMembership.CONTENT_ITEM_TYPE
Walter Jangf5dfea42015-09-16 12:30:36 -0700145 });
146
147 @Override
Walter Jang192a01c2015-09-22 15:23:55 -0700148 public int compare(String mimeType1, String mimeType2) {
149 if (mimeType1 == mimeType2) return 0;
150 if (mimeType1 == null) return -1;
151 if (mimeType2 == null) return 1;
Walter Jangf5dfea42015-09-16 12:30:36 -0700152
153 int index1 = MIME_TYPE_ORDER.indexOf(mimeType1);
154 int index2 = MIME_TYPE_ORDER.indexOf(mimeType2);
155
156 // Fallback to alphabetical ordering of the mime type if both are not found
157 if (index1 < 0 && index2 < 0) return mimeType1.compareTo(mimeType2);
158 if (index1 < 0) return 1;
159 if (index2 < 0) return -1;
160
161 return index1 < index2 ? -1 : 1;
162 }
163 }
164
Walter Jang79658e12015-09-24 10:36:26 -0700165 public static class SavedState extends BaseSavedState {
166
167 public static final Parcelable.Creator<SavedState> CREATOR =
168 new Parcelable.Creator<SavedState>() {
169 public SavedState createFromParcel(Parcel in) {
170 return new SavedState(in);
171 }
172 public SavedState[] newArray(int size) {
173 return new SavedState[size];
174 }
175 };
176
177 private boolean mIsExpanded;
178
179 public SavedState(Parcelable superState) {
180 super(superState);
181 }
182
183 private SavedState(Parcel in) {
184 super(in);
185 mIsExpanded = in.readInt() != 0;
186 }
187
188 @Override
189 public void writeToParcel(Parcel out, int flags) {
190 super.writeToParcel(out, flags);
191 out.writeInt(mIsExpanded ? 1 : 0);
192 }
193 }
194
Gary Mai363af602016-09-28 10:01:23 -0700195 private RawContactEditorView.Listener mListener;
Walter Jangb6ca2722015-02-20 11:10:25 -0800196
Walter Jangcab3dce2015-02-09 17:48:03 -0800197 private AccountTypeManager mAccountTypeManager;
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700198 private AccountDisplayInfoFactory mAccountDisplayInfoFactory;
Walter Jangcab3dce2015-02-09 17:48:03 -0800199 private LayoutInflater mLayoutInflater;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800200
Walter Jangcab3dce2015-02-09 17:48:03 -0800201 private ViewIdGenerator mViewIdGenerator;
Walter Jangf46abd82015-02-20 16:52:04 -0800202 private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
Walter Jang708ea9e2015-09-10 15:42:05 -0700203 private boolean mHasNewContact;
204 private boolean mIsUserProfile;
205 private AccountWithDataSet mPrimaryAccount;
Walter Jang9a552372016-08-24 11:51:05 -0700206 private RawContactDeltaList mRawContactDeltas;
Gary Mai4ceabed2016-09-16 12:14:13 -0700207 private RawContactDelta mCurrentRawContactDelta;
Walter Jang9a552372016-08-24 11:51:05 -0700208 private long mRawContactIdToDisplayAlone = -1;
Gary Mai98868d32016-09-14 11:55:04 -0700209 private Map<String, KindSectionData> mKindSectionDataMap = new HashMap<>();
210 private Set<String> mSortedMimetypes = new TreeSet<>(new MimeTypeComparator());
Walter Jangcab3dce2015-02-09 17:48:03 -0800211
Walter Jang708ea9e2015-09-10 15:42:05 -0700212 // Account header
213 private View mAccountHeaderContainer;
214 private TextView mAccountHeaderType;
215 private TextView mAccountHeaderName;
Tingting Wang91cee282015-10-07 13:48:17 -0700216 private ImageView mAccountHeaderIcon;
Gary Mai46cb3102016-08-10 18:14:09 -0700217 private ImageView mAccountHeaderExpanderIcon;
Walter Jang708ea9e2015-09-10 15:42:05 -0700218
Gary Mai363af602016-09-28 10:01:23 -0700219 private PhotoEditorView mPhotoView;
Walter Jangf5dfea42015-09-16 12:30:36 -0700220 private ViewGroup mKindSectionViews;
Gary Mai363af602016-09-28 10:01:23 -0700221 private Map<String, KindSectionView> mKindSectionViewMap = new HashMap<>();
Walter Jangb6ca2722015-02-20 11:10:25 -0800222 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -0800223
Walter Jang79658e12015-09-24 10:36:26 -0700224 private boolean mIsExpanded;
Walter Jangcbd431d2015-10-06 13:07:30 -0700225
Walter Jang31a74ad2015-10-02 19:17:39 -0700226 private ValuesDelta mPhotoValuesDelta;
Walter Jang82acd422015-10-17 14:01:27 -0700227
Gary Mai363af602016-09-28 10:01:23 -0700228 public RawContactEditorView(Context context) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800229 super(context);
230 }
231
Gary Mai363af602016-09-28 10:01:23 -0700232 public RawContactEditorView(Context context, AttributeSet attrs) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800233 super(context, attrs);
234 }
235
Walter Jangb6ca2722015-02-20 11:10:25 -0800236 /**
Gary Mai363af602016-09-28 10:01:23 -0700237 * Sets the receiver for {@link RawContactEditorView} callbacks.
Walter Jangb6ca2722015-02-20 11:10:25 -0800238 */
239 public void setListener(Listener listener) {
240 mListener = listener;
241 }
242
Walter Jangcab3dce2015-02-09 17:48:03 -0800243 @Override
244 protected void onFinishInflate() {
245 super.onFinishInflate();
246
247 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700248 mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forWritableAccounts(getContext());
Walter Jangcab3dce2015-02-09 17:48:03 -0800249 mLayoutInflater = (LayoutInflater)
250 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
251
Walter Jang708ea9e2015-09-10 15:42:05 -0700252 // Account header
Gary Mai46cb3102016-08-10 18:14:09 -0700253 mAccountHeaderContainer = findViewById(R.id.account_header_container);
Walter Jang708ea9e2015-09-10 15:42:05 -0700254 mAccountHeaderType = (TextView) findViewById(R.id.account_type);
255 mAccountHeaderName = (TextView) findViewById(R.id.account_name);
Tingting Wang91cee282015-10-07 13:48:17 -0700256 mAccountHeaderIcon = (ImageView) findViewById(R.id.account_type_icon);
Gary Mai46cb3102016-08-10 18:14:09 -0700257 mAccountHeaderExpanderIcon = (ImageView) findViewById(R.id.account_expander_icon);
Walter Jang708ea9e2015-09-10 15:42:05 -0700258
Gary Mai363af602016-09-28 10:01:23 -0700259 mPhotoView = (PhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangf5dfea42015-09-16 12:30:36 -0700260 mKindSectionViews = (LinearLayout) findViewById(R.id.kind_section_views);
Walter Jangb6ca2722015-02-20 11:10:25 -0800261 mMoreFields = findViewById(R.id.more_fields);
262 mMoreFields.setOnClickListener(this);
263 }
264
Walter Jangb6ca2722015-02-20 11:10:25 -0800265 @Override
266 public void onClick(View view) {
Walter Jangf5dfea42015-09-16 12:30:36 -0700267 if (view.getId() == R.id.more_fields) {
Walter Jangd6753152015-10-02 09:23:13 -0700268 showAllFields();
Walter Jangb6ca2722015-02-20 11:10:25 -0800269 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800270 }
271
272 @Override
273 public void setEnabled(boolean enabled) {
274 super.setEnabled(enabled);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700275 final int childCount = mKindSectionViews.getChildCount();
276 for (int i = 0; i < childCount; i++) {
277 mKindSectionViews.getChildAt(i).setEnabled(enabled);
Walter Jangcab3dce2015-02-09 17:48:03 -0800278 }
279 }
280
Walter Jang79658e12015-09-24 10:36:26 -0700281 @Override
282 public Parcelable onSaveInstanceState() {
283 final Parcelable superState = super.onSaveInstanceState();
284 final SavedState savedState = new SavedState(superState);
285 savedState.mIsExpanded = mIsExpanded;
286 return savedState;
287 }
288
289 @Override
290 public void onRestoreInstanceState(Parcelable state) {
291 if(!(state instanceof SavedState)) {
292 super.onRestoreInstanceState(state);
293 return;
294 }
295 final SavedState savedState = (SavedState) state;
296 super.onRestoreInstanceState(savedState.getSuperState());
297 mIsExpanded = savedState.mIsExpanded;
Walter Jangd6753152015-10-02 09:23:13 -0700298 if (mIsExpanded) {
299 showAllFields();
Walter Jang79658e12015-09-24 10:36:26 -0700300 }
301 }
302
Walter Jang3efae4a2015-02-18 11:12:00 -0800303 /**
Gary Mai363af602016-09-28 10:01:23 -0700304 * Pass through to {@link PhotoEditorView#setListener}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800305 */
Gary Mai363af602016-09-28 10:01:23 -0700306 public void setPhotoListener(PhotoEditorView.Listener listener) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700307 mPhotoView.setListener(listener);
Walter Jang3efae4a2015-02-18 11:12:00 -0800308 }
309
Walter Jang31a74ad2015-10-02 19:17:39 -0700310 public void removePhoto() {
Walter Jang45b86d52015-10-15 15:23:16 -0700311 mPhotoValuesDelta.setFromTemplate(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700312 mPhotoValuesDelta.put(Photo.PHOTO, (byte[]) null);
Gary Mai18724182016-09-20 17:33:30 -0700313 mPhotoValuesDelta.put(Photo.PHOTO_FILE_ID, (String) null);
Walter Jang31a74ad2015-10-02 19:17:39 -0700314
315 mPhotoView.removePhoto();
Walter Jang3efae4a2015-02-18 11:12:00 -0800316 }
317
318 /**
Gary Mai363af602016-09-28 10:01:23 -0700319 * Pass through to {@link PhotoEditorView#setFullSizedPhoto(Uri)}.
Walter Jang41b3ea12015-03-09 17:30:06 -0700320 */
321 public void setFullSizePhoto(Uri photoUri) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700322 mPhotoView.setFullSizedPhoto(photoUri);
Walter Jang41b3ea12015-03-09 17:30:06 -0700323 }
324
Walter Jang31a74ad2015-10-02 19:17:39 -0700325 public void updatePhoto(Uri photoUri) {
Walter Jang45b86d52015-10-15 15:23:16 -0700326 mPhotoValuesDelta.setFromTemplate(false);
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700327 // Unset primary for all photos
Wenyi Wang9086fb92015-11-16 09:58:20 -0800328 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700329 // Mark the currently displayed photo as primary
330 mPhotoValuesDelta.setSuperPrimary(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700331
Wenyi Wang4c9cf3a2015-10-15 17:58:23 -0700332 // Even though high-res photos cannot be saved by passing them via
333 // an EntityDeltaList (since they cause the Bundle size limit to be
334 // exceeded), we still pass a low-res thumbnail. This simplifies
335 // code all over the place, because we don't have to test whether
336 // there is a change in EITHER the delta-list OR a changed photo...
337 // this way, there is always a change in the delta-list.
338 try {
339 final byte[] bytes = EditorUiUtils.getCompressedThumbnailBitmapBytes(
340 getContext(), photoUri);
341 if (bytes != null) {
342 mPhotoValuesDelta.setPhoto(bytes);
343 }
344 } catch (FileNotFoundException e) {
345 elog("Failed to get bitmap from photo Uri");
346 }
347
Walter Jang31a74ad2015-10-02 19:17:39 -0700348 mPhotoView.setFullSizedPhoto(photoUri);
349 }
350
Wenyi Wang9086fb92015-11-16 09:58:20 -0800351 private void unsetSuperPrimaryFromAllPhotos() {
Gary Mai4ceabed2016-09-16 12:14:13 -0700352 for (int i = 0; i < mRawContactDeltas.size(); i++) {
353 final RawContactDelta rawContactDelta = mRawContactDeltas.get(i);
354 if (!rawContactDelta.hasMimeEntries(Photo.CONTENT_ITEM_TYPE)) {
355 continue;
356 }
357 final List<ValuesDelta> photosDeltas =
358 mRawContactDeltas.get(i).getMimeEntries(Photo.CONTENT_ITEM_TYPE);
359 if (photosDeltas == null) {
360 continue;
361 }
362 for (int j = 0; j < photosDeltas.size(); j++) {
363 photosDeltas.get(j).setSuperPrimary(false);
Walter Jang3f18d612015-10-07 16:01:05 -0700364 }
365 }
366 }
367
Walter Jang41b3ea12015-03-09 17:30:06 -0700368 /**
Gary Mai363af602016-09-28 10:01:23 -0700369 * Pass through to {@link PhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800370 */
371 public boolean isWritablePhotoSet() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700372 return mPhotoView.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800373 }
374
375 /**
Gary Mai363af602016-09-28 10:01:23 -0700376 * Get the raw contact ID for the current photo.
Walter Jang3efae4a2015-02-18 11:12:00 -0800377 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800378 public long getPhotoRawContactId() {
Gary Maida20b472016-09-20 14:46:40 -0700379 return mCurrentRawContactDelta.getRawContactId();
Walter Jang3efae4a2015-02-18 11:12:00 -0800380 }
381
Gary Mai698cee72016-09-19 16:09:54 -0700382 public StructuredNameEditorView getNameEditorView() {
Gary Mai363af602016-09-28 10:01:23 -0700383 final KindSectionView nameKindSectionView = mKindSectionViewMap
Gary Mai698cee72016-09-19 16:09:54 -0700384 .get(StructuredName.CONTENT_ITEM_TYPE);
385 return nameKindSectionView == null
386 ? null : nameKindSectionView.getNameEditorView();
Walter Jang4f5594a2015-10-06 18:40:31 -0700387 }
388
Gary Mai220d10c2016-09-23 13:56:39 -0700389 public RawContactDelta getCurrentRawContactDelta() {
390 return mCurrentRawContactDelta;
391 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700392
393 /**
Gary Maida20b472016-09-20 14:46:40 -0700394 * Marks the raw contact photo given as primary for the aggregate contact.
Walter Jang31a74ad2015-10-02 19:17:39 -0700395 */
Gary Maida20b472016-09-20 14:46:40 -0700396 public void setPrimaryPhoto() {
Walter Jangda258ff2015-11-17 11:06:02 -0800397
398 // Update values delta
Gary Mai4ceabed2016-09-16 12:14:13 -0700399 final ValuesDelta valuesDelta = mCurrentRawContactDelta
400 .getSuperPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
Gary Maida20b472016-09-20 14:46:40 -0700401 if (valuesDelta == null) {
402 Log.wtf(TAG, "setPrimaryPhoto: had no ValuesDelta for the current RawContactDelta");
403 return;
404 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700405 valuesDelta.setFromTemplate(false);
Wenyi Wang9086fb92015-11-16 09:58:20 -0800406 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700407 valuesDelta.setSuperPrimary(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700408 }
409
Walter Jangd35e5ef2015-02-24 09:18:16 -0800410 public View getAggregationAnchorView() {
Gary Mai698cee72016-09-19 16:09:54 -0700411 final StructuredNameEditorView nameEditorView = getNameEditorView();
Gary Mai710802c2016-09-19 15:05:44 -0700412 return nameEditorView != null ? nameEditorView.findViewById(R.id.anchor_view) : null;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800413 }
414
Walter Jangf10ca152015-09-22 15:23:55 -0700415 public void setGroupMetaData(Cursor groupMetaData) {
Gary Mai363af602016-09-28 10:01:23 -0700416 final KindSectionView groupKindSectionView =
Gary Mai98868d32016-09-14 11:55:04 -0700417 mKindSectionViewMap.get(GroupMembership.CONTENT_ITEM_TYPE);
418 if (groupKindSectionView == null) {
419 return;
420 }
421 groupKindSectionView.setGroupMetaData(groupMetaData);
422 if (mIsExpanded) {
423 groupKindSectionView.setHideWhenEmpty(false);
424 groupKindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
Walter Jangf10ca152015-09-22 15:23:55 -0700425 }
426 }
427
Walter Jangf46abd82015-02-20 16:52:04 -0800428 public void setState(RawContactDeltaList rawContactDeltas,
Walter Jang06f73a12015-06-17 11:15:48 -0700429 MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
Gary Maida20b472016-09-20 14:46:40 -0700430 boolean hasNewContact, boolean isUserProfile, AccountWithDataSet primaryAccount,
Gary Mai5a00de32016-10-19 18:20:41 -0700431 long rawContactIdToDisplayAlone) {
Marcus Hagerottf04c9752016-09-08 15:22:57 -0700432
Walter Jang9a552372016-08-24 11:51:05 -0700433 mRawContactDeltas = rawContactDeltas;
434 mRawContactIdToDisplayAlone = rawContactIdToDisplayAlone;
Walter Jang9a552372016-08-24 11:51:05 -0700435
Gary Mai98868d32016-09-14 11:55:04 -0700436 mKindSectionViewMap.clear();
Walter Jangf5dfea42015-09-16 12:30:36 -0700437 mKindSectionViews.removeAllViews();
Walter Jang363d3fd2015-09-16 10:29:07 -0700438 mMoreFields.setVisibility(View.VISIBLE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800439
Walter Jangf46abd82015-02-20 16:52:04 -0800440 mMaterialPalette = materialPalette;
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700441 mViewIdGenerator = viewIdGenerator;
Tingting Wangf695eb32015-10-15 18:45:15 -0700442
Walter Jang708ea9e2015-09-10 15:42:05 -0700443 mHasNewContact = hasNewContact;
444 mIsUserProfile = isUserProfile;
445 mPrimaryAccount = primaryAccount;
446 if (mPrimaryAccount == null) {
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700447 mPrimaryAccount = ContactEditorUtils.create(getContext()).getOnlyOrDefaultAccount();
Walter Jang708ea9e2015-09-10 15:42:05 -0700448 }
449 vlog("state: primary " + mPrimaryAccount);
Walter Jangcab3dce2015-02-09 17:48:03 -0800450
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700451 // Parse the given raw contact deltas
452 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
453 elog("No raw contact deltas");
Walter Jang79658e12015-09-24 10:36:26 -0700454 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jangf5dfea42015-09-16 12:30:36 -0700455 return;
456 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700457 pickRawContactDelta();
458 parseRawContactDelta();
Walter Jang79658e12015-09-24 10:36:26 -0700459 if (mKindSectionDataMap.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700460 elog("No kind section data parsed from RawContactDelta(s)");
Walter Jang79658e12015-09-24 10:36:26 -0700461 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700462 return;
463 }
Walter Jang82acd422015-10-17 14:01:27 -0700464
Gary Mai98868d32016-09-14 11:55:04 -0700465 final KindSectionData nameSectionData =
466 mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
467 // Ensure that a structured name and photo exists
468 if (nameSectionData != null) {
Walter Jang82acd422015-10-17 14:01:27 -0700469 final RawContactDelta rawContactDelta =
Gary Mai98868d32016-09-14 11:55:04 -0700470 nameSectionData.getRawContactDelta();
Walter Jang82acd422015-10-17 14:01:27 -0700471 RawContactModifier.ensureKindExists(
472 rawContactDelta,
473 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700474 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang82acd422015-10-17 14:01:27 -0700475 RawContactModifier.ensureKindExists(
476 rawContactDelta,
477 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700478 Photo.CONTENT_ITEM_TYPE);
479 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700480
481 // Setup the view
Walter Jang363d3fd2015-09-16 10:29:07 -0700482 addPhotoView();
Gary Mai4ceabed2016-09-16 12:14:13 -0700483 if (isReadOnlyRawContact()) {
Walter Jang9a552372016-08-24 11:51:05 -0700484 // We're want to display the inputs fields for a single read only raw contact
485 addReadOnlyRawContactEditorViews();
Walter Jang9a552372016-08-24 11:51:05 -0700486 } else {
Gary Mai363af602016-09-28 10:01:23 -0700487 setupEditorNormally();
Walter Jang9a552372016-08-24 11:51:05 -0700488 }
Walter Jangd6753152015-10-02 09:23:13 -0700489 if (mListener != null) mListener.onEditorsBound();
Walter Jangcab3dce2015-02-09 17:48:03 -0800490 }
491
Gary Mai363af602016-09-28 10:01:23 -0700492 private void setupEditorNormally() {
Gary Mai98868d32016-09-14 11:55:04 -0700493 addAccountInfo();
Walter Jang9a552372016-08-24 11:51:05 -0700494 addKindSectionViews();
Walter Jangcab3dce2015-02-09 17:48:03 -0800495
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700496 mMoreFields.setVisibility(hasMoreFields() ? View.VISIBLE : View.GONE);
497
Walter Jang9a552372016-08-24 11:51:05 -0700498 if (mIsExpanded) showAllFields();
499 }
500
Gary Mai4ceabed2016-09-16 12:14:13 -0700501 private boolean isReadOnlyRawContact() {
502 return !mCurrentRawContactDelta.getAccountType(mAccountTypeManager).areContactsWritable();
Walter Jang9a552372016-08-24 11:51:05 -0700503 }
504
Gary Mai4ceabed2016-09-16 12:14:13 -0700505 private void pickRawContactDelta() {
Gary Mai4ceabed2016-09-16 12:14:13 -0700506 vlog("parse: " + mRawContactDeltas.size() + " rawContactDelta(s)");
507 for (int j = 0; j < mRawContactDeltas.size(); j++) {
508 final RawContactDelta rawContactDelta = mRawContactDeltas.get(j);
Walter Jang192a01c2015-09-22 15:23:55 -0700509 vlog("parse: " + j + " rawContactDelta" + rawContactDelta);
Walter Jang363d3fd2015-09-16 10:29:07 -0700510 if (rawContactDelta == null || !rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800511 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jang363d3fd2015-09-16 10:29:07 -0700512 if (accountType == null) continue;
Gary Mai4ceabed2016-09-16 12:14:13 -0700513
514 if (mRawContactIdToDisplayAlone > 0) {
515 // Look for the raw contact if specified.
516 if (rawContactDelta.getRawContactId().equals(mRawContactIdToDisplayAlone)) {
517 mCurrentRawContactDelta = rawContactDelta;
518 return;
Walter Jang192a01c2015-09-22 15:23:55 -0700519 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700520 } else if (mPrimaryAccount != null
521 && mPrimaryAccount.equals(rawContactDelta.getAccountWithDataSet())) {
522 // Otherwise try to find the one that matches the default.
523 mCurrentRawContactDelta = rawContactDelta;
524 return;
525 } else if (accountType.areContactsWritable()){
526 // TODO: Find better raw contact delta
527 // Just select an arbitrary writable contact.
528 mCurrentRawContactDelta = rawContactDelta;
Walter Jangac679af2015-06-01 12:17:06 -0700529 }
530 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700531
532 }
533
534 private void parseRawContactDelta() {
535 mKindSectionDataMap.clear();
536 mSortedMimetypes.clear();
537
538 final AccountType accountType = mCurrentRawContactDelta.getAccountType(mAccountTypeManager);
539 final List<DataKind> dataKinds = accountType.getSortedDataKinds();
540 final int dataKindSize = dataKinds == null ? 0 : dataKinds.size();
541 vlog("parse: " + dataKindSize + " dataKinds(s)");
542
543 for (int i = 0; i < dataKindSize; i++) {
544 final DataKind dataKind = dataKinds.get(i);
Gary Maid7faa652016-10-03 11:53:39 -0700545 // Skip null and un-editable fields.
546 if (dataKind == null || !dataKind.editable) {
547 vlog("parse: " + i +
548 (dataKind == null ? " dropped null data kind"
549 : " dropped uneditable mimetype: " + dataKind.mimeType));
Gary Mai4ceabed2016-09-16 12:14:13 -0700550 continue;
551 }
552 final String mimeType = dataKind.mimeType;
553
554 // Skip psuedo mime types
Gary Maiac5bd642016-10-12 10:31:36 -0700555 if (DataKind.PSEUDO_MIME_TYPE_NAME.equals(mimeType) ||
556 DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
Gary Mai4ceabed2016-09-16 12:14:13 -0700557 vlog("parse: " + i + " " + dataKind.mimeType + " dropped pseudo type");
558 continue;
559 }
560
561 // Skip custom fields
562 // TODO: Handle them when we implement editing custom fields.
563 if (CustomDataItem.MIMETYPE_CUSTOM_FIELD.equals(mimeType)) {
564 vlog("parse: " + i + " " + dataKind.mimeType + " dropped custom field");
565 continue;
566 }
567
568 final KindSectionData kindSectionData =
569 new KindSectionData(accountType, dataKind, mCurrentRawContactDelta);
570 mKindSectionDataMap.put(mimeType, kindSectionData);
571 mSortedMimetypes.add(mimeType);
572
573 vlog("parse: " + i + " " + dataKind.mimeType + " " +
574 kindSectionData.getValuesDeltas().size() + " value(s) " +
575 kindSectionData.getNonEmptyValuesDeltas().size() + " non-empty value(s) " +
576 kindSectionData.getVisibleValuesDeltas().size() +
577 " visible value(s)");
578 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800579 }
580
Walter Jang9a552372016-08-24 11:51:05 -0700581 private void addReadOnlyRawContactEditorViews() {
Gary Mai65971d02016-09-15 15:00:16 -0700582 mKindSectionViews.removeAllViews();
Gary Mai4ceabed2016-09-16 12:14:13 -0700583 addAccountInfo();
Walter Jang9a552372016-08-24 11:51:05 -0700584 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
585 getContext());
Gary Mai4ceabed2016-09-16 12:14:13 -0700586 final AccountType type = mCurrentRawContactDelta.getAccountType(accountTypes);
Walter Jang9a552372016-08-24 11:51:05 -0700587
Gary Mai65971d02016-09-15 15:00:16 -0700588 // Bail if invalid state or source
Gary Mai4ceabed2016-09-16 12:14:13 -0700589 if (type == null) return;
Walter Jang9a552372016-08-24 11:51:05 -0700590
Gary Mai65971d02016-09-15 15:00:16 -0700591 // Make sure we have StructuredName
Gary Mai4ceabed2016-09-16 12:14:13 -0700592 RawContactModifier.ensureKindExists(
593 mCurrentRawContactDelta, type, StructuredName.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700594
595 ValuesDelta primary;
596
597 // Name
598 final Context context = getContext();
599 final Resources res = context.getResources();
Gary Mai4ceabed2016-09-16 12:14:13 -0700600 primary = mCurrentRawContactDelta.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700601 final String name = primary != null ? primary.getAsString(StructuredName.DISPLAY_NAME) :
602 getContext().getString(R.string.missing_name);
603 final Drawable nameDrawable = context.getDrawable(R.drawable.ic_person_24dp);
604 final String nameContentDescription = res.getString(R.string.header_name_entry);
605 bindData(nameDrawable, nameContentDescription, name, /* type */ null,
606 /* isFirstEntry */ true);
607
608 // Phones
Gary Mai4ceabed2016-09-16 12:14:13 -0700609 final ArrayList<ValuesDelta> phones = mCurrentRawContactDelta
610 .getMimeEntries(Phone.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700611 final Drawable phoneDrawable = context.getDrawable(R.drawable.ic_phone_24dp);
612 final String phoneContentDescription = res.getString(R.string.header_phone_entry);
613 if (phones != null) {
614 boolean isFirstPhoneBound = true;
615 for (ValuesDelta phone : phones) {
616 final String phoneNumber = phone.getPhoneNumber();
617 if (TextUtils.isEmpty(phoneNumber)) {
618 continue;
619 }
620 final String formattedNumber = PhoneNumberUtilsCompat.formatNumber(
621 phoneNumber, phone.getPhoneNormalizedNumber(),
622 GeoUtil.getCurrentCountryIso(getContext()));
623 CharSequence phoneType = null;
624 if (phone.hasPhoneType()) {
625 phoneType = Phone.getTypeLabel(
626 res, phone.getPhoneType(), phone.getPhoneLabel());
627 }
628 bindData(phoneDrawable, phoneContentDescription, formattedNumber, phoneType,
629 isFirstPhoneBound, true);
630 isFirstPhoneBound = false;
631 }
632 }
633
634 // Emails
Gary Mai4ceabed2016-09-16 12:14:13 -0700635 final ArrayList<ValuesDelta> emails = mCurrentRawContactDelta
636 .getMimeEntries(Email.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700637 final Drawable emailDrawable = context.getDrawable(R.drawable.ic_email_24dp);
638 final String emailContentDescription = res.getString(R.string.header_email_entry);
639 if (emails != null) {
640 boolean isFirstEmailBound = true;
641 for (ValuesDelta email : emails) {
642 final String emailAddress = email.getEmailData();
643 if (TextUtils.isEmpty(emailAddress)) {
644 continue;
645 }
646 CharSequence emailType = null;
647 if (email.hasEmailType()) {
648 emailType = Email.getTypeLabel(
649 res, email.getEmailType(), email.getEmailLabel());
650 }
651 bindData(emailDrawable, emailContentDescription, emailAddress, emailType,
652 isFirstEmailBound);
653 isFirstEmailBound = false;
654 }
655 }
656
657 mKindSectionViews.setVisibility(mKindSectionViews.getChildCount() > 0 ? VISIBLE : GONE);
658 // Hide the "More fields" link
659 mMoreFields.setVisibility(GONE);
660 }
661
Gary Mai65971d02016-09-15 15:00:16 -0700662 private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
663 CharSequence type, boolean isFirstEntry) {
664 bindData(icon, iconContentDescription, data, type, isFirstEntry, false);
665 }
666
667 private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
668 CharSequence type, boolean isFirstEntry, boolean forceLTR) {
669 final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
670 Context.LAYOUT_INFLATER_SERVICE);
671 final View field = inflater.inflate(R.layout.item_read_only_field, mKindSectionViews,
672 false);
673 if (isFirstEntry) {
674 final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
675 imageView.setImageDrawable(icon);
676 imageView.setContentDescription(iconContentDescription);
677 } else {
678 final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
679 imageView.setVisibility(View.INVISIBLE);
680 imageView.setContentDescription(null);
681 }
682 final TextView dataView = (TextView) field.findViewById(R.id.data);
683 dataView.setText(data);
684 if (forceLTR) {
685 dataView.setTextDirection(View.TEXT_DIRECTION_LTR);
686 }
687 final TextView typeView = (TextView) field.findViewById(R.id.type);
688 if (!TextUtils.isEmpty(type)) {
689 typeView.setText(type);
690 } else {
691 typeView.setVisibility(View.GONE);
692 }
693 mKindSectionViews.addView(field);
Walter Jang9a552372016-08-24 11:51:05 -0700694 }
695
Gary Mai98868d32016-09-14 11:55:04 -0700696 private void addAccountInfo() {
Walter Jang82acd422015-10-17 14:01:27 -0700697 mAccountHeaderContainer.setVisibility(View.GONE);
Walter Jang708ea9e2015-09-10 15:42:05 -0700698
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700699 final AccountDisplayInfo account =
Gary Mai4ceabed2016-09-16 12:14:13 -0700700 mAccountDisplayInfoFactory.getAccountDisplayInfoFor(mCurrentRawContactDelta);
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700701
Walter Jang708ea9e2015-09-10 15:42:05 -0700702 // Get the account information for the primary raw contact delta
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700703 final String accountLabel = mIsUserProfile
704 ? EditorUiUtils.getAccountHeaderLabelForMyProfile(getContext(), account)
705 : account.getNameLabel().toString();
Walter Jang708ea9e2015-09-10 15:42:05 -0700706
Gary Maid7faa652016-10-03 11:53:39 -0700707 addAccountHeader(accountLabel);
708
709 // If we're saving a new contact and there are multiple accounts, add the account selector.
Walter Jang708ea9e2015-09-10 15:42:05 -0700710 final List<AccountWithDataSet> accounts =
711 AccountTypeManager.getInstance(getContext()).getAccounts(true);
Gary Maid7faa652016-10-03 11:53:39 -0700712 if (mHasNewContact && !mIsUserProfile && accounts.size() > 1) {
713 addAccountSelector(mCurrentRawContactDelta);
Walter Jang23709542015-10-22 12:50:58 -0700714 }
Walter Jang708ea9e2015-09-10 15:42:05 -0700715 }
716
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700717 private void addAccountHeader(String accountLabel) {
Walter Jang82acd422015-10-17 14:01:27 -0700718 mAccountHeaderContainer.setVisibility(View.VISIBLE);
719
Walter Jangbe5e1b12015-10-17 11:38:29 -0700720 // Set the account name
Walter Jangbe5e1b12015-10-17 11:38:29 -0700721 mAccountHeaderName.setVisibility(View.VISIBLE);
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700722 mAccountHeaderName.setText(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700723
724 // Set the account type
Gary Mai4ceabed2016-09-16 12:14:13 -0700725 final String selectorTitle = getResources().getString(isReadOnlyRawContact() ?
Gary Mai363af602016-09-28 10:01:23 -0700726 R.string.editor_account_selector_read_only_title :
727 R.string.editor_account_selector_title);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700728 mAccountHeaderType.setText(selectorTitle);
729
730 // Set the icon
Gary Mai4ceabed2016-09-16 12:14:13 -0700731 final AccountType accountType =
732 mCurrentRawContactDelta.getRawContactAccountType(getContext());
733 mAccountHeaderIcon.setImageDrawable(accountType.getDisplayIcon(getContext()));
Walter Jangbe5e1b12015-10-17 11:38:29 -0700734
735 // Set the content description
736 mAccountHeaderContainer.setContentDescription(
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700737 EditorUiUtils.getAccountInfoContentDescription(accountLabel,
738 selectorTitle));
Walter Jangbe5e1b12015-10-17 11:38:29 -0700739 }
740
Gary Maid7faa652016-10-03 11:53:39 -0700741 private void addAccountSelector(final RawContactDelta rawContactDelta) {
742 // Add handlers for choosing another account to save to.
743 mAccountHeaderExpanderIcon.setVisibility(View.VISIBLE);
744 mAccountHeaderContainer.setOnClickListener(new View.OnClickListener() {
Walter Jang708ea9e2015-09-10 15:42:05 -0700745 @Override
746 public void onClick(View v) {
747 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
748 final AccountsListAdapter adapter =
749 new AccountsListAdapter(getContext(),
750 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE,
751 mPrimaryAccount);
Gary Mai46cb3102016-08-10 18:14:09 -0700752 popup.setWidth(mAccountHeaderContainer.getWidth());
753 popup.setAnchorView(mAccountHeaderContainer);
Walter Jang708ea9e2015-09-10 15:42:05 -0700754 popup.setAdapter(adapter);
755 popup.setModal(true);
756 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
757 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
758 @Override
759 public void onItemClick(AdapterView<?> parent, View view, int position,
760 long id) {
761 UiClosables.closeQuietly(popup);
762 final AccountWithDataSet newAccount = adapter.getItem(position);
763 if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
Gary Mai4ceabed2016-09-16 12:14:13 -0700764 mIsExpanded = false;
Walter Jang708ea9e2015-09-10 15:42:05 -0700765 mListener.onRebindEditorsForNewContact(
Walter Jang82acd422015-10-17 14:01:27 -0700766 rawContactDelta,
Walter Jang708ea9e2015-09-10 15:42:05 -0700767 mPrimaryAccount,
768 newAccount);
769 }
770 }
771 });
772 popup.show();
773 }
Gary Maid7faa652016-10-03 11:53:39 -0700774 });
Tingting Wang655ad1a2015-10-05 17:51:14 -0700775 }
776
Walter Jang363d3fd2015-09-16 10:29:07 -0700777 private void addPhotoView() {
Gary Mai4ceabed2016-09-16 12:14:13 -0700778 if (!mCurrentRawContactDelta.hasMimeEntries(Photo.CONTENT_ITEM_TYPE)) {
779 wlog("No photo mimetype for this raw contact.");
Gary Maida20b472016-09-20 14:46:40 -0700780 mPhotoView.setVisibility(GONE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700781 return;
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700782 } else {
Gary Maida20b472016-09-20 14:46:40 -0700783 mPhotoView.setVisibility(VISIBLE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700784 }
Walter Jang06f73a12015-06-17 11:15:48 -0700785
Gary Mai4ceabed2016-09-16 12:14:13 -0700786 final ValuesDelta superPrimaryDelta = mCurrentRawContactDelta
787 .getSuperPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
Gary Maida20b472016-09-20 14:46:40 -0700788 if (superPrimaryDelta == null) {
789 Log.wtf(TAG, "addPhotoView: no ValueDelta found for current RawContactDelta"
790 + "that supports a photo.");
791 mPhotoView.setVisibility(GONE);
792 return;
793 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700794 // Set the photo view
Gary Mai18724182016-09-20 17:33:30 -0700795 mPhotoView.setPalette(mMaterialPalette);
796 mPhotoView.setPhoto(superPrimaryDelta);
Walter Jang31a74ad2015-10-02 19:17:39 -0700797
Gary Mai4ceabed2016-09-16 12:14:13 -0700798 if (isReadOnlyRawContact()) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700799 mPhotoView.setReadOnly(true);
800 return;
801 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700802 mPhotoView.setReadOnly(false);
Gary Mai4ceabed2016-09-16 12:14:13 -0700803 mPhotoValuesDelta = superPrimaryDelta;
Walter Jang31a74ad2015-10-02 19:17:39 -0700804 }
805
Walter Jangf5dfea42015-09-16 12:30:36 -0700806 private void addKindSectionViews() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700807 int i = -1;
Gary Mai98868d32016-09-14 11:55:04 -0700808
809 for (String mimeType : mSortedMimetypes) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700810 i++;
Gary Mai4ceabed2016-09-16 12:14:13 -0700811 // Ignore mime types that we've already handled
812 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
813 vlog("kind: " + i + " " + mimeType + " dropped");
814 continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800815 }
Gary Mai363af602016-09-28 10:01:23 -0700816 final KindSectionView kindSectionView;
Gary Mai4ceabed2016-09-16 12:14:13 -0700817 final KindSectionData kindSectionData = mKindSectionDataMap.get(mimeType);
Gary Mai698cee72016-09-19 16:09:54 -0700818 kindSectionView = inflateKindSectionView(mKindSectionViews, kindSectionData, mimeType);
Gary Mai98868d32016-09-14 11:55:04 -0700819 mKindSectionViews.addView(kindSectionView);
Walter Jangcab3dce2015-02-09 17:48:03 -0800820
Gary Mai98868d32016-09-14 11:55:04 -0700821 // Keep a pointer to the KindSectionView for each mimeType
822 mKindSectionViewMap.put(mimeType, kindSectionView);
Walter Jangf10ca152015-09-22 15:23:55 -0700823 }
Walter Jangf10ca152015-09-22 15:23:55 -0700824 }
825
Gary Mai363af602016-09-28 10:01:23 -0700826 private KindSectionView inflateKindSectionView(ViewGroup viewGroup,
Gary Mai698cee72016-09-19 16:09:54 -0700827 KindSectionData kindSectionData, String mimeType) {
Gary Mai363af602016-09-28 10:01:23 -0700828 final KindSectionView kindSectionView = (KindSectionView)
829 mLayoutInflater.inflate(R.layout.item_kind_section, viewGroup,
Walter Jangf5dfea42015-09-16 12:30:36 -0700830 /* attachToRoot =*/ false);
Walter Jang45b86d52015-10-15 15:23:16 -0700831 kindSectionView.setIsUserProfile(mIsUserProfile);
Walter Jangf5dfea42015-09-16 12:30:36 -0700832
Walter Jang192a01c2015-09-22 15:23:55 -0700833 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangf5dfea42015-09-16 12:30:36 -0700834 || Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -0700835 // Phone numbers and email addresses are always displayed,
836 // even if they are empty
Walter Jangf5dfea42015-09-16 12:30:36 -0700837 kindSectionView.setHideWhenEmpty(false);
838 }
Walter Jangf5dfea42015-09-16 12:30:36 -0700839
Walter Jang192a01c2015-09-22 15:23:55 -0700840 // Since phone numbers and email addresses displayed even if they are empty,
841 // they will be the only types you add new values to initially for new contacts
842 kindSectionView.setShowOneEmptyEditor(true);
843
Gary Mai698cee72016-09-19 16:09:54 -0700844 kindSectionView.setState(kindSectionData, mViewIdGenerator, mListener);
Walter Jangf5dfea42015-09-16 12:30:36 -0700845
846 return kindSectionView;
Walter Jangcab3dce2015-02-09 17:48:03 -0800847 }
848
Walter Jangd6753152015-10-02 09:23:13 -0700849 private void showAllFields() {
Walter Jang79658e12015-09-24 10:36:26 -0700850 // Stop hiding empty editors and allow the user to enter values for all kinds now
851 for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
Gary Mai363af602016-09-28 10:01:23 -0700852 final KindSectionView kindSectionView =
853 (KindSectionView) mKindSectionViews.getChildAt(i);
Walter Jang79658e12015-09-24 10:36:26 -0700854 kindSectionView.setHideWhenEmpty(false);
855 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
856 }
857 mIsExpanded = true;
Walter Jang79658e12015-09-24 10:36:26 -0700858
Walter Jangf5dfea42015-09-16 12:30:36 -0700859 // Hide the more fields button
860 mMoreFields.setVisibility(View.GONE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800861 }
862
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700863 private boolean hasMoreFields() {
Gary Mai363af602016-09-28 10:01:23 -0700864 for (KindSectionView section : mKindSectionViewMap.values()) {
Gary Mai98868d32016-09-14 11:55:04 -0700865 if (section.getVisibility() != View.VISIBLE) {
866 return true;
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700867 }
868 }
869 return false;
870 }
871
Walter Jangbf63a6d2015-05-05 09:14:35 -0700872 private static void vlog(String message) {
873 if (Log.isLoggable(TAG, Log.VERBOSE)) {
874 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -0800875 }
876 }
Walter Jang363d3fd2015-09-16 10:29:07 -0700877
878 private static void wlog(String message) {
879 if (Log.isLoggable(TAG, Log.WARN)) {
880 Log.w(TAG, message);
881 }
882 }
883
884 private static void elog(String message) {
885 Log.e(TAG, message);
886 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800887}