blob: 97d2f67dea7b11b070538dfd2dc467c86f6469a4 [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.BaseAdapter;
48import android.widget.ImageView;
Walter Jangcab3dce2015-02-09 17:48:03 -080049import android.widget.LinearLayout;
Walter Jang708ea9e2015-09-10 15:42:05 -070050import android.widget.ListPopupWindow;
Walter Jang2d3f31c2015-06-18 23:15:31 -070051import android.widget.TextView;
Walter Jangcab3dce2015-02-09 17:48:03 -080052
Gary Maib6e28d22016-09-12 14:04:53 -070053import com.android.contacts.R;
Gary Mai65971d02016-09-15 15:00:16 -070054import com.android.contacts.common.GeoUtil;
55import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
Gary Maib6e28d22016-09-12 14:04:53 -070056import com.android.contacts.common.model.AccountTypeManager;
57import com.android.contacts.common.model.RawContactDelta;
58import com.android.contacts.common.model.RawContactDeltaList;
59import com.android.contacts.common.model.RawContactModifier;
60import com.android.contacts.common.model.ValuesDelta;
61import com.android.contacts.common.model.account.AccountDisplayInfo;
62import com.android.contacts.common.model.account.AccountDisplayInfoFactory;
63import com.android.contacts.common.model.account.AccountType;
64import com.android.contacts.common.model.account.AccountWithDataSet;
Gary Mai9fa89872016-09-19 15:29:59 -070065import com.android.contacts.common.model.account.DeviceLocalAccountType;
66import com.android.contacts.common.model.account.SimAccountType;
Gary Mai98868d32016-09-14 11:55:04 -070067import com.android.contacts.common.model.dataitem.CustomDataItem;
Gary Maib6e28d22016-09-12 14:04:53 -070068import com.android.contacts.common.model.dataitem.DataKind;
69import com.android.contacts.common.util.AccountsListAdapter;
70import com.android.contacts.common.util.MaterialColorMapUtils;
71import com.android.contacts.util.UiClosables;
72
Walter Jang708774a2015-10-16 09:32:06 -070073import java.io.FileNotFoundException;
Walter Jangcab3dce2015-02-09 17:48:03 -080074import java.util.ArrayList;
Walter Jangf5dfea42015-09-16 12:30:36 -070075import java.util.Arrays;
Walter Jang192a01c2015-09-22 15:23:55 -070076import java.util.Collections;
Walter Jangf5dfea42015-09-16 12:30:36 -070077import java.util.Comparator;
Walter Jang19d985f2015-07-01 13:36:27 -070078import java.util.HashMap;
Walter Jangcab3dce2015-02-09 17:48:03 -080079import java.util.List;
Walter Jang19d985f2015-07-01 13:36:27 -070080import java.util.Map;
Gary Mai98868d32016-09-14 11:55:04 -070081import java.util.Set;
Walter Jangf5dfea42015-09-16 12:30:36 -070082import java.util.TreeSet;
Walter Jangcab3dce2015-02-09 17:48:03 -080083
84/**
Walter Jangf5dfea42015-09-16 12:30:36 -070085 * View to display information from multiple {@link RawContactDelta}s grouped together.
Walter Jangcab3dce2015-02-09 17:48:03 -080086 */
Walter Jangb6ca2722015-02-20 11:10:25 -080087public class CompactRawContactsEditorView extends LinearLayout implements View.OnClickListener {
Walter Jangcab3dce2015-02-09 17:48:03 -080088
Walter Jang4f5594a2015-10-06 18:40:31 -070089 static final String TAG = "CompactEditorView";
Walter Jangcab3dce2015-02-09 17:48:03 -080090
Walter Jangb6ca2722015-02-20 11:10:25 -080091 /**
92 * Callbacks for hosts of {@link CompactRawContactsEditorView}s.
93 */
94 public interface Listener {
95
96 /**
Walter Jang151f3e62015-02-26 15:29:40 -080097 * Invoked when the structured name editor field has changed.
98 *
99 * @param rawContactId The raw contact ID from the underlying {@link RawContactDelta}.
100 * @param valuesDelta The values from the underlying {@link RawContactDelta}.
101 */
102 public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta);
Walter Jang708ea9e2015-09-10 15:42:05 -0700103
104 /**
105 * Invoked when the compact editor should rebind editors for a new account.
106 *
107 * @param oldState Old data being edited.
108 * @param oldAccount Old account associated with oldState.
109 * @param newAccount New account to be used.
110 */
111 public void onRebindEditorsForNewContact(RawContactDelta oldState,
112 AccountWithDataSet oldAccount, AccountWithDataSet newAccount);
Walter Jang79658e12015-09-24 10:36:26 -0700113
114 /**
115 * Invoked when no editors could be bound for the contact.
116 */
117 public void onBindEditorsFailed();
Walter Jangd6753152015-10-02 09:23:13 -0700118
119 /**
120 * Invoked after editors have been bound for the contact.
121 */
122 public void onEditorsBound();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700123
124 /**
Tingting Wang5585c6e2015-10-14 11:19:58 -0700125 * Invoked when a rawcontact from linked contacts is selected in editor.
Tingting Wang655ad1a2015-10-05 17:51:14 -0700126 */
Gary Mai4ceabed2016-09-16 12:14:13 -0700127 void onRawContactSelected(long rawContactId, boolean isReadOnly);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700128 }
129
130 /**
131 * Used to list the account info for the given raw contacts list.
132 */
133 private static final class RawContactAccountListAdapter extends BaseAdapter {
134 private final LayoutInflater mInflater;
135 private final Context mContext;
136 private final RawContactDeltaList mRawContactDeltas;
137
138 public RawContactAccountListAdapter(Context context, RawContactDeltaList rawContactDeltas) {
139 mContext = context;
140 mRawContactDeltas = new RawContactDeltaList();
141 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang23709542015-10-22 12:50:58 -0700142 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
Tingting Wang655ad1a2015-10-05 17:51:14 -0700143 mRawContactDeltas.add(rawContactDelta);
144 }
145 }
146 mInflater = LayoutInflater.from(context);
147 }
148
149 @Override
150 public View getView(int position, View convertView, ViewGroup parent) {
151 final View resultView = convertView != null ? convertView
152 : mInflater.inflate(R.layout.account_selector_list_item, parent, false);
153
154 final RawContactDelta rawContactDelta = mRawContactDeltas.get(position);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700155
156 final TextView text1 = (TextView) resultView.findViewById(android.R.id.text1);
Walter Jang9488a762015-10-16 13:42:48 -0700157 final AccountType accountType = rawContactDelta.getRawContactAccountType(mContext);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700158 text1.setText(accountType.getDisplayLabel(mContext));
159
Tingting Wang655ad1a2015-10-05 17:51:14 -0700160 final TextView text2 = (TextView) resultView.findViewById(android.R.id.text2);
Walter Jang9488a762015-10-16 13:42:48 -0700161 final String accountName = rawContactDelta.getAccountName();
Gary Mai9fa89872016-09-19 15:29:59 -0700162 if (TextUtils.isEmpty(accountName) || accountType instanceof DeviceLocalAccountType
163 || accountType instanceof SimAccountType) {
Tingting Wanga0b69402015-10-13 14:27:58 -0700164 text2.setVisibility(View.GONE);
165 } else {
Walter Jang9488a762015-10-16 13:42:48 -0700166 // Truncate email addresses in the middle so we don't lose the domain
Tingting Wanga0b69402015-10-13 14:27:58 -0700167 text2.setText(accountName);
168 text2.setEllipsize(TextUtils.TruncateAt.MIDDLE);
169 }
Tingting Wang655ad1a2015-10-05 17:51:14 -0700170
171 final ImageView icon = (ImageView) resultView.findViewById(android.R.id.icon);
172 icon.setImageDrawable(accountType.getDisplayIcon(mContext));
173
174 return resultView;
175 }
176
177 @Override
178 public int getCount() {
179 return mRawContactDeltas.size();
180 }
181
182 @Override
183 public RawContactDelta getItem(int position) {
184 return mRawContactDeltas.get(position);
185 }
186
187 @Override
188 public long getItemId(int position) {
189 return getItem(position).getRawContactId();
190 }
Walter Jang151f3e62015-02-26 15:29:40 -0800191 }
192
Walter Jang192a01c2015-09-22 15:23:55 -0700193 /**
194 * Sorts kinds roughly the same as quick contacts; we diverge in the following ways:
195 * <ol>
196 * <li>All names are together at the top.</li>
197 * <li>IM is moved up after addresses</li>
198 * <li>SIP addresses are moved to below phone numbers</li>
Walter Jang79658e12015-09-24 10:36:26 -0700199 * <li>Group membership is placed at the end</li>
Walter Jang192a01c2015-09-22 15:23:55 -0700200 * </ol>
201 */
202 private static final class MimeTypeComparator implements Comparator<String> {
203
Walter Jangf5dfea42015-09-16 12:30:36 -0700204 private static final List<String> MIME_TYPE_ORDER = Arrays.asList(new String[] {
205 StructuredName.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700206 Nickname.CONTENT_ITEM_TYPE,
Walter Jang2ae21752015-09-25 11:25:38 -0700207 Organization.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700208 Phone.CONTENT_ITEM_TYPE,
209 SipAddress.CONTENT_ITEM_TYPE,
210 Email.CONTENT_ITEM_TYPE,
211 StructuredPostal.CONTENT_ITEM_TYPE,
212 Im.CONTENT_ITEM_TYPE,
213 Website.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700214 Event.CONTENT_ITEM_TYPE,
215 Relation.CONTENT_ITEM_TYPE,
Walter Jangf10ca152015-09-22 15:23:55 -0700216 Note.CONTENT_ITEM_TYPE,
217 GroupMembership.CONTENT_ITEM_TYPE
Walter Jangf5dfea42015-09-16 12:30:36 -0700218 });
219
220 @Override
Walter Jang192a01c2015-09-22 15:23:55 -0700221 public int compare(String mimeType1, String mimeType2) {
222 if (mimeType1 == mimeType2) return 0;
223 if (mimeType1 == null) return -1;
224 if (mimeType2 == null) return 1;
Walter Jangf5dfea42015-09-16 12:30:36 -0700225
226 int index1 = MIME_TYPE_ORDER.indexOf(mimeType1);
227 int index2 = MIME_TYPE_ORDER.indexOf(mimeType2);
228
229 // Fallback to alphabetical ordering of the mime type if both are not found
230 if (index1 < 0 && index2 < 0) return mimeType1.compareTo(mimeType2);
231 if (index1 < 0) return 1;
232 if (index2 < 0) return -1;
233
234 return index1 < index2 ? -1 : 1;
235 }
236 }
237
Walter Jang79658e12015-09-24 10:36:26 -0700238 public static class SavedState extends BaseSavedState {
239
240 public static final Parcelable.Creator<SavedState> CREATOR =
241 new Parcelable.Creator<SavedState>() {
242 public SavedState createFromParcel(Parcel in) {
243 return new SavedState(in);
244 }
245 public SavedState[] newArray(int size) {
246 return new SavedState[size];
247 }
248 };
249
250 private boolean mIsExpanded;
251
252 public SavedState(Parcelable superState) {
253 super(superState);
254 }
255
256 private SavedState(Parcel in) {
257 super(in);
258 mIsExpanded = in.readInt() != 0;
259 }
260
261 @Override
262 public void writeToParcel(Parcel out, int flags) {
263 super.writeToParcel(out, flags);
264 out.writeInt(mIsExpanded ? 1 : 0);
265 }
266 }
267
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700268 private CompactRawContactsEditorView.Listener mListener;
Walter Jangb6ca2722015-02-20 11:10:25 -0800269
Walter Jangcab3dce2015-02-09 17:48:03 -0800270 private AccountTypeManager mAccountTypeManager;
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700271 private AccountDisplayInfoFactory mAccountDisplayInfoFactory;
Walter Jangcab3dce2015-02-09 17:48:03 -0800272 private LayoutInflater mLayoutInflater;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800273
Walter Jangcab3dce2015-02-09 17:48:03 -0800274 private ViewIdGenerator mViewIdGenerator;
Walter Jangf46abd82015-02-20 16:52:04 -0800275 private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
Walter Jang708ea9e2015-09-10 15:42:05 -0700276 private boolean mHasNewContact;
277 private boolean mIsUserProfile;
278 private AccountWithDataSet mPrimaryAccount;
Walter Jang9a552372016-08-24 11:51:05 -0700279 private RawContactDeltaList mRawContactDeltas;
Gary Mai4ceabed2016-09-16 12:14:13 -0700280 private RawContactDelta mCurrentRawContactDelta;
Walter Jang9a552372016-08-24 11:51:05 -0700281 private long mRawContactIdToDisplayAlone = -1;
Walter Jang9a552372016-08-24 11:51:05 -0700282 private boolean mIsEditingReadOnlyRawContactWithNewContact;
Gary Mai98868d32016-09-14 11:55:04 -0700283 private Map<String, KindSectionData> mKindSectionDataMap = new HashMap<>();
284 private Set<String> mSortedMimetypes = new TreeSet<>(new MimeTypeComparator());
Walter Jangcab3dce2015-02-09 17:48:03 -0800285
Walter Jang708ea9e2015-09-10 15:42:05 -0700286 // Account header
287 private View mAccountHeaderContainer;
288 private TextView mAccountHeaderType;
289 private TextView mAccountHeaderName;
Tingting Wang91cee282015-10-07 13:48:17 -0700290 private ImageView mAccountHeaderIcon;
Gary Mai46cb3102016-08-10 18:14:09 -0700291 private ImageView mAccountHeaderExpanderIcon;
Walter Jang708ea9e2015-09-10 15:42:05 -0700292
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700293 private CompactPhotoEditorView mPhotoView;
Walter Jangf5dfea42015-09-16 12:30:36 -0700294 private ViewGroup mKindSectionViews;
Gary Mai98868d32016-09-14 11:55:04 -0700295 private Map<String, CompactKindSectionView> mKindSectionViewMap = new HashMap<>();
Walter Jangb6ca2722015-02-20 11:10:25 -0800296 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -0800297
Walter Jang79658e12015-09-24 10:36:26 -0700298 private boolean mIsExpanded;
Walter Jangcbd431d2015-10-06 13:07:30 -0700299
Walter Jang31a74ad2015-10-02 19:17:39 -0700300 private ValuesDelta mPhotoValuesDelta;
Walter Jang82acd422015-10-17 14:01:27 -0700301
Walter Jangcab3dce2015-02-09 17:48:03 -0800302 public CompactRawContactsEditorView(Context context) {
303 super(context);
304 }
305
306 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
307 super(context, attrs);
308 }
309
Walter Jangb6ca2722015-02-20 11:10:25 -0800310 /**
311 * Sets the receiver for {@link CompactRawContactsEditorView} callbacks.
312 */
313 public void setListener(Listener listener) {
314 mListener = listener;
315 }
316
Walter Jangcab3dce2015-02-09 17:48:03 -0800317 @Override
318 protected void onFinishInflate() {
319 super.onFinishInflate();
320
321 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700322 mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forWritableAccounts(getContext());
Walter Jangcab3dce2015-02-09 17:48:03 -0800323 mLayoutInflater = (LayoutInflater)
324 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
325
Walter Jang708ea9e2015-09-10 15:42:05 -0700326 // Account header
Gary Mai46cb3102016-08-10 18:14:09 -0700327 mAccountHeaderContainer = findViewById(R.id.account_header_container);
Walter Jang708ea9e2015-09-10 15:42:05 -0700328 mAccountHeaderType = (TextView) findViewById(R.id.account_type);
329 mAccountHeaderName = (TextView) findViewById(R.id.account_name);
Tingting Wang91cee282015-10-07 13:48:17 -0700330 mAccountHeaderIcon = (ImageView) findViewById(R.id.account_type_icon);
Gary Mai46cb3102016-08-10 18:14:09 -0700331 mAccountHeaderExpanderIcon = (ImageView) findViewById(R.id.account_expander_icon);
Walter Jang708ea9e2015-09-10 15:42:05 -0700332
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700333 mPhotoView = (CompactPhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangf5dfea42015-09-16 12:30:36 -0700334 mKindSectionViews = (LinearLayout) findViewById(R.id.kind_section_views);
Walter Jangb6ca2722015-02-20 11:10:25 -0800335 mMoreFields = findViewById(R.id.more_fields);
336 mMoreFields.setOnClickListener(this);
337 }
338
Walter Jangb6ca2722015-02-20 11:10:25 -0800339 @Override
340 public void onClick(View view) {
Walter Jangf5dfea42015-09-16 12:30:36 -0700341 if (view.getId() == R.id.more_fields) {
Walter Jangd6753152015-10-02 09:23:13 -0700342 showAllFields();
Walter Jangb6ca2722015-02-20 11:10:25 -0800343 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800344 }
345
346 @Override
347 public void setEnabled(boolean enabled) {
348 super.setEnabled(enabled);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700349 final int childCount = mKindSectionViews.getChildCount();
350 for (int i = 0; i < childCount; i++) {
351 mKindSectionViews.getChildAt(i).setEnabled(enabled);
Walter Jangcab3dce2015-02-09 17:48:03 -0800352 }
353 }
354
Walter Jang79658e12015-09-24 10:36:26 -0700355 @Override
356 public Parcelable onSaveInstanceState() {
357 final Parcelable superState = super.onSaveInstanceState();
358 final SavedState savedState = new SavedState(superState);
359 savedState.mIsExpanded = mIsExpanded;
360 return savedState;
361 }
362
363 @Override
364 public void onRestoreInstanceState(Parcelable state) {
365 if(!(state instanceof SavedState)) {
366 super.onRestoreInstanceState(state);
367 return;
368 }
369 final SavedState savedState = (SavedState) state;
370 super.onRestoreInstanceState(savedState.getSuperState());
371 mIsExpanded = savedState.mIsExpanded;
Walter Jangd6753152015-10-02 09:23:13 -0700372 if (mIsExpanded) {
373 showAllFields();
Walter Jang79658e12015-09-24 10:36:26 -0700374 }
375 }
376
Walter Jang3efae4a2015-02-18 11:12:00 -0800377 /**
Walter Jang31a74ad2015-10-02 19:17:39 -0700378 * Pass through to {@link CompactPhotoEditorView#setListener}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800379 */
Walter Jang31a74ad2015-10-02 19:17:39 -0700380 public void setPhotoListener(CompactPhotoEditorView.Listener listener) {
381 mPhotoView.setListener(listener);
Walter Jang3efae4a2015-02-18 11:12:00 -0800382 }
383
Walter Jang31a74ad2015-10-02 19:17:39 -0700384 public void removePhoto() {
Walter Jang45b86d52015-10-15 15:23:16 -0700385 mPhotoValuesDelta.setFromTemplate(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700386 mPhotoValuesDelta.put(Photo.PHOTO, (byte[]) null);
Gary Mai18724182016-09-20 17:33:30 -0700387 mPhotoValuesDelta.put(Photo.PHOTO_FILE_ID, (String) null);
Walter Jang31a74ad2015-10-02 19:17:39 -0700388
389 mPhotoView.removePhoto();
Walter Jang3efae4a2015-02-18 11:12:00 -0800390 }
391
392 /**
Walter Jang41b3ea12015-03-09 17:30:06 -0700393 * Pass through to {@link CompactPhotoEditorView#setFullSizedPhoto(Uri)}.
394 */
395 public void setFullSizePhoto(Uri photoUri) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700396 mPhotoView.setFullSizedPhoto(photoUri);
Walter Jang41b3ea12015-03-09 17:30:06 -0700397 }
398
Walter Jang31a74ad2015-10-02 19:17:39 -0700399 public void updatePhoto(Uri photoUri) {
Walter Jang45b86d52015-10-15 15:23:16 -0700400 mPhotoValuesDelta.setFromTemplate(false);
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700401 // Unset primary for all photos
Wenyi Wang9086fb92015-11-16 09:58:20 -0800402 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700403 // Mark the currently displayed photo as primary
404 mPhotoValuesDelta.setSuperPrimary(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700405
Wenyi Wang4c9cf3a2015-10-15 17:58:23 -0700406 // Even though high-res photos cannot be saved by passing them via
407 // an EntityDeltaList (since they cause the Bundle size limit to be
408 // exceeded), we still pass a low-res thumbnail. This simplifies
409 // code all over the place, because we don't have to test whether
410 // there is a change in EITHER the delta-list OR a changed photo...
411 // this way, there is always a change in the delta-list.
412 try {
413 final byte[] bytes = EditorUiUtils.getCompressedThumbnailBitmapBytes(
414 getContext(), photoUri);
415 if (bytes != null) {
416 mPhotoValuesDelta.setPhoto(bytes);
417 }
418 } catch (FileNotFoundException e) {
419 elog("Failed to get bitmap from photo Uri");
420 }
421
Walter Jang31a74ad2015-10-02 19:17:39 -0700422 mPhotoView.setFullSizedPhoto(photoUri);
423 }
424
Wenyi Wang9086fb92015-11-16 09:58:20 -0800425 private void unsetSuperPrimaryFromAllPhotos() {
Gary Mai4ceabed2016-09-16 12:14:13 -0700426 for (int i = 0; i < mRawContactDeltas.size(); i++) {
427 final RawContactDelta rawContactDelta = mRawContactDeltas.get(i);
428 if (!rawContactDelta.hasMimeEntries(Photo.CONTENT_ITEM_TYPE)) {
429 continue;
430 }
431 final List<ValuesDelta> photosDeltas =
432 mRawContactDeltas.get(i).getMimeEntries(Photo.CONTENT_ITEM_TYPE);
433 if (photosDeltas == null) {
434 continue;
435 }
436 for (int j = 0; j < photosDeltas.size(); j++) {
437 photosDeltas.get(j).setSuperPrimary(false);
Walter Jang3f18d612015-10-07 16:01:05 -0700438 }
439 }
440 }
441
Walter Jang41b3ea12015-03-09 17:30:06 -0700442 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800443 * Pass through to {@link CompactPhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800444 */
445 public boolean isWritablePhotoSet() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700446 return mPhotoView.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800447 }
448
449 /**
Walter Jang3efae4a2015-02-18 11:12:00 -0800450 * Get the raw contact ID for the CompactHeaderView photo.
451 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800452 public long getPhotoRawContactId() {
Gary Maida20b472016-09-20 14:46:40 -0700453 return mCurrentRawContactDelta.getRawContactId();
Walter Jang3efae4a2015-02-18 11:12:00 -0800454 }
455
Walter Jang4f5594a2015-10-06 18:40:31 -0700456 public StructuredNameEditorView getPrimaryNameEditorView() {
Walter Jange3945952015-10-27 12:44:54 -0700457 final CompactKindSectionView primaryNameKindSectionView = getPrimaryNameKindSectionView();
458 return primaryNameKindSectionView == null
459 ? null : primaryNameKindSectionView.getPrimaryNameEditorView();
Walter Jang4f5594a2015-10-06 18:40:31 -0700460 }
461
Gary Mai220d10c2016-09-23 13:56:39 -0700462 public RawContactDelta getCurrentRawContactDelta() {
463 return mCurrentRawContactDelta;
464 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700465
466 /**
Gary Maida20b472016-09-20 14:46:40 -0700467 * Marks the raw contact photo given as primary for the aggregate contact.
Walter Jang31a74ad2015-10-02 19:17:39 -0700468 */
Gary Maida20b472016-09-20 14:46:40 -0700469 public void setPrimaryPhoto() {
Walter Jangda258ff2015-11-17 11:06:02 -0800470
471 // Update values delta
Gary Mai4ceabed2016-09-16 12:14:13 -0700472 final ValuesDelta valuesDelta = mCurrentRawContactDelta
473 .getSuperPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
Gary Maida20b472016-09-20 14:46:40 -0700474 if (valuesDelta == null) {
475 Log.wtf(TAG, "setPrimaryPhoto: had no ValuesDelta for the current RawContactDelta");
476 return;
477 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700478 valuesDelta.setFromTemplate(false);
Wenyi Wang9086fb92015-11-16 09:58:20 -0800479 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700480 valuesDelta.setSuperPrimary(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700481 }
482
Walter Jangd35e5ef2015-02-24 09:18:16 -0800483 public View getAggregationAnchorView() {
Gary Mai710802c2016-09-19 15:05:44 -0700484 final StructuredNameEditorView nameEditorView = getPrimaryNameEditorView();
485 return nameEditorView != null ? nameEditorView.findViewById(R.id.anchor_view) : null;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800486 }
487
Walter Jangf10ca152015-09-22 15:23:55 -0700488 public void setGroupMetaData(Cursor groupMetaData) {
Gary Mai98868d32016-09-14 11:55:04 -0700489 final CompactKindSectionView groupKindSectionView =
490 mKindSectionViewMap.get(GroupMembership.CONTENT_ITEM_TYPE);
491 if (groupKindSectionView == null) {
492 return;
493 }
494 groupKindSectionView.setGroupMetaData(groupMetaData);
495 if (mIsExpanded) {
496 groupKindSectionView.setHideWhenEmpty(false);
497 groupKindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
Walter Jangf10ca152015-09-22 15:23:55 -0700498 }
499 }
500
Walter Jangf46abd82015-02-20 16:52:04 -0800501 public void setState(RawContactDeltaList rawContactDeltas,
Walter Jang06f73a12015-06-17 11:15:48 -0700502 MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
Gary Maida20b472016-09-20 14:46:40 -0700503 boolean hasNewContact, boolean isUserProfile, AccountWithDataSet primaryAccount,
504 long rawContactIdToDisplayAlone, boolean isEditingReadOnlyRawContactWithNewContact) {
Marcus Hagerottf04c9752016-09-08 15:22:57 -0700505
Walter Jang9a552372016-08-24 11:51:05 -0700506 mRawContactDeltas = rawContactDeltas;
507 mRawContactIdToDisplayAlone = rawContactIdToDisplayAlone;
Walter Jang9a552372016-08-24 11:51:05 -0700508 mIsEditingReadOnlyRawContactWithNewContact = isEditingReadOnlyRawContactWithNewContact;
509
Gary Mai98868d32016-09-14 11:55:04 -0700510 mKindSectionViewMap.clear();
Walter Jangf5dfea42015-09-16 12:30:36 -0700511 mKindSectionViews.removeAllViews();
Walter Jang363d3fd2015-09-16 10:29:07 -0700512 mMoreFields.setVisibility(View.VISIBLE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800513
Walter Jangf46abd82015-02-20 16:52:04 -0800514 mMaterialPalette = materialPalette;
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700515 mViewIdGenerator = viewIdGenerator;
Tingting Wangf695eb32015-10-15 18:45:15 -0700516
Walter Jang708ea9e2015-09-10 15:42:05 -0700517 mHasNewContact = hasNewContact;
518 mIsUserProfile = isUserProfile;
519 mPrimaryAccount = primaryAccount;
520 if (mPrimaryAccount == null) {
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700521 mPrimaryAccount = ContactEditorUtils.create(getContext()).getOnlyOrDefaultAccount();
Walter Jang708ea9e2015-09-10 15:42:05 -0700522 }
523 vlog("state: primary " + mPrimaryAccount);
Walter Jangcab3dce2015-02-09 17:48:03 -0800524
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700525 // Parse the given raw contact deltas
526 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
527 elog("No raw contact deltas");
Walter Jang79658e12015-09-24 10:36:26 -0700528 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jangf5dfea42015-09-16 12:30:36 -0700529 return;
530 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700531 pickRawContactDelta();
532 parseRawContactDelta();
Walter Jang79658e12015-09-24 10:36:26 -0700533 if (mKindSectionDataMap.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700534 elog("No kind section data parsed from RawContactDelta(s)");
Walter Jang79658e12015-09-24 10:36:26 -0700535 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700536 return;
537 }
Walter Jang82acd422015-10-17 14:01:27 -0700538
Gary Mai98868d32016-09-14 11:55:04 -0700539 final KindSectionData nameSectionData =
540 mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
541 // Ensure that a structured name and photo exists
542 if (nameSectionData != null) {
Walter Jang82acd422015-10-17 14:01:27 -0700543 final RawContactDelta rawContactDelta =
Gary Mai98868d32016-09-14 11:55:04 -0700544 nameSectionData.getRawContactDelta();
Walter Jang82acd422015-10-17 14:01:27 -0700545 RawContactModifier.ensureKindExists(
546 rawContactDelta,
547 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700548 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang82acd422015-10-17 14:01:27 -0700549 RawContactModifier.ensureKindExists(
550 rawContactDelta,
551 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700552 Photo.CONTENT_ITEM_TYPE);
553 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700554
555 // Setup the view
Walter Jang363d3fd2015-09-16 10:29:07 -0700556 addPhotoView();
Gary Mai4ceabed2016-09-16 12:14:13 -0700557 if (isReadOnlyRawContact()) {
Walter Jang9a552372016-08-24 11:51:05 -0700558 // We're want to display the inputs fields for a single read only raw contact
559 addReadOnlyRawContactEditorViews();
Walter Jang9a552372016-08-24 11:51:05 -0700560 } else {
561 setupCompactEditorNormally();
562 }
Walter Jangd6753152015-10-02 09:23:13 -0700563 if (mListener != null) mListener.onEditorsBound();
Walter Jangcab3dce2015-02-09 17:48:03 -0800564 }
565
Walter Jang9a552372016-08-24 11:51:05 -0700566 private void setupCompactEditorNormally() {
Gary Mai98868d32016-09-14 11:55:04 -0700567 addAccountInfo();
Walter Jang9a552372016-08-24 11:51:05 -0700568 addKindSectionViews();
Walter Jangcab3dce2015-02-09 17:48:03 -0800569
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700570 mMoreFields.setVisibility(hasMoreFields() ? View.VISIBLE : View.GONE);
571
Walter Jang9a552372016-08-24 11:51:05 -0700572 if (mIsExpanded) showAllFields();
573 }
574
Gary Mai4ceabed2016-09-16 12:14:13 -0700575 private boolean isReadOnlyRawContact() {
576 return !mCurrentRawContactDelta.getAccountType(mAccountTypeManager).areContactsWritable();
Walter Jang9a552372016-08-24 11:51:05 -0700577 }
578
Gary Mai4ceabed2016-09-16 12:14:13 -0700579 private void pickRawContactDelta() {
Walter Jang363d3fd2015-09-16 10:29:07 -0700580 // Build the kind section data list map
Gary Mai4ceabed2016-09-16 12:14:13 -0700581 vlog("parse: " + mRawContactDeltas.size() + " rawContactDelta(s)");
582 for (int j = 0; j < mRawContactDeltas.size(); j++) {
583 final RawContactDelta rawContactDelta = mRawContactDeltas.get(j);
Walter Jang192a01c2015-09-22 15:23:55 -0700584 vlog("parse: " + j + " rawContactDelta" + rawContactDelta);
Walter Jang363d3fd2015-09-16 10:29:07 -0700585 if (rawContactDelta == null || !rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800586 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jang363d3fd2015-09-16 10:29:07 -0700587 if (accountType == null) continue;
Gary Mai4ceabed2016-09-16 12:14:13 -0700588
589 if (mRawContactIdToDisplayAlone > 0) {
590 // Look for the raw contact if specified.
591 if (rawContactDelta.getRawContactId().equals(mRawContactIdToDisplayAlone)) {
592 mCurrentRawContactDelta = rawContactDelta;
593 return;
Walter Jang192a01c2015-09-22 15:23:55 -0700594 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700595 } else if (mPrimaryAccount != null
596 && mPrimaryAccount.equals(rawContactDelta.getAccountWithDataSet())) {
597 // Otherwise try to find the one that matches the default.
598 mCurrentRawContactDelta = rawContactDelta;
599 return;
600 } else if (accountType.areContactsWritable()){
601 // TODO: Find better raw contact delta
602 // Just select an arbitrary writable contact.
603 mCurrentRawContactDelta = rawContactDelta;
Walter Jangac679af2015-06-01 12:17:06 -0700604 }
605 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700606
607 }
608
609 private void parseRawContactDelta() {
610 mKindSectionDataMap.clear();
611 mSortedMimetypes.clear();
612
613 final AccountType accountType = mCurrentRawContactDelta.getAccountType(mAccountTypeManager);
614 final List<DataKind> dataKinds = accountType.getSortedDataKinds();
615 final int dataKindSize = dataKinds == null ? 0 : dataKinds.size();
616 vlog("parse: " + dataKindSize + " dataKinds(s)");
617
618 for (int i = 0; i < dataKindSize; i++) {
619 final DataKind dataKind = dataKinds.get(i);
620 if (dataKind == null) {
621 vlog("parse: " + i + " " + dataKind.mimeType + " dropped null data kind");
622 continue;
623 }
624 final String mimeType = dataKind.mimeType;
625
626 // Skip psuedo mime types
627 if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
628 || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
629 vlog("parse: " + i + " " + dataKind.mimeType + " dropped pseudo type");
630 continue;
631 }
632
633 // Skip custom fields
634 // TODO: Handle them when we implement editing custom fields.
635 if (CustomDataItem.MIMETYPE_CUSTOM_FIELD.equals(mimeType)) {
636 vlog("parse: " + i + " " + dataKind.mimeType + " dropped custom field");
637 continue;
638 }
639
640 final KindSectionData kindSectionData =
641 new KindSectionData(accountType, dataKind, mCurrentRawContactDelta);
642 mKindSectionDataMap.put(mimeType, kindSectionData);
643 mSortedMimetypes.add(mimeType);
644
645 vlog("parse: " + i + " " + dataKind.mimeType + " " +
646 kindSectionData.getValuesDeltas().size() + " value(s) " +
647 kindSectionData.getNonEmptyValuesDeltas().size() + " non-empty value(s) " +
648 kindSectionData.getVisibleValuesDeltas().size() +
649 " visible value(s)");
650 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800651 }
652
Walter Jang9a552372016-08-24 11:51:05 -0700653 private void addReadOnlyRawContactEditorViews() {
Gary Mai65971d02016-09-15 15:00:16 -0700654 mKindSectionViews.removeAllViews();
Gary Mai4ceabed2016-09-16 12:14:13 -0700655 addAccountInfo();
Walter Jang9a552372016-08-24 11:51:05 -0700656 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
657 getContext());
Gary Mai4ceabed2016-09-16 12:14:13 -0700658 final AccountType type = mCurrentRawContactDelta.getAccountType(accountTypes);
Walter Jang9a552372016-08-24 11:51:05 -0700659
Gary Mai65971d02016-09-15 15:00:16 -0700660 // Bail if invalid state or source
Gary Mai4ceabed2016-09-16 12:14:13 -0700661 if (type == null) return;
Walter Jang9a552372016-08-24 11:51:05 -0700662
Gary Mai65971d02016-09-15 15:00:16 -0700663 // Make sure we have StructuredName
Gary Mai4ceabed2016-09-16 12:14:13 -0700664 RawContactModifier.ensureKindExists(
665 mCurrentRawContactDelta, type, StructuredName.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700666
667 ValuesDelta primary;
668
669 // Name
670 final Context context = getContext();
671 final Resources res = context.getResources();
Gary Mai4ceabed2016-09-16 12:14:13 -0700672 primary = mCurrentRawContactDelta.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700673 final String name = primary != null ? primary.getAsString(StructuredName.DISPLAY_NAME) :
674 getContext().getString(R.string.missing_name);
675 final Drawable nameDrawable = context.getDrawable(R.drawable.ic_person_24dp);
676 final String nameContentDescription = res.getString(R.string.header_name_entry);
677 bindData(nameDrawable, nameContentDescription, name, /* type */ null,
678 /* isFirstEntry */ true);
679
680 // Phones
Gary Mai4ceabed2016-09-16 12:14:13 -0700681 final ArrayList<ValuesDelta> phones = mCurrentRawContactDelta
682 .getMimeEntries(Phone.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700683 final Drawable phoneDrawable = context.getDrawable(R.drawable.ic_phone_24dp);
684 final String phoneContentDescription = res.getString(R.string.header_phone_entry);
685 if (phones != null) {
686 boolean isFirstPhoneBound = true;
687 for (ValuesDelta phone : phones) {
688 final String phoneNumber = phone.getPhoneNumber();
689 if (TextUtils.isEmpty(phoneNumber)) {
690 continue;
691 }
692 final String formattedNumber = PhoneNumberUtilsCompat.formatNumber(
693 phoneNumber, phone.getPhoneNormalizedNumber(),
694 GeoUtil.getCurrentCountryIso(getContext()));
695 CharSequence phoneType = null;
696 if (phone.hasPhoneType()) {
697 phoneType = Phone.getTypeLabel(
698 res, phone.getPhoneType(), phone.getPhoneLabel());
699 }
700 bindData(phoneDrawable, phoneContentDescription, formattedNumber, phoneType,
701 isFirstPhoneBound, true);
702 isFirstPhoneBound = false;
703 }
704 }
705
706 // Emails
Gary Mai4ceabed2016-09-16 12:14:13 -0700707 final ArrayList<ValuesDelta> emails = mCurrentRawContactDelta
708 .getMimeEntries(Email.CONTENT_ITEM_TYPE);
Gary Mai65971d02016-09-15 15:00:16 -0700709 final Drawable emailDrawable = context.getDrawable(R.drawable.ic_email_24dp);
710 final String emailContentDescription = res.getString(R.string.header_email_entry);
711 if (emails != null) {
712 boolean isFirstEmailBound = true;
713 for (ValuesDelta email : emails) {
714 final String emailAddress = email.getEmailData();
715 if (TextUtils.isEmpty(emailAddress)) {
716 continue;
717 }
718 CharSequence emailType = null;
719 if (email.hasEmailType()) {
720 emailType = Email.getTypeLabel(
721 res, email.getEmailType(), email.getEmailLabel());
722 }
723 bindData(emailDrawable, emailContentDescription, emailAddress, emailType,
724 isFirstEmailBound);
725 isFirstEmailBound = false;
726 }
727 }
728
729 mKindSectionViews.setVisibility(mKindSectionViews.getChildCount() > 0 ? VISIBLE : GONE);
730 // Hide the "More fields" link
731 mMoreFields.setVisibility(GONE);
732 }
733
Gary Mai65971d02016-09-15 15:00:16 -0700734 private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
735 CharSequence type, boolean isFirstEntry) {
736 bindData(icon, iconContentDescription, data, type, isFirstEntry, false);
737 }
738
739 private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
740 CharSequence type, boolean isFirstEntry, boolean forceLTR) {
741 final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
742 Context.LAYOUT_INFLATER_SERVICE);
743 final View field = inflater.inflate(R.layout.item_read_only_field, mKindSectionViews,
744 false);
745 if (isFirstEntry) {
746 final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
747 imageView.setImageDrawable(icon);
748 imageView.setContentDescription(iconContentDescription);
749 } else {
750 final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
751 imageView.setVisibility(View.INVISIBLE);
752 imageView.setContentDescription(null);
753 }
754 final TextView dataView = (TextView) field.findViewById(R.id.data);
755 dataView.setText(data);
756 if (forceLTR) {
757 dataView.setTextDirection(View.TEXT_DIRECTION_LTR);
758 }
759 final TextView typeView = (TextView) field.findViewById(R.id.type);
760 if (!TextUtils.isEmpty(type)) {
761 typeView.setText(type);
762 } else {
763 typeView.setVisibility(View.GONE);
764 }
765 mKindSectionViews.addView(field);
Walter Jang9a552372016-08-24 11:51:05 -0700766 }
767
Gary Mai98868d32016-09-14 11:55:04 -0700768 private void addAccountInfo() {
Walter Jang82acd422015-10-17 14:01:27 -0700769 mAccountHeaderContainer.setVisibility(View.GONE);
Walter Jang708ea9e2015-09-10 15:42:05 -0700770
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700771 final AccountDisplayInfo account =
Gary Mai4ceabed2016-09-16 12:14:13 -0700772 mAccountDisplayInfoFactory.getAccountDisplayInfoFor(mCurrentRawContactDelta);
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700773
Walter Jang708ea9e2015-09-10 15:42:05 -0700774 // Get the account information for the primary raw contact delta
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700775 final String accountLabel = mIsUserProfile
776 ? EditorUiUtils.getAccountHeaderLabelForMyProfile(getContext(), account)
777 : account.getNameLabel().toString();
Walter Jang708ea9e2015-09-10 15:42:05 -0700778
Walter Jang23709542015-10-22 12:50:58 -0700779 // Either the account header or selector should be shown, not both.
Walter Jang708ea9e2015-09-10 15:42:05 -0700780 final List<AccountWithDataSet> accounts =
781 AccountTypeManager.getInstance(getContext()).getAccounts(true);
Gary Mai4ceabed2016-09-16 12:14:13 -0700782
Walter Jang82acd422015-10-17 14:01:27 -0700783 if (mHasNewContact && !mIsUserProfile) {
784 if (accounts.size() > 1) {
Gary Mai4ceabed2016-09-16 12:14:13 -0700785 addAccountSelector(mCurrentRawContactDelta, accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700786 } else {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700787 addAccountHeader(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700788 }
Gary Maida20b472016-09-20 14:46:40 -0700789 } else {
790 // The raw contact selector should only display linked raw contacts that can be edited
791 // in the full editor (i.e. they are not newly created raw contacts)
792 final RawContactAccountListAdapter adapter = new RawContactAccountListAdapter(
793 getContext(), getRawContactDeltaListForSelector(mRawContactDeltas));
794 if (adapter.getCount() > 0 && !mIsEditingReadOnlyRawContactWithNewContact) {
795 addRawContactAccountSelector(accountLabel, adapter);
796 } else {
797 addAccountHeader(accountLabel);
798 }
Walter Jang23709542015-10-22 12:50:58 -0700799 }
Walter Jang708ea9e2015-09-10 15:42:05 -0700800 }
801
Tingting Wang2bb85d22015-10-23 13:01:09 -0700802 private RawContactDeltaList getRawContactDeltaListForSelector(
803 RawContactDeltaList rawContactDeltas) {
804 // Sort raw contacts so google accounts come first
805 Collections.sort(rawContactDeltas, new RawContactDeltaComparator(getContext()));
806
807 final RawContactDeltaList result = new RawContactDeltaList();
Gary Mai98868d32016-09-14 11:55:04 -0700808 for (int i = 0; i < rawContactDeltas.size(); i++) {
809 final RawContactDelta rawContactDelta = rawContactDeltas.get(i);
Tingting Wang2bb85d22015-10-23 13:01:09 -0700810 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
811 // Only add raw contacts that can be opened in the editor
812 result.add(rawContactDelta);
813 }
814 }
815 // Don't return a list of size 1 that would just open the raw contact being edited
816 // in the compact editor in the full editor
817 if (result.size() == 1 && result.get(0).getRawContactAccountType(
818 getContext()).areContactsWritable()) {
819 result.clear();
820 return result;
821 }
822 return result;
823 }
824
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700825 private void addAccountHeader(String accountLabel) {
Walter Jang82acd422015-10-17 14:01:27 -0700826 mAccountHeaderContainer.setVisibility(View.VISIBLE);
827
Walter Jangbe5e1b12015-10-17 11:38:29 -0700828 // Set the account name
Walter Jangbe5e1b12015-10-17 11:38:29 -0700829 mAccountHeaderName.setVisibility(View.VISIBLE);
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700830 mAccountHeaderName.setText(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700831
832 // Set the account type
Gary Mai4ceabed2016-09-16 12:14:13 -0700833 final String selectorTitle = getResources().getString(isReadOnlyRawContact() ?
834 R.string.compact_editor_account_selector_read_only_title :
Walter Jangbe5e1b12015-10-17 11:38:29 -0700835 R.string.compact_editor_account_selector_title);
836 mAccountHeaderType.setText(selectorTitle);
837
838 // Set the icon
Gary Mai4ceabed2016-09-16 12:14:13 -0700839 final AccountType accountType =
840 mCurrentRawContactDelta.getRawContactAccountType(getContext());
841 mAccountHeaderIcon.setImageDrawable(accountType.getDisplayIcon(getContext()));
Walter Jangbe5e1b12015-10-17 11:38:29 -0700842
843 // Set the content description
844 mAccountHeaderContainer.setContentDescription(
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700845 EditorUiUtils.getAccountInfoContentDescription(accountLabel,
846 selectorTitle));
Walter Jangbe5e1b12015-10-17 11:38:29 -0700847 }
848
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700849 private void addAccountSelector(final RawContactDelta rawContactDelta, CharSequence nameLabel) {
Gary Mai4ceabed2016-09-16 12:14:13 -0700850 final View.OnClickListener onClickListener = new View.OnClickListener() {
Walter Jang708ea9e2015-09-10 15:42:05 -0700851 @Override
852 public void onClick(View v) {
853 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
854 final AccountsListAdapter adapter =
855 new AccountsListAdapter(getContext(),
856 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE,
857 mPrimaryAccount);
Gary Mai46cb3102016-08-10 18:14:09 -0700858 popup.setWidth(mAccountHeaderContainer.getWidth());
859 popup.setAnchorView(mAccountHeaderContainer);
Walter Jang708ea9e2015-09-10 15:42:05 -0700860 popup.setAdapter(adapter);
861 popup.setModal(true);
862 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
863 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
864 @Override
865 public void onItemClick(AdapterView<?> parent, View view, int position,
866 long id) {
867 UiClosables.closeQuietly(popup);
868 final AccountWithDataSet newAccount = adapter.getItem(position);
869 if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
Gary Mai4ceabed2016-09-16 12:14:13 -0700870 mIsExpanded = false;
Walter Jang708ea9e2015-09-10 15:42:05 -0700871 mListener.onRebindEditorsForNewContact(
Walter Jang82acd422015-10-17 14:01:27 -0700872 rawContactDelta,
Walter Jang708ea9e2015-09-10 15:42:05 -0700873 mPrimaryAccount,
874 newAccount);
875 }
876 }
877 });
878 popup.show();
879 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700880 };
881 setUpAccountSelector(nameLabel.toString(), onClickListener);
Walter Jang708ea9e2015-09-10 15:42:05 -0700882 }
883
Gary Mai4ceabed2016-09-16 12:14:13 -0700884 private void addRawContactAccountSelector(String nameLabel,
Walter Jang23709542015-10-22 12:50:58 -0700885 final RawContactAccountListAdapter adapter) {
Gary Mai4ceabed2016-09-16 12:14:13 -0700886 final View.OnClickListener onClickListener = new OnClickListener() {
Tingting Wang655ad1a2015-10-05 17:51:14 -0700887 @Override
888 public void onClick(View v) {
889 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
Gary Mai4ceabed2016-09-16 12:14:13 -0700890 popup.setWidth(mAccountHeaderContainer.getWidth());
891 popup.setAnchorView(mAccountHeaderContainer);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700892 popup.setAdapter(adapter);
893 popup.setModal(true);
894 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
895 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
896 @Override
897 public void onItemClick(AdapterView<?> parent, View view, int position,
898 long id) {
899 UiClosables.closeQuietly(popup);
Gary Mai4ceabed2016-09-16 12:14:13 -0700900 final long rawContactId = adapter.getItemId(position);
901 // Only switch if it's actually a different raw contact.
902 if (rawContactId != mCurrentRawContactDelta.getRawContactId()
903 && mListener != null) {
Tingting Wang91cee282015-10-07 13:48:17 -0700904 final RawContactDelta rawContactDelta = adapter.getItem(position);
905 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
906 getContext());
907 final AccountType accountType = rawContactDelta.getAccountType(
908 accountTypes);
909 final boolean isReadOnly = !accountType.areContactsWritable();
Gary Mai4ceabed2016-09-16 12:14:13 -0700910 // Reset state.
911 mIsExpanded = false;
912 mListener.onRawContactSelected(rawContactId, isReadOnly);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700913 }
914 }
915 });
916 popup.show();
917 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700918 };
919 setUpAccountSelector(nameLabel, onClickListener);
920 }
921
922 private void setUpAccountSelector(String nameLabel, OnClickListener listener) {
923 addAccountHeader(nameLabel);
924 // Add handlers for choosing another account to save to.
925 mAccountHeaderExpanderIcon.setVisibility(View.VISIBLE);
926 mAccountHeaderContainer.setOnClickListener(listener);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700927 }
928
Walter Jang363d3fd2015-09-16 10:29:07 -0700929 private void addPhotoView() {
Gary Mai4ceabed2016-09-16 12:14:13 -0700930 if (!mCurrentRawContactDelta.hasMimeEntries(Photo.CONTENT_ITEM_TYPE)) {
931 wlog("No photo mimetype for this raw contact.");
Gary Maida20b472016-09-20 14:46:40 -0700932 mPhotoView.setVisibility(GONE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700933 return;
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700934 } else {
Gary Maida20b472016-09-20 14:46:40 -0700935 mPhotoView.setVisibility(VISIBLE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700936 }
Walter Jang06f73a12015-06-17 11:15:48 -0700937
Gary Mai4ceabed2016-09-16 12:14:13 -0700938 final ValuesDelta superPrimaryDelta = mCurrentRawContactDelta
939 .getSuperPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
Gary Maida20b472016-09-20 14:46:40 -0700940 if (superPrimaryDelta == null) {
941 Log.wtf(TAG, "addPhotoView: no ValueDelta found for current RawContactDelta"
942 + "that supports a photo.");
943 mPhotoView.setVisibility(GONE);
944 return;
945 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700946 // Set the photo view
Gary Mai18724182016-09-20 17:33:30 -0700947 mPhotoView.setPalette(mMaterialPalette);
948 mPhotoView.setPhoto(superPrimaryDelta);
Walter Jang31a74ad2015-10-02 19:17:39 -0700949
Gary Mai4ceabed2016-09-16 12:14:13 -0700950 if (isReadOnlyRawContact()) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700951 mPhotoView.setReadOnly(true);
952 return;
953 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700954 mPhotoView.setReadOnly(false);
Gary Mai4ceabed2016-09-16 12:14:13 -0700955 mPhotoValuesDelta = superPrimaryDelta;
Walter Jang31a74ad2015-10-02 19:17:39 -0700956 }
957
Walter Jangf5dfea42015-09-16 12:30:36 -0700958 private void addKindSectionViews() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700959 int i = -1;
Gary Mai98868d32016-09-14 11:55:04 -0700960
961 for (String mimeType : mSortedMimetypes) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700962 i++;
Gary Mai4ceabed2016-09-16 12:14:13 -0700963 // Ignore mime types that we've already handled
964 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
965 vlog("kind: " + i + " " + mimeType + " dropped");
966 continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800967 }
Gary Mai4ceabed2016-09-16 12:14:13 -0700968 final CompactKindSectionView kindSectionView;
969 final KindSectionData kindSectionData = mKindSectionDataMap.get(mimeType);
970 final ValuesDelta primaryDelta = mCurrentRawContactDelta.getPrimaryEntry(mimeType);
971 kindSectionView = inflateKindSectionView(mKindSectionViews, kindSectionData, mimeType,
972 primaryDelta);
Gary Mai98868d32016-09-14 11:55:04 -0700973 mKindSectionViews.addView(kindSectionView);
Walter Jangcab3dce2015-02-09 17:48:03 -0800974
Gary Mai98868d32016-09-14 11:55:04 -0700975 // Keep a pointer to the KindSectionView for each mimeType
976 mKindSectionViewMap.put(mimeType, kindSectionView);
Walter Jangf10ca152015-09-22 15:23:55 -0700977 }
Walter Jangf10ca152015-09-22 15:23:55 -0700978 }
979
Walter Jangf5dfea42015-09-16 12:30:36 -0700980 private CompactKindSectionView inflateKindSectionView(ViewGroup viewGroup,
Gary Mai98868d32016-09-14 11:55:04 -0700981 KindSectionData kindSectionData, String mimeType,
Walter Jang82acd422015-10-17 14:01:27 -0700982 ValuesDelta primaryValuesDelta) {
Walter Jangf5dfea42015-09-16 12:30:36 -0700983 final CompactKindSectionView kindSectionView = (CompactKindSectionView)
984 mLayoutInflater.inflate(R.layout.compact_item_kind_section, viewGroup,
985 /* attachToRoot =*/ false);
Walter Jang45b86d52015-10-15 15:23:16 -0700986 kindSectionView.setIsUserProfile(mIsUserProfile);
Walter Jangf5dfea42015-09-16 12:30:36 -0700987
Walter Jang192a01c2015-09-22 15:23:55 -0700988 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangf5dfea42015-09-16 12:30:36 -0700989 || Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -0700990 // Phone numbers and email addresses are always displayed,
991 // even if they are empty
Walter Jangf5dfea42015-09-16 12:30:36 -0700992 kindSectionView.setHideWhenEmpty(false);
993 }
Walter Jangf5dfea42015-09-16 12:30:36 -0700994
Walter Jang192a01c2015-09-22 15:23:55 -0700995 // Since phone numbers and email addresses displayed even if they are empty,
996 // they will be the only types you add new values to initially for new contacts
997 kindSectionView.setShowOneEmptyEditor(true);
998
Gary Mai98868d32016-09-14 11:55:04 -0700999 kindSectionView.setState(kindSectionData, mViewIdGenerator, mListener,
Walter Jang82acd422015-10-17 14:01:27 -07001000 primaryValuesDelta);
Walter Jangf5dfea42015-09-16 12:30:36 -07001001
1002 return kindSectionView;
Walter Jangcab3dce2015-02-09 17:48:03 -08001003 }
1004
Walter Jange3945952015-10-27 12:44:54 -07001005 void maybeSetReadOnlyDisplayNameAsPrimary(String readOnlyDisplayName) {
1006 if (TextUtils.isEmpty(readOnlyDisplayName)) return;
1007 final CompactKindSectionView primaryNameKindSectionView = getPrimaryNameKindSectionView();
1008 if (primaryNameKindSectionView != null && primaryNameKindSectionView.isEmptyName()) {
1009 vlog("name: using read only display name as primary name");
1010 primaryNameKindSectionView.setName(readOnlyDisplayName);
1011 }
1012 }
1013
1014 private CompactKindSectionView getPrimaryNameKindSectionView() {
Gary Mai98868d32016-09-14 11:55:04 -07001015 return mKindSectionViewMap.get(StructuredName.CONTENT_ITEM_TYPE);
Walter Jang4f5594a2015-10-06 18:40:31 -07001016 }
1017
Walter Jangd6753152015-10-02 09:23:13 -07001018 private void showAllFields() {
Walter Jang79658e12015-09-24 10:36:26 -07001019 // Stop hiding empty editors and allow the user to enter values for all kinds now
1020 for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
1021 final CompactKindSectionView kindSectionView =
1022 (CompactKindSectionView) mKindSectionViews.getChildAt(i);
1023 kindSectionView.setHideWhenEmpty(false);
1024 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
1025 }
1026 mIsExpanded = true;
Walter Jang79658e12015-09-24 10:36:26 -07001027
Walter Jangf5dfea42015-09-16 12:30:36 -07001028 // Hide the more fields button
1029 mMoreFields.setVisibility(View.GONE);
Walter Jangcab3dce2015-02-09 17:48:03 -08001030 }
1031
Marcus Hagerottcf7f2952016-09-06 13:49:17 -07001032 private boolean hasMoreFields() {
Gary Mai98868d32016-09-14 11:55:04 -07001033 for (CompactKindSectionView section : mKindSectionViewMap.values()) {
1034 if (section.getVisibility() != View.VISIBLE) {
1035 return true;
Marcus Hagerottcf7f2952016-09-06 13:49:17 -07001036 }
1037 }
1038 return false;
1039 }
1040
Walter Jangbf63a6d2015-05-05 09:14:35 -07001041 private static void vlog(String message) {
1042 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1043 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -08001044 }
1045 }
Walter Jang363d3fd2015-09-16 10:29:07 -07001046
1047 private static void wlog(String message) {
1048 if (Log.isLoggable(TAG, Log.WARN)) {
1049 Log.w(TAG, message);
1050 }
1051 }
1052
1053 private static void elog(String message) {
1054 Log.e(TAG, message);
1055 }
Walter Jangcab3dce2015-02-09 17:48:03 -08001056}