blob: 038a8de09d55e8f309228519d38370d35b98d859 [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;
Walter Jang9a552372016-08-24 11:51:05 -0700209 private boolean mIsEditingReadOnlyRawContactWithNewContact;
Gary Mai98868d32016-09-14 11:55:04 -0700210 private Map<String, KindSectionData> mKindSectionDataMap = new HashMap<>();
211 private Set<String> mSortedMimetypes = new TreeSet<>(new MimeTypeComparator());
Walter Jangcab3dce2015-02-09 17:48:03 -0800212
Walter Jang708ea9e2015-09-10 15:42:05 -0700213 // Account header
214 private View mAccountHeaderContainer;
215 private TextView mAccountHeaderType;
216 private TextView mAccountHeaderName;
Tingting Wang91cee282015-10-07 13:48:17 -0700217 private ImageView mAccountHeaderIcon;
Gary Mai46cb3102016-08-10 18:14:09 -0700218 private ImageView mAccountHeaderExpanderIcon;
Walter Jang708ea9e2015-09-10 15:42:05 -0700219
Gary Mai363af602016-09-28 10:01:23 -0700220 private PhotoEditorView mPhotoView;
Walter Jangf5dfea42015-09-16 12:30:36 -0700221 private ViewGroup mKindSectionViews;
Gary Mai363af602016-09-28 10:01:23 -0700222 private Map<String, KindSectionView> mKindSectionViewMap = new HashMap<>();
Walter Jangb6ca2722015-02-20 11:10:25 -0800223 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -0800224
Walter Jang79658e12015-09-24 10:36:26 -0700225 private boolean mIsExpanded;
Walter Jangcbd431d2015-10-06 13:07:30 -0700226
Walter Jang31a74ad2015-10-02 19:17:39 -0700227 private ValuesDelta mPhotoValuesDelta;
Walter Jang82acd422015-10-17 14:01:27 -0700228
Gary Mai363af602016-09-28 10:01:23 -0700229 public RawContactEditorView(Context context) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800230 super(context);
231 }
232
Gary Mai363af602016-09-28 10:01:23 -0700233 public RawContactEditorView(Context context, AttributeSet attrs) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800234 super(context, attrs);
235 }
236
Walter Jangb6ca2722015-02-20 11:10:25 -0800237 /**
Gary Mai363af602016-09-28 10:01:23 -0700238 * Sets the receiver for {@link RawContactEditorView} callbacks.
Walter Jangb6ca2722015-02-20 11:10:25 -0800239 */
240 public void setListener(Listener listener) {
241 mListener = listener;
242 }
243
Walter Jangcab3dce2015-02-09 17:48:03 -0800244 @Override
245 protected void onFinishInflate() {
246 super.onFinishInflate();
247
248 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700249 mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forWritableAccounts(getContext());
Walter Jangcab3dce2015-02-09 17:48:03 -0800250 mLayoutInflater = (LayoutInflater)
251 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
252
Walter Jang708ea9e2015-09-10 15:42:05 -0700253 // Account header
Gary Mai46cb3102016-08-10 18:14:09 -0700254 mAccountHeaderContainer = findViewById(R.id.account_header_container);
Walter Jang708ea9e2015-09-10 15:42:05 -0700255 mAccountHeaderType = (TextView) findViewById(R.id.account_type);
256 mAccountHeaderName = (TextView) findViewById(R.id.account_name);
Tingting Wang91cee282015-10-07 13:48:17 -0700257 mAccountHeaderIcon = (ImageView) findViewById(R.id.account_type_icon);
Gary Mai46cb3102016-08-10 18:14:09 -0700258 mAccountHeaderExpanderIcon = (ImageView) findViewById(R.id.account_expander_icon);
Walter Jang708ea9e2015-09-10 15:42:05 -0700259
Gary Mai363af602016-09-28 10:01:23 -0700260 mPhotoView = (PhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangf5dfea42015-09-16 12:30:36 -0700261 mKindSectionViews = (LinearLayout) findViewById(R.id.kind_section_views);
Walter Jangb6ca2722015-02-20 11:10:25 -0800262 mMoreFields = findViewById(R.id.more_fields);
263 mMoreFields.setOnClickListener(this);
264 }
265
Walter Jangb6ca2722015-02-20 11:10:25 -0800266 @Override
267 public void onClick(View view) {
Walter Jangf5dfea42015-09-16 12:30:36 -0700268 if (view.getId() == R.id.more_fields) {
Walter Jangd6753152015-10-02 09:23:13 -0700269 showAllFields();
Walter Jangb6ca2722015-02-20 11:10:25 -0800270 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800271 }
272
273 @Override
274 public void setEnabled(boolean enabled) {
275 super.setEnabled(enabled);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700276 final int childCount = mKindSectionViews.getChildCount();
277 for (int i = 0; i < childCount; i++) {
278 mKindSectionViews.getChildAt(i).setEnabled(enabled);
Walter Jangcab3dce2015-02-09 17:48:03 -0800279 }
280 }
281
Walter Jang79658e12015-09-24 10:36:26 -0700282 @Override
283 public Parcelable onSaveInstanceState() {
284 final Parcelable superState = super.onSaveInstanceState();
285 final SavedState savedState = new SavedState(superState);
286 savedState.mIsExpanded = mIsExpanded;
287 return savedState;
288 }
289
290 @Override
291 public void onRestoreInstanceState(Parcelable state) {
292 if(!(state instanceof SavedState)) {
293 super.onRestoreInstanceState(state);
294 return;
295 }
296 final SavedState savedState = (SavedState) state;
297 super.onRestoreInstanceState(savedState.getSuperState());
298 mIsExpanded = savedState.mIsExpanded;
Walter Jangd6753152015-10-02 09:23:13 -0700299 if (mIsExpanded) {
300 showAllFields();
Walter Jang79658e12015-09-24 10:36:26 -0700301 }
302 }
303
Walter Jang3efae4a2015-02-18 11:12:00 -0800304 /**
Gary Mai363af602016-09-28 10:01:23 -0700305 * Pass through to {@link PhotoEditorView#setListener}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800306 */
Gary Mai363af602016-09-28 10:01:23 -0700307 public void setPhotoListener(PhotoEditorView.Listener listener) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700308 mPhotoView.setListener(listener);
Walter Jang3efae4a2015-02-18 11:12:00 -0800309 }
310
Walter Jang31a74ad2015-10-02 19:17:39 -0700311 public void removePhoto() {
Walter Jang45b86d52015-10-15 15:23:16 -0700312 mPhotoValuesDelta.setFromTemplate(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700313 mPhotoValuesDelta.put(Photo.PHOTO, (byte[]) null);
Gary Mai18724182016-09-20 17:33:30 -0700314 mPhotoValuesDelta.put(Photo.PHOTO_FILE_ID, (String) null);
Walter Jang31a74ad2015-10-02 19:17:39 -0700315
316 mPhotoView.removePhoto();
Walter Jang3efae4a2015-02-18 11:12:00 -0800317 }
318
319 /**
Gary Mai363af602016-09-28 10:01:23 -0700320 * Pass through to {@link PhotoEditorView#setFullSizedPhoto(Uri)}.
Walter Jang41b3ea12015-03-09 17:30:06 -0700321 */
322 public void setFullSizePhoto(Uri photoUri) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700323 mPhotoView.setFullSizedPhoto(photoUri);
Walter Jang41b3ea12015-03-09 17:30:06 -0700324 }
325
Walter Jang31a74ad2015-10-02 19:17:39 -0700326 public void updatePhoto(Uri photoUri) {
Walter Jang45b86d52015-10-15 15:23:16 -0700327 mPhotoValuesDelta.setFromTemplate(false);
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700328 // Unset primary for all photos
Wenyi Wang9086fb92015-11-16 09:58:20 -0800329 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700330 // Mark the currently displayed photo as primary
331 mPhotoValuesDelta.setSuperPrimary(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700332
Wenyi Wang4c9cf3a2015-10-15 17:58:23 -0700333 // Even though high-res photos cannot be saved by passing them via
334 // an EntityDeltaList (since they cause the Bundle size limit to be
335 // exceeded), we still pass a low-res thumbnail. This simplifies
336 // code all over the place, because we don't have to test whether
337 // there is a change in EITHER the delta-list OR a changed photo...
338 // this way, there is always a change in the delta-list.
339 try {
340 final byte[] bytes = EditorUiUtils.getCompressedThumbnailBitmapBytes(
341 getContext(), photoUri);
342 if (bytes != null) {
343 mPhotoValuesDelta.setPhoto(bytes);
344 }
345 } catch (FileNotFoundException e) {
346 elog("Failed to get bitmap from photo Uri");
347 }
348
Walter Jang31a74ad2015-10-02 19:17:39 -0700349 mPhotoView.setFullSizedPhoto(photoUri);
350 }
351
Wenyi Wang9086fb92015-11-16 09:58:20 -0800352 private void unsetSuperPrimaryFromAllPhotos() {
Gary Mai4ceabed2016-09-16 12:14:13 -0700353 for (int i = 0; i < mRawContactDeltas.size(); i++) {
354 final RawContactDelta rawContactDelta = mRawContactDeltas.get(i);
355 if (!rawContactDelta.hasMimeEntries(Photo.CONTENT_ITEM_TYPE)) {
356 continue;
357 }
358 final List<ValuesDelta> photosDeltas =
359 mRawContactDeltas.get(i).getMimeEntries(Photo.CONTENT_ITEM_TYPE);
360 if (photosDeltas == null) {
361 continue;
362 }
363 for (int j = 0; j < photosDeltas.size(); j++) {
364 photosDeltas.get(j).setSuperPrimary(false);
Walter Jang3f18d612015-10-07 16:01:05 -0700365 }
366 }
367 }
368
Walter Jang41b3ea12015-03-09 17:30:06 -0700369 /**
Gary Mai363af602016-09-28 10:01:23 -0700370 * Pass through to {@link PhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800371 */
372 public boolean isWritablePhotoSet() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700373 return mPhotoView.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800374 }
375
376 /**
Gary Mai363af602016-09-28 10:01:23 -0700377 * Get the raw contact ID for the current photo.
Walter Jang3efae4a2015-02-18 11:12:00 -0800378 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800379 public long getPhotoRawContactId() {
Gary Maida20b472016-09-20 14:46:40 -0700380 return mCurrentRawContactDelta.getRawContactId();
Walter Jang3efae4a2015-02-18 11:12:00 -0800381 }
382
Gary Mai698cee72016-09-19 16:09:54 -0700383 public StructuredNameEditorView getNameEditorView() {
Gary Mai363af602016-09-28 10:01:23 -0700384 final KindSectionView nameKindSectionView = mKindSectionViewMap
Gary Mai698cee72016-09-19 16:09:54 -0700385 .get(StructuredName.CONTENT_ITEM_TYPE);
386 return nameKindSectionView == null
387 ? null : nameKindSectionView.getNameEditorView();
Walter Jang4f5594a2015-10-06 18:40:31 -0700388 }
389
Gary Mai220d10c2016-09-23 13:56:39 -0700390 public RawContactDelta getCurrentRawContactDelta() {
391 return mCurrentRawContactDelta;
392 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700393
394 /**
Gary Maida20b472016-09-20 14:46:40 -0700395 * Marks the raw contact photo given as primary for the aggregate contact.
Walter Jang31a74ad2015-10-02 19:17:39 -0700396 */
Gary Maida20b472016-09-20 14:46:40 -0700397 public void setPrimaryPhoto() {
Walter Jangda258ff2015-11-17 11:06:02 -0800398
399 // Update values delta
Gary Mai4ceabed2016-09-16 12:14:13 -0700400 final ValuesDelta valuesDelta = mCurrentRawContactDelta
401 .getSuperPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
Gary Maida20b472016-09-20 14:46:40 -0700402 if (valuesDelta == null) {
403 Log.wtf(TAG, "setPrimaryPhoto: had no ValuesDelta for the current RawContactDelta");
404 return;
405 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700406 valuesDelta.setFromTemplate(false);
Wenyi Wang9086fb92015-11-16 09:58:20 -0800407 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700408 valuesDelta.setSuperPrimary(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700409 }
410
Walter Jangd35e5ef2015-02-24 09:18:16 -0800411 public View getAggregationAnchorView() {
Gary Mai698cee72016-09-19 16:09:54 -0700412 final StructuredNameEditorView nameEditorView = getNameEditorView();
Gary Mai710802c2016-09-19 15:05:44 -0700413 return nameEditorView != null ? nameEditorView.findViewById(R.id.anchor_view) : null;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800414 }
415
Walter Jangf10ca152015-09-22 15:23:55 -0700416 public void setGroupMetaData(Cursor groupMetaData) {
Gary Mai363af602016-09-28 10:01:23 -0700417 final KindSectionView groupKindSectionView =
Gary Mai98868d32016-09-14 11:55:04 -0700418 mKindSectionViewMap.get(GroupMembership.CONTENT_ITEM_TYPE);
419 if (groupKindSectionView == null) {
420 return;
421 }
422 groupKindSectionView.setGroupMetaData(groupMetaData);
423 if (mIsExpanded) {
424 groupKindSectionView.setHideWhenEmpty(false);
425 groupKindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
Walter Jangf10ca152015-09-22 15:23:55 -0700426 }
427 }
428
Walter Jangf46abd82015-02-20 16:52:04 -0800429 public void setState(RawContactDeltaList rawContactDeltas,
Walter Jang06f73a12015-06-17 11:15:48 -0700430 MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
Gary Maida20b472016-09-20 14:46:40 -0700431 boolean hasNewContact, boolean isUserProfile, AccountWithDataSet primaryAccount,
432 long rawContactIdToDisplayAlone, boolean isEditingReadOnlyRawContactWithNewContact) {
Marcus Hagerottf04c9752016-09-08 15:22:57 -0700433
Walter Jang9a552372016-08-24 11:51:05 -0700434 mRawContactDeltas = rawContactDeltas;
435 mRawContactIdToDisplayAlone = rawContactIdToDisplayAlone;
Walter Jang9a552372016-08-24 11:51:05 -0700436 mIsEditingReadOnlyRawContactWithNewContact = isEditingReadOnlyRawContactWithNewContact;
437
Gary Mai98868d32016-09-14 11:55:04 -0700438 mKindSectionViewMap.clear();
Walter Jangf5dfea42015-09-16 12:30:36 -0700439 mKindSectionViews.removeAllViews();
Walter Jang363d3fd2015-09-16 10:29:07 -0700440 mMoreFields.setVisibility(View.VISIBLE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800441
Walter Jangf46abd82015-02-20 16:52:04 -0800442 mMaterialPalette = materialPalette;
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700443 mViewIdGenerator = viewIdGenerator;
Tingting Wangf695eb32015-10-15 18:45:15 -0700444
Walter Jang708ea9e2015-09-10 15:42:05 -0700445 mHasNewContact = hasNewContact;
446 mIsUserProfile = isUserProfile;
447 mPrimaryAccount = primaryAccount;
448 if (mPrimaryAccount == null) {
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700449 mPrimaryAccount = ContactEditorUtils.create(getContext()).getOnlyOrDefaultAccount();
Walter Jang708ea9e2015-09-10 15:42:05 -0700450 }
451 vlog("state: primary " + mPrimaryAccount);
Walter Jangcab3dce2015-02-09 17:48:03 -0800452
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700453 // Parse the given raw contact deltas
454 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
455 elog("No raw contact deltas");
Walter Jang79658e12015-09-24 10:36:26 -0700456 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jangf5dfea42015-09-16 12:30:36 -0700457 return;
458 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700459 pickRawContactDelta();
460 parseRawContactDelta();
Walter Jang79658e12015-09-24 10:36:26 -0700461 if (mKindSectionDataMap.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700462 elog("No kind section data parsed from RawContactDelta(s)");
Walter Jang79658e12015-09-24 10:36:26 -0700463 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700464 return;
465 }
Walter Jang82acd422015-10-17 14:01:27 -0700466
Gary Mai98868d32016-09-14 11:55:04 -0700467 final KindSectionData nameSectionData =
468 mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
469 // Ensure that a structured name and photo exists
470 if (nameSectionData != null) {
Walter Jang82acd422015-10-17 14:01:27 -0700471 final RawContactDelta rawContactDelta =
Gary Mai98868d32016-09-14 11:55:04 -0700472 nameSectionData.getRawContactDelta();
Walter Jang82acd422015-10-17 14:01:27 -0700473 RawContactModifier.ensureKindExists(
474 rawContactDelta,
475 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700476 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang82acd422015-10-17 14:01:27 -0700477 RawContactModifier.ensureKindExists(
478 rawContactDelta,
479 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700480 Photo.CONTENT_ITEM_TYPE);
481 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700482
483 // Setup the view
Walter Jang363d3fd2015-09-16 10:29:07 -0700484 addPhotoView();
Gary Mai4ceabed2016-09-16 12:14:13 -0700485 if (isReadOnlyRawContact()) {
Walter Jang9a552372016-08-24 11:51:05 -0700486 // We're want to display the inputs fields for a single read only raw contact
487 addReadOnlyRawContactEditorViews();
Walter Jang9a552372016-08-24 11:51:05 -0700488 } else {
Gary Mai363af602016-09-28 10:01:23 -0700489 setupEditorNormally();
Walter Jang9a552372016-08-24 11:51:05 -0700490 }
Walter Jangd6753152015-10-02 09:23:13 -0700491 if (mListener != null) mListener.onEditorsBound();
Walter Jangcab3dce2015-02-09 17:48:03 -0800492 }
493
Gary Mai363af602016-09-28 10:01:23 -0700494 private void setupEditorNormally() {
Gary Mai98868d32016-09-14 11:55:04 -0700495 addAccountInfo();
Walter Jang9a552372016-08-24 11:51:05 -0700496 addKindSectionViews();
Walter Jangcab3dce2015-02-09 17:48:03 -0800497
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700498 mMoreFields.setVisibility(hasMoreFields() ? View.VISIBLE : View.GONE);
499
Walter Jang9a552372016-08-24 11:51:05 -0700500 if (mIsExpanded) showAllFields();
501 }
502
Gary Mai4ceabed2016-09-16 12:14:13 -0700503 private boolean isReadOnlyRawContact() {
504 return !mCurrentRawContactDelta.getAccountType(mAccountTypeManager).areContactsWritable();
Walter Jang9a552372016-08-24 11:51:05 -0700505 }
506
Gary Mai4ceabed2016-09-16 12:14:13 -0700507 private void pickRawContactDelta() {
Gary Mai4ceabed2016-09-16 12:14:13 -0700508 vlog("parse: " + mRawContactDeltas.size() + " rawContactDelta(s)");
509 for (int j = 0; j < mRawContactDeltas.size(); j++) {
510 final RawContactDelta rawContactDelta = mRawContactDeltas.get(j);
Walter Jang192a01c2015-09-22 15:23:55 -0700511 vlog("parse: " + j + " rawContactDelta" + rawContactDelta);
Walter Jang363d3fd2015-09-16 10:29:07 -0700512 if (rawContactDelta == null || !rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800513 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jang363d3fd2015-09-16 10:29:07 -0700514 if (accountType == null) continue;
Gary Mai4ceabed2016-09-16 12:14:13 -0700515
516 if (mRawContactIdToDisplayAlone > 0) {
517 // Look for the raw contact if specified.
518 if (rawContactDelta.getRawContactId().equals(mRawContactIdToDisplayAlone)) {
519 mCurrentRawContactDelta = rawContactDelta;
520 return;
Walter Jang192a01c2015-09-22 15:23:55 -0700521 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700522 } else if (mPrimaryAccount != null
523 && mPrimaryAccount.equals(rawContactDelta.getAccountWithDataSet())) {
524 // Otherwise try to find the one that matches the default.
525 mCurrentRawContactDelta = rawContactDelta;
526 return;
527 } else if (accountType.areContactsWritable()){
528 // TODO: Find better raw contact delta
529 // Just select an arbitrary writable contact.
530 mCurrentRawContactDelta = rawContactDelta;
Walter Jangac679af2015-06-01 12:17:06 -0700531 }
532 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700533
534 }
535
536 private void parseRawContactDelta() {
537 mKindSectionDataMap.clear();
538 mSortedMimetypes.clear();
539
540 final AccountType accountType = mCurrentRawContactDelta.getAccountType(mAccountTypeManager);
541 final List<DataKind> dataKinds = accountType.getSortedDataKinds();
542 final int dataKindSize = dataKinds == null ? 0 : dataKinds.size();
543 vlog("parse: " + dataKindSize + " dataKinds(s)");
544
545 for (int i = 0; i < dataKindSize; i++) {
546 final DataKind dataKind = dataKinds.get(i);
Gary Maid7faa652016-10-03 11:53:39 -0700547 // Skip null and un-editable fields.
548 if (dataKind == null || !dataKind.editable) {
549 vlog("parse: " + i +
550 (dataKind == null ? " dropped null data kind"
551 : " dropped uneditable mimetype: " + dataKind.mimeType));
Gary Mai4ceabed2016-09-16 12:14:13 -0700552 continue;
553 }
554 final String mimeType = dataKind.mimeType;
555
556 // Skip psuedo mime types
Gary Maiac5bd642016-10-12 10:31:36 -0700557 if (DataKind.PSEUDO_MIME_TYPE_NAME.equals(mimeType) ||
558 DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
Gary Mai4ceabed2016-09-16 12:14:13 -0700559 vlog("parse: " + i + " " + dataKind.mimeType + " dropped pseudo type");
560 continue;
561 }
562
563 // Skip custom fields
564 // TODO: Handle them when we implement editing custom fields.
565 if (CustomDataItem.MIMETYPE_CUSTOM_FIELD.equals(mimeType)) {
566 vlog("parse: " + i + " " + dataKind.mimeType + " dropped custom field");
567 continue;
568 }
569
570 final KindSectionData kindSectionData =
571 new KindSectionData(accountType, dataKind, mCurrentRawContactDelta);
572 mKindSectionDataMap.put(mimeType, kindSectionData);
573 mSortedMimetypes.add(mimeType);
574
575 vlog("parse: " + i + " " + dataKind.mimeType + " " +
576 kindSectionData.getValuesDeltas().size() + " value(s) " +
577 kindSectionData.getNonEmptyValuesDeltas().size() + " non-empty value(s) " +
578 kindSectionData.getVisibleValuesDeltas().size() +
579 " visible value(s)");
580 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800581 }
582
Walter Jang9a552372016-08-24 11:51:05 -0700583 private void addReadOnlyRawContactEditorViews() {
Gary Mai65971d02016-09-15 15:00:16 -0700584 mKindSectionViews.removeAllViews();
Gary Mai4ceabed2016-09-16 12:14:13 -0700585 addAccountInfo();
Walter Jang9a552372016-08-24 11:51:05 -0700586 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
587 getContext());
Gary Mai4ceabed2016-09-16 12:14:13 -0700588 final AccountType type = mCurrentRawContactDelta.getAccountType(accountTypes);
Walter Jang9a552372016-08-24 11:51:05 -0700589
Gary Mai65971d02016-09-15 15:00:16 -0700590 // Bail if invalid state or source
Gary Mai4ceabed2016-09-16 12:14:13 -0700591 if (type == null) return;
Walter Jang9a552372016-08-24 11:51:05 -0700592
Gary Mai65971d02016-09-15 15:00:16 -0700593 // Make sure we have StructuredName
Gary Mai4ceabed2016-09-16 12:14:13 -0700594 RawContactModifier.ensureKindExists(
595 mCurrentRawContactDelta, type, StructuredName.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700596
597 ValuesDelta primary;
598
599 // Name
600 final Context context = getContext();
601 final Resources res = context.getResources();
Gary Mai4ceabed2016-09-16 12:14:13 -0700602 primary = mCurrentRawContactDelta.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700603 final String name = primary != null ? primary.getAsString(StructuredName.DISPLAY_NAME) :
604 getContext().getString(R.string.missing_name);
605 final Drawable nameDrawable = context.getDrawable(R.drawable.ic_person_24dp);
606 final String nameContentDescription = res.getString(R.string.header_name_entry);
607 bindData(nameDrawable, nameContentDescription, name, /* type */ null,
608 /* isFirstEntry */ true);
609
610 // Phones
Gary Mai4ceabed2016-09-16 12:14:13 -0700611 final ArrayList<ValuesDelta> phones = mCurrentRawContactDelta
612 .getMimeEntries(Phone.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700613 final Drawable phoneDrawable = context.getDrawable(R.drawable.ic_phone_24dp);
614 final String phoneContentDescription = res.getString(R.string.header_phone_entry);
615 if (phones != null) {
616 boolean isFirstPhoneBound = true;
617 for (ValuesDelta phone : phones) {
618 final String phoneNumber = phone.getPhoneNumber();
619 if (TextUtils.isEmpty(phoneNumber)) {
620 continue;
621 }
622 final String formattedNumber = PhoneNumberUtilsCompat.formatNumber(
623 phoneNumber, phone.getPhoneNormalizedNumber(),
624 GeoUtil.getCurrentCountryIso(getContext()));
625 CharSequence phoneType = null;
626 if (phone.hasPhoneType()) {
627 phoneType = Phone.getTypeLabel(
628 res, phone.getPhoneType(), phone.getPhoneLabel());
629 }
630 bindData(phoneDrawable, phoneContentDescription, formattedNumber, phoneType,
631 isFirstPhoneBound, true);
632 isFirstPhoneBound = false;
633 }
634 }
635
636 // Emails
Gary Mai4ceabed2016-09-16 12:14:13 -0700637 final ArrayList<ValuesDelta> emails = mCurrentRawContactDelta
638 .getMimeEntries(Email.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700639 final Drawable emailDrawable = context.getDrawable(R.drawable.ic_email_24dp);
640 final String emailContentDescription = res.getString(R.string.header_email_entry);
641 if (emails != null) {
642 boolean isFirstEmailBound = true;
643 for (ValuesDelta email : emails) {
644 final String emailAddress = email.getEmailData();
645 if (TextUtils.isEmpty(emailAddress)) {
646 continue;
647 }
648 CharSequence emailType = null;
649 if (email.hasEmailType()) {
650 emailType = Email.getTypeLabel(
651 res, email.getEmailType(), email.getEmailLabel());
652 }
653 bindData(emailDrawable, emailContentDescription, emailAddress, emailType,
654 isFirstEmailBound);
655 isFirstEmailBound = false;
656 }
657 }
658
659 mKindSectionViews.setVisibility(mKindSectionViews.getChildCount() > 0 ? VISIBLE : GONE);
660 // Hide the "More fields" link
661 mMoreFields.setVisibility(GONE);
662 }
663
Gary Mai65971d02016-09-15 15:00:16 -0700664 private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
665 CharSequence type, boolean isFirstEntry) {
666 bindData(icon, iconContentDescription, data, type, isFirstEntry, false);
667 }
668
669 private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
670 CharSequence type, boolean isFirstEntry, boolean forceLTR) {
671 final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
672 Context.LAYOUT_INFLATER_SERVICE);
673 final View field = inflater.inflate(R.layout.item_read_only_field, mKindSectionViews,
674 false);
675 if (isFirstEntry) {
676 final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
677 imageView.setImageDrawable(icon);
678 imageView.setContentDescription(iconContentDescription);
679 } else {
680 final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
681 imageView.setVisibility(View.INVISIBLE);
682 imageView.setContentDescription(null);
683 }
684 final TextView dataView = (TextView) field.findViewById(R.id.data);
685 dataView.setText(data);
686 if (forceLTR) {
687 dataView.setTextDirection(View.TEXT_DIRECTION_LTR);
688 }
689 final TextView typeView = (TextView) field.findViewById(R.id.type);
690 if (!TextUtils.isEmpty(type)) {
691 typeView.setText(type);
692 } else {
693 typeView.setVisibility(View.GONE);
694 }
695 mKindSectionViews.addView(field);
Walter Jang9a552372016-08-24 11:51:05 -0700696 }
697
Gary Mai98868d32016-09-14 11:55:04 -0700698 private void addAccountInfo() {
Walter Jang82acd422015-10-17 14:01:27 -0700699 mAccountHeaderContainer.setVisibility(View.GONE);
Walter Jang708ea9e2015-09-10 15:42:05 -0700700
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700701 final AccountDisplayInfo account =
Gary Mai4ceabed2016-09-16 12:14:13 -0700702 mAccountDisplayInfoFactory.getAccountDisplayInfoFor(mCurrentRawContactDelta);
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700703
Walter Jang708ea9e2015-09-10 15:42:05 -0700704 // Get the account information for the primary raw contact delta
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700705 final String accountLabel = mIsUserProfile
706 ? EditorUiUtils.getAccountHeaderLabelForMyProfile(getContext(), account)
707 : account.getNameLabel().toString();
Walter Jang708ea9e2015-09-10 15:42:05 -0700708
Gary Maid7faa652016-10-03 11:53:39 -0700709 addAccountHeader(accountLabel);
710
711 // If we're saving a new contact and there are multiple accounts, add the account selector.
Walter Jang708ea9e2015-09-10 15:42:05 -0700712 final List<AccountWithDataSet> accounts =
713 AccountTypeManager.getInstance(getContext()).getAccounts(true);
Gary Maid7faa652016-10-03 11:53:39 -0700714 if (mHasNewContact && !mIsUserProfile && accounts.size() > 1) {
715 addAccountSelector(mCurrentRawContactDelta);
Walter Jang23709542015-10-22 12:50:58 -0700716 }
Walter Jang708ea9e2015-09-10 15:42:05 -0700717 }
718
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700719 private void addAccountHeader(String accountLabel) {
Walter Jang82acd422015-10-17 14:01:27 -0700720 mAccountHeaderContainer.setVisibility(View.VISIBLE);
721
Walter Jangbe5e1b12015-10-17 11:38:29 -0700722 // Set the account name
Walter Jangbe5e1b12015-10-17 11:38:29 -0700723 mAccountHeaderName.setVisibility(View.VISIBLE);
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700724 mAccountHeaderName.setText(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700725
726 // Set the account type
Gary Mai4ceabed2016-09-16 12:14:13 -0700727 final String selectorTitle = getResources().getString(isReadOnlyRawContact() ?
Gary Mai363af602016-09-28 10:01:23 -0700728 R.string.editor_account_selector_read_only_title :
729 R.string.editor_account_selector_title);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700730 mAccountHeaderType.setText(selectorTitle);
731
732 // Set the icon
Gary Mai4ceabed2016-09-16 12:14:13 -0700733 final AccountType accountType =
734 mCurrentRawContactDelta.getRawContactAccountType(getContext());
735 mAccountHeaderIcon.setImageDrawable(accountType.getDisplayIcon(getContext()));
Walter Jangbe5e1b12015-10-17 11:38:29 -0700736
737 // Set the content description
738 mAccountHeaderContainer.setContentDescription(
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700739 EditorUiUtils.getAccountInfoContentDescription(accountLabel,
740 selectorTitle));
Walter Jangbe5e1b12015-10-17 11:38:29 -0700741 }
742
Gary Maid7faa652016-10-03 11:53:39 -0700743 private void addAccountSelector(final RawContactDelta rawContactDelta) {
744 // Add handlers for choosing another account to save to.
745 mAccountHeaderExpanderIcon.setVisibility(View.VISIBLE);
746 mAccountHeaderContainer.setOnClickListener(new View.OnClickListener() {
Walter Jang708ea9e2015-09-10 15:42:05 -0700747 @Override
748 public void onClick(View v) {
749 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
750 final AccountsListAdapter adapter =
751 new AccountsListAdapter(getContext(),
752 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE,
753 mPrimaryAccount);
Gary Mai46cb3102016-08-10 18:14:09 -0700754 popup.setWidth(mAccountHeaderContainer.getWidth());
755 popup.setAnchorView(mAccountHeaderContainer);
Walter Jang708ea9e2015-09-10 15:42:05 -0700756 popup.setAdapter(adapter);
757 popup.setModal(true);
758 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
759 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
760 @Override
761 public void onItemClick(AdapterView<?> parent, View view, int position,
762 long id) {
763 UiClosables.closeQuietly(popup);
764 final AccountWithDataSet newAccount = adapter.getItem(position);
765 if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
Gary Mai4ceabed2016-09-16 12:14:13 -0700766 mIsExpanded = false;
Walter Jang708ea9e2015-09-10 15:42:05 -0700767 mListener.onRebindEditorsForNewContact(
Walter Jang82acd422015-10-17 14:01:27 -0700768 rawContactDelta,
Walter Jang708ea9e2015-09-10 15:42:05 -0700769 mPrimaryAccount,
770 newAccount);
771 }
772 }
773 });
774 popup.show();
775 }
Gary Maid7faa652016-10-03 11:53:39 -0700776 });
Tingting Wang655ad1a2015-10-05 17:51:14 -0700777 }
778
Walter Jang363d3fd2015-09-16 10:29:07 -0700779 private void addPhotoView() {
Gary Mai4ceabed2016-09-16 12:14:13 -0700780 if (!mCurrentRawContactDelta.hasMimeEntries(Photo.CONTENT_ITEM_TYPE)) {
781 wlog("No photo mimetype for this raw contact.");
Gary Maida20b472016-09-20 14:46:40 -0700782 mPhotoView.setVisibility(GONE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700783 return;
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700784 } else {
Gary Maida20b472016-09-20 14:46:40 -0700785 mPhotoView.setVisibility(VISIBLE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700786 }
Walter Jang06f73a12015-06-17 11:15:48 -0700787
Gary Mai4ceabed2016-09-16 12:14:13 -0700788 final ValuesDelta superPrimaryDelta = mCurrentRawContactDelta
789 .getSuperPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
Gary Maida20b472016-09-20 14:46:40 -0700790 if (superPrimaryDelta == null) {
791 Log.wtf(TAG, "addPhotoView: no ValueDelta found for current RawContactDelta"
792 + "that supports a photo.");
793 mPhotoView.setVisibility(GONE);
794 return;
795 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700796 // Set the photo view
Gary Mai18724182016-09-20 17:33:30 -0700797 mPhotoView.setPalette(mMaterialPalette);
798 mPhotoView.setPhoto(superPrimaryDelta);
Walter Jang31a74ad2015-10-02 19:17:39 -0700799
Gary Mai4ceabed2016-09-16 12:14:13 -0700800 if (isReadOnlyRawContact()) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700801 mPhotoView.setReadOnly(true);
802 return;
803 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700804 mPhotoView.setReadOnly(false);
Gary Mai4ceabed2016-09-16 12:14:13 -0700805 mPhotoValuesDelta = superPrimaryDelta;
Walter Jang31a74ad2015-10-02 19:17:39 -0700806 }
807
Walter Jangf5dfea42015-09-16 12:30:36 -0700808 private void addKindSectionViews() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700809 int i = -1;
Gary Mai98868d32016-09-14 11:55:04 -0700810
811 for (String mimeType : mSortedMimetypes) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700812 i++;
Gary Mai4ceabed2016-09-16 12:14:13 -0700813 // Ignore mime types that we've already handled
814 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
815 vlog("kind: " + i + " " + mimeType + " dropped");
816 continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800817 }
Gary Mai363af602016-09-28 10:01:23 -0700818 final KindSectionView kindSectionView;
Gary Mai4ceabed2016-09-16 12:14:13 -0700819 final KindSectionData kindSectionData = mKindSectionDataMap.get(mimeType);
Gary Mai698cee72016-09-19 16:09:54 -0700820 kindSectionView = inflateKindSectionView(mKindSectionViews, kindSectionData, mimeType);
Gary Mai98868d32016-09-14 11:55:04 -0700821 mKindSectionViews.addView(kindSectionView);
Walter Jangcab3dce2015-02-09 17:48:03 -0800822
Gary Mai98868d32016-09-14 11:55:04 -0700823 // Keep a pointer to the KindSectionView for each mimeType
824 mKindSectionViewMap.put(mimeType, kindSectionView);
Walter Jangf10ca152015-09-22 15:23:55 -0700825 }
Walter Jangf10ca152015-09-22 15:23:55 -0700826 }
827
Gary Mai363af602016-09-28 10:01:23 -0700828 private KindSectionView inflateKindSectionView(ViewGroup viewGroup,
Gary Mai698cee72016-09-19 16:09:54 -0700829 KindSectionData kindSectionData, String mimeType) {
Gary Mai363af602016-09-28 10:01:23 -0700830 final KindSectionView kindSectionView = (KindSectionView)
831 mLayoutInflater.inflate(R.layout.item_kind_section, viewGroup,
Walter Jangf5dfea42015-09-16 12:30:36 -0700832 /* attachToRoot =*/ false);
Walter Jang45b86d52015-10-15 15:23:16 -0700833 kindSectionView.setIsUserProfile(mIsUserProfile);
Walter Jangf5dfea42015-09-16 12:30:36 -0700834
Walter Jang192a01c2015-09-22 15:23:55 -0700835 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangf5dfea42015-09-16 12:30:36 -0700836 || Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -0700837 // Phone numbers and email addresses are always displayed,
838 // even if they are empty
Walter Jangf5dfea42015-09-16 12:30:36 -0700839 kindSectionView.setHideWhenEmpty(false);
840 }
Walter Jangf5dfea42015-09-16 12:30:36 -0700841
Walter Jang192a01c2015-09-22 15:23:55 -0700842 // Since phone numbers and email addresses displayed even if they are empty,
843 // they will be the only types you add new values to initially for new contacts
844 kindSectionView.setShowOneEmptyEditor(true);
845
Gary Mai698cee72016-09-19 16:09:54 -0700846 kindSectionView.setState(kindSectionData, mViewIdGenerator, mListener);
Walter Jangf5dfea42015-09-16 12:30:36 -0700847
848 return kindSectionView;
Walter Jangcab3dce2015-02-09 17:48:03 -0800849 }
850
Walter Jangd6753152015-10-02 09:23:13 -0700851 private void showAllFields() {
Walter Jang79658e12015-09-24 10:36:26 -0700852 // Stop hiding empty editors and allow the user to enter values for all kinds now
853 for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
Gary Mai363af602016-09-28 10:01:23 -0700854 final KindSectionView kindSectionView =
855 (KindSectionView) mKindSectionViews.getChildAt(i);
Walter Jang79658e12015-09-24 10:36:26 -0700856 kindSectionView.setHideWhenEmpty(false);
857 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
858 }
859 mIsExpanded = true;
Walter Jang79658e12015-09-24 10:36:26 -0700860
Walter Jangf5dfea42015-09-16 12:30:36 -0700861 // Hide the more fields button
862 mMoreFields.setVisibility(View.GONE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800863 }
864
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700865 private boolean hasMoreFields() {
Gary Mai363af602016-09-28 10:01:23 -0700866 for (KindSectionView section : mKindSectionViewMap.values()) {
Gary Mai98868d32016-09-14 11:55:04 -0700867 if (section.getVisibility() != View.VISIBLE) {
868 return true;
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700869 }
870 }
871 return false;
872 }
873
Walter Jangbf63a6d2015-05-05 09:14:35 -0700874 private static void vlog(String message) {
875 if (Log.isLoggable(TAG, Log.VERBOSE)) {
876 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -0800877 }
878 }
Walter Jang363d3fd2015-09-16 10:29:07 -0700879
880 private static void wlog(String message) {
881 if (Log.isLoggable(TAG, Log.WARN)) {
882 Log.w(TAG, message);
883 }
884 }
885
886 private static void elog(String message) {
887 Log.e(TAG, message);
888 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800889}