blob: 5388810da8c3bf95f584aa31e5dab3b729e435f0 [file] [log] [blame]
Walter Jangcab3dce2015-02-09 17:48:03 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.contacts.editor;
18
19import com.android.contacts.R;
20import com.android.contacts.common.model.AccountTypeManager;
21import com.android.contacts.common.model.RawContactDelta;
22import com.android.contacts.common.model.RawContactDeltaList;
Walter Jange720fde2015-02-17 10:54:14 -080023import com.android.contacts.common.model.RawContactModifier;
Walter Jangcab3dce2015-02-09 17:48:03 -080024import com.android.contacts.common.model.ValuesDelta;
25import com.android.contacts.common.model.account.AccountType;
Walter Jang2d3f31c2015-06-18 23:15:31 -070026import com.android.contacts.common.model.account.AccountWithDataSet;
Walter Jangcab3dce2015-02-09 17:48:03 -080027import com.android.contacts.common.model.dataitem.DataKind;
Walter Jang708ea9e2015-09-10 15:42:05 -070028import com.android.contacts.common.util.AccountsListAdapter;
Walter Jangf46abd82015-02-20 16:52:04 -080029import com.android.contacts.common.util.MaterialColorMapUtils;
Walter Jang708ea9e2015-09-10 15:42:05 -070030import com.android.contacts.util.UiClosables;
Walter Jangcab3dce2015-02-09 17:48:03 -080031
Tingting Wang655ad1a2015-10-05 17:51:14 -070032import android.content.ContentUris;
Walter Jangcab3dce2015-02-09 17:48:03 -080033import android.content.Context;
Walter Jangf10ca152015-09-22 15:23:55 -070034import android.database.Cursor;
Walter Jang3efae4a2015-02-18 11:12:00 -080035import android.graphics.Bitmap;
Walter Jang41b3ea12015-03-09 17:30:06 -070036import android.net.Uri;
Walter Jang3f18d612015-10-07 16:01:05 -070037import android.os.Bundle;
Walter Jang79658e12015-09-24 10:36:26 -070038import android.os.Parcel;
39import android.os.Parcelable;
Tingting Wang655ad1a2015-10-05 17:51:14 -070040import android.provider.ContactsContract;
Walter Jangcab3dce2015-02-09 17:48:03 -080041import android.provider.ContactsContract.CommonDataKinds.Email;
Walter Jangf5dfea42015-09-16 12:30:36 -070042import android.provider.ContactsContract.CommonDataKinds.Event;
Walter Jangcab3dce2015-02-09 17:48:03 -080043import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Walter Jangf5dfea42015-09-16 12:30:36 -070044import android.provider.ContactsContract.CommonDataKinds.Im;
Walter Jang3efae4a2015-02-18 11:12:00 -080045import android.provider.ContactsContract.CommonDataKinds.Nickname;
Walter Jangf5dfea42015-09-16 12:30:36 -070046import android.provider.ContactsContract.CommonDataKinds.Note;
47import android.provider.ContactsContract.CommonDataKinds.Organization;
Walter Jangcab3dce2015-02-09 17:48:03 -080048import android.provider.ContactsContract.CommonDataKinds.Phone;
49import android.provider.ContactsContract.CommonDataKinds.Photo;
Walter Jangf5dfea42015-09-16 12:30:36 -070050import android.provider.ContactsContract.CommonDataKinds.Relation;
51import android.provider.ContactsContract.CommonDataKinds.SipAddress;
Walter Jangcab3dce2015-02-09 17:48:03 -080052import android.provider.ContactsContract.CommonDataKinds.StructuredName;
Walter Jangf5dfea42015-09-16 12:30:36 -070053import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
54import android.provider.ContactsContract.CommonDataKinds.Website;
Walter Jangcab3dce2015-02-09 17:48:03 -080055import android.text.TextUtils;
56import android.util.AttributeSet;
57import android.util.Log;
Walter Jang2d3f31c2015-06-18 23:15:31 -070058import android.util.Pair;
Walter Jangcab3dce2015-02-09 17:48:03 -080059import android.view.LayoutInflater;
Walter Jangb1c87622015-02-13 17:51:38 -080060import android.view.View;
Walter Jangcab3dce2015-02-09 17:48:03 -080061import android.view.ViewGroup;
Walter Jang708ea9e2015-09-10 15:42:05 -070062import android.widget.AdapterView;
Tingting Wang655ad1a2015-10-05 17:51:14 -070063import android.widget.BaseAdapter;
64import android.widget.ImageView;
Walter Jangcab3dce2015-02-09 17:48:03 -080065import android.widget.LinearLayout;
Walter Jang708ea9e2015-09-10 15:42:05 -070066import android.widget.ListPopupWindow;
Walter Jang2d3f31c2015-06-18 23:15:31 -070067import android.widget.TextView;
Walter Jangcab3dce2015-02-09 17:48:03 -080068
69import java.util.ArrayList;
Walter Jangf5dfea42015-09-16 12:30:36 -070070import java.util.Arrays;
Walter Jang192a01c2015-09-22 15:23:55 -070071import java.util.Collections;
Walter Jangf5dfea42015-09-16 12:30:36 -070072import java.util.Comparator;
Walter Jang19d985f2015-07-01 13:36:27 -070073import java.util.HashMap;
Tingting Wanga0b69402015-10-13 14:27:58 -070074import java.util.LinkedHashMap;
75import java.util.LinkedHashSet;
Walter Jangcab3dce2015-02-09 17:48:03 -080076import java.util.List;
Walter Jang19d985f2015-07-01 13:36:27 -070077import java.util.Map;
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 */
Walter Jangb6ca2722015-02-20 11:10:25 -080083public class CompactRawContactsEditorView extends LinearLayout implements View.OnClickListener {
Walter Jangcab3dce2015-02-09 17:48:03 -080084
Walter Jang4f5594a2015-10-06 18:40:31 -070085 static final String TAG = "CompactEditorView";
Walter Jangcab3dce2015-02-09 17:48:03 -080086
Walter Jang192a01c2015-09-22 15:23:55 -070087 private static final KindSectionDataMapEntryComparator
88 KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR = new KindSectionDataMapEntryComparator();
Walter Jangf5dfea42015-09-16 12:30:36 -070089
Walter Jangb6ca2722015-02-20 11:10:25 -080090 /**
91 * Callbacks for hosts of {@link CompactRawContactsEditorView}s.
92 */
93 public interface Listener {
94
95 /**
Walter Jang151f3e62015-02-26 15:29:40 -080096 * Invoked when the structured name editor field has changed.
97 *
98 * @param rawContactId The raw contact ID from the underlying {@link RawContactDelta}.
99 * @param valuesDelta The values from the underlying {@link RawContactDelta}.
100 */
101 public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta);
Walter Jang708ea9e2015-09-10 15:42:05 -0700102
103 /**
104 * Invoked when the compact editor should rebind editors for a new account.
105 *
106 * @param oldState Old data being edited.
107 * @param oldAccount Old account associated with oldState.
108 * @param newAccount New account to be used.
109 */
110 public void onRebindEditorsForNewContact(RawContactDelta oldState,
111 AccountWithDataSet oldAccount, AccountWithDataSet newAccount);
Walter Jang79658e12015-09-24 10:36:26 -0700112
113 /**
114 * Invoked when no editors could be bound for the contact.
115 */
116 public void onBindEditorsFailed();
Walter Jangd6753152015-10-02 09:23:13 -0700117
118 /**
119 * Invoked after editors have been bound for the contact.
120 */
121 public void onEditorsBound();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700122
123 /**
Tingting Wang5585c6e2015-10-14 11:19:58 -0700124 * Invoked when a rawcontact from linked contacts is selected in editor.
Tingting Wang655ad1a2015-10-05 17:51:14 -0700125 */
Tingting Wang91cee282015-10-07 13:48:17 -0700126 public void onRawContactSelected(Uri uri, long rawContactId, boolean isReadOnly);
Walter Jang3f18d612015-10-07 16:01:05 -0700127
128 /**
129 * Returns the map of raw contact IDs to newly taken or selected photos that have not
130 * yet been saved to CP2.
131 */
132 public Bundle getUpdatedPhotos();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700133 }
134
135 /**
136 * Used to list the account info for the given raw contacts list.
137 */
138 private static final class RawContactAccountListAdapter extends BaseAdapter {
139 private final LayoutInflater mInflater;
140 private final Context mContext;
141 private final RawContactDeltaList mRawContactDeltas;
142
143 public RawContactAccountListAdapter(Context context, RawContactDeltaList rawContactDeltas) {
144 mContext = context;
145 mRawContactDeltas = new RawContactDeltaList();
146 for (RawContactDelta rawContactDelta : rawContactDeltas) {
147 if (rawContactDelta.isVisible()) {
148 mRawContactDeltas.add(rawContactDelta);
149 }
150 }
151 mInflater = LayoutInflater.from(context);
152 }
153
154 @Override
155 public View getView(int position, View convertView, ViewGroup parent) {
156 final View resultView = convertView != null ? convertView
157 : mInflater.inflate(R.layout.account_selector_list_item, parent, false);
158
159 final RawContactDelta rawContactDelta = mRawContactDeltas.get(position);
160 final String accountName = rawContactDelta.getAccountName();
161 final AccountType accountType = rawContactDelta.getRawContactAccountType(mContext);
162
163 final TextView text1 = (TextView) resultView.findViewById(android.R.id.text1);
164 text1.setText(accountType.getDisplayLabel(mContext));
165
166 // For email addresses, we don't want to truncate at end, which might cut off the domain
167 // name.
168 final TextView text2 = (TextView) resultView.findViewById(android.R.id.text2);
Tingting Wanga0b69402015-10-13 14:27:58 -0700169 if (TextUtils.isEmpty(accountName)) {
170 text2.setVisibility(View.GONE);
171 } else {
172 text2.setText(accountName);
173 text2.setEllipsize(TextUtils.TruncateAt.MIDDLE);
174 }
Tingting Wang655ad1a2015-10-05 17:51:14 -0700175
176 final ImageView icon = (ImageView) resultView.findViewById(android.R.id.icon);
177 icon.setImageDrawable(accountType.getDisplayIcon(mContext));
178
179 return resultView;
180 }
181
182 @Override
183 public int getCount() {
184 return mRawContactDeltas.size();
185 }
186
187 @Override
188 public RawContactDelta getItem(int position) {
189 return mRawContactDeltas.get(position);
190 }
191
192 @Override
193 public long getItemId(int position) {
194 return getItem(position).getRawContactId();
195 }
Walter Jang151f3e62015-02-26 15:29:40 -0800196 }
197
Walter Jang192a01c2015-09-22 15:23:55 -0700198 /** Used to sort entire kind sections. */
199 private static final class KindSectionDataMapEntryComparator implements
Walter Jangcbd431d2015-10-06 13:07:30 -0700200 Comparator<Map.Entry<String,KindSectionDataList>> {
Walter Jangf5dfea42015-09-16 12:30:36 -0700201
Walter Jang192a01c2015-09-22 15:23:55 -0700202 final MimeTypeComparator mMimeTypeComparator = new MimeTypeComparator();
203
204 @Override
Walter Jangcbd431d2015-10-06 13:07:30 -0700205 public int compare(Map.Entry<String, KindSectionDataList> entry1,
206 Map.Entry<String, KindSectionDataList> entry2) {
Walter Jang192a01c2015-09-22 15:23:55 -0700207 if (entry1 == entry2) return 0;
208 if (entry1 == null) return -1;
209 if (entry2 == null) return 1;
210
211 final String mimeType1 = entry1.getKey();
212 final String mimeType2 = entry2.getKey();
213
214 return mMimeTypeComparator.compare(mimeType1, mimeType2);
215 }
216 }
217
218 /**
219 * Sorts kinds roughly the same as quick contacts; we diverge in the following ways:
220 * <ol>
221 * <li>All names are together at the top.</li>
222 * <li>IM is moved up after addresses</li>
223 * <li>SIP addresses are moved to below phone numbers</li>
Walter Jang79658e12015-09-24 10:36:26 -0700224 * <li>Group membership is placed at the end</li>
Walter Jang192a01c2015-09-22 15:23:55 -0700225 * </ol>
226 */
227 private static final class MimeTypeComparator implements Comparator<String> {
228
Walter Jangf5dfea42015-09-16 12:30:36 -0700229 private static final List<String> MIME_TYPE_ORDER = Arrays.asList(new String[] {
230 StructuredName.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700231 Nickname.CONTENT_ITEM_TYPE,
Walter Jang2ae21752015-09-25 11:25:38 -0700232 Organization.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700233 Phone.CONTENT_ITEM_TYPE,
234 SipAddress.CONTENT_ITEM_TYPE,
235 Email.CONTENT_ITEM_TYPE,
236 StructuredPostal.CONTENT_ITEM_TYPE,
237 Im.CONTENT_ITEM_TYPE,
238 Website.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700239 Event.CONTENT_ITEM_TYPE,
240 Relation.CONTENT_ITEM_TYPE,
Walter Jangf10ca152015-09-22 15:23:55 -0700241 Note.CONTENT_ITEM_TYPE,
242 GroupMembership.CONTENT_ITEM_TYPE
Walter Jangf5dfea42015-09-16 12:30:36 -0700243 });
244
245 @Override
Walter Jang192a01c2015-09-22 15:23:55 -0700246 public int compare(String mimeType1, String mimeType2) {
247 if (mimeType1 == mimeType2) return 0;
248 if (mimeType1 == null) return -1;
249 if (mimeType2 == null) return 1;
Walter Jangf5dfea42015-09-16 12:30:36 -0700250
251 int index1 = MIME_TYPE_ORDER.indexOf(mimeType1);
252 int index2 = MIME_TYPE_ORDER.indexOf(mimeType2);
253
254 // Fallback to alphabetical ordering of the mime type if both are not found
255 if (index1 < 0 && index2 < 0) return mimeType1.compareTo(mimeType2);
256 if (index1 < 0) return 1;
257 if (index2 < 0) return -1;
258
259 return index1 < index2 ? -1 : 1;
260 }
261 }
262
Walter Jang192a01c2015-09-22 15:23:55 -0700263 /**
264 * Sorts primary accounts and google account types before others.
265 */
266 private static final class EditorComparator implements Comparator<KindSectionData> {
267
268 private RawContactDeltaComparator mRawContactDeltaComparator;
269
270 private EditorComparator(Context context) {
271 mRawContactDeltaComparator = new RawContactDeltaComparator(context);
272 }
273
274 @Override
275 public int compare(KindSectionData kindSectionData1, KindSectionData kindSectionData2) {
276 if (kindSectionData1 == kindSectionData2) return 0;
277 if (kindSectionData1 == null) return -1;
278 if (kindSectionData2 == null) return 1;
279
280 final RawContactDelta rawContactDelta1 = kindSectionData1.getRawContactDelta();
281 final RawContactDelta rawContactDelta2 = kindSectionData2.getRawContactDelta();
282
283 if (rawContactDelta1 == rawContactDelta2) return 0;
284 if (rawContactDelta1 == null) return -1;
285 if (rawContactDelta2 == null) return 1;
286
287 return mRawContactDeltaComparator.compare(rawContactDelta1, rawContactDelta2);
288 }
289 }
290
291 /**
292 * Sorts primary account names first, followed by google account types, and other account
293 * types last. For names from the same account we order structured names before nicknames,
294 * but still keep names from the same account together.
295 */
296 private static final class NameEditorComparator implements Comparator<KindSectionData> {
297
Walter Jang4f5594a2015-10-06 18:40:31 -0700298 private final RawContactDeltaComparator mRawContactDeltaComparator;
299 private final MimeTypeComparator mMimeTypeComparator;
300 private final RawContactDelta mPrimaryRawContactDelta;
Walter Jang192a01c2015-09-22 15:23:55 -0700301
302 private NameEditorComparator(Context context, RawContactDelta primaryRawContactDelta) {
303 mRawContactDeltaComparator = new RawContactDeltaComparator(context);
304 mMimeTypeComparator = new MimeTypeComparator();
305 mPrimaryRawContactDelta = primaryRawContactDelta;
306 }
307
308 @Override
309 public int compare(KindSectionData kindSectionData1, KindSectionData kindSectionData2) {
310 if (kindSectionData1 == kindSectionData2) return 0;
311 if (kindSectionData1 == null) return -1;
312 if (kindSectionData2 == null) return 1;
313
314 final RawContactDelta rawContactDelta1 = kindSectionData1.getRawContactDelta();
315 final RawContactDelta rawContactDelta2 = kindSectionData2.getRawContactDelta();
316
317 if (rawContactDelta1 == rawContactDelta2) return 0;
318 if (rawContactDelta1 == null) return -1;
319 if (rawContactDelta2 == null) return 1;
320
321 final boolean isRawContactDelta1Primary =
322 mPrimaryRawContactDelta.equals(rawContactDelta1);
323 final boolean isRawContactDelta2Primary =
324 mPrimaryRawContactDelta.equals(rawContactDelta2);
325
326 // If both names are from the primary account, sort my by mime type
327 if (isRawContactDelta1Primary && isRawContactDelta2Primary) {
328 final String mimeType1 = kindSectionData1.getDataKind().mimeType;
329 final String mimeType2 = kindSectionData2.getDataKind().mimeType;
330 return mMimeTypeComparator.compare(mimeType1, mimeType2);
331 }
332
333 // The primary account name should be before all others
Walter Jang79658e12015-09-24 10:36:26 -0700334 if (isRawContactDelta1Primary) return -1;
335 if (isRawContactDelta2Primary) return 1;
Walter Jang192a01c2015-09-22 15:23:55 -0700336
337 return mRawContactDeltaComparator.compare(rawContactDelta1, rawContactDelta2);
338 }
339 }
340
Walter Jang79658e12015-09-24 10:36:26 -0700341 public static class SavedState extends BaseSavedState {
342
343 public static final Parcelable.Creator<SavedState> CREATOR =
344 new Parcelable.Creator<SavedState>() {
345 public SavedState createFromParcel(Parcel in) {
346 return new SavedState(in);
347 }
348 public SavedState[] newArray(int size) {
349 return new SavedState[size];
350 }
351 };
352
353 private boolean mIsExpanded;
354
355 public SavedState(Parcelable superState) {
356 super(superState);
357 }
358
359 private SavedState(Parcel in) {
360 super(in);
361 mIsExpanded = in.readInt() != 0;
362 }
363
364 @Override
365 public void writeToParcel(Parcel out, int flags) {
366 super.writeToParcel(out, flags);
367 out.writeInt(mIsExpanded ? 1 : 0);
368 }
369 }
370
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700371 private CompactRawContactsEditorView.Listener mListener;
Walter Jangb6ca2722015-02-20 11:10:25 -0800372
Walter Jangcab3dce2015-02-09 17:48:03 -0800373 private AccountTypeManager mAccountTypeManager;
374 private LayoutInflater mLayoutInflater;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800375
Walter Jangcab3dce2015-02-09 17:48:03 -0800376 private ViewIdGenerator mViewIdGenerator;
Walter Jangf46abd82015-02-20 16:52:04 -0800377 private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
Walter Jang708ea9e2015-09-10 15:42:05 -0700378 private long mPhotoId;
Walter Jang4f5594a2015-10-06 18:40:31 -0700379 private String mReadOnlyDisplayName;
Walter Jang708ea9e2015-09-10 15:42:05 -0700380 private boolean mHasNewContact;
381 private boolean mIsUserProfile;
382 private AccountWithDataSet mPrimaryAccount;
383 private RawContactDelta mPrimaryRawContactDelta;
Walter Jangcbd431d2015-10-06 13:07:30 -0700384 private Map<String,KindSectionDataList> mKindSectionDataMap = new HashMap<>();
Walter Jangcab3dce2015-02-09 17:48:03 -0800385
Walter Jang708ea9e2015-09-10 15:42:05 -0700386 // Account header
387 private View mAccountHeaderContainer;
388 private TextView mAccountHeaderType;
389 private TextView mAccountHeaderName;
Tingting Wang91cee282015-10-07 13:48:17 -0700390 private ImageView mAccountHeaderIcon;
Walter Jang708ea9e2015-09-10 15:42:05 -0700391
392 // Account selector
393 private View mAccountSelectorContainer;
394 private View mAccountSelector;
395 private TextView mAccountSelectorType;
396 private TextView mAccountSelectorName;
397
Tingting Wang655ad1a2015-10-05 17:51:14 -0700398 // Raw contacts selector
399 private View mRawContactContainer;
400 private TextView mRawContactSummary;
401 private ImageView mPrimaryAccountIcon;
402
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700403 private CompactPhotoEditorView mPhotoView;
Walter Jangf5dfea42015-09-16 12:30:36 -0700404 private ViewGroup mKindSectionViews;
Walter Jangf10ca152015-09-22 15:23:55 -0700405 private Map<String,List<CompactKindSectionView>> mKindSectionViewsMap = new HashMap<>();
Walter Jangb6ca2722015-02-20 11:10:25 -0800406 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -0800407
Walter Jang79658e12015-09-24 10:36:26 -0700408 private boolean mIsExpanded;
Walter Jangcbd431d2015-10-06 13:07:30 -0700409
Walter Jang3efae4a2015-02-18 11:12:00 -0800410 private long mPhotoRawContactId;
Walter Jang31a74ad2015-10-02 19:17:39 -0700411 private ValuesDelta mPhotoValuesDelta;
Walter Jang4f5594a2015-10-06 18:40:31 -0700412 private StructuredNameEditorView mPrimaryNameEditorView;
Walter Jang3efae4a2015-02-18 11:12:00 -0800413
Walter Jangcab3dce2015-02-09 17:48:03 -0800414 public CompactRawContactsEditorView(Context context) {
415 super(context);
416 }
417
418 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
419 super(context, attrs);
420 }
421
Walter Jangb6ca2722015-02-20 11:10:25 -0800422 /**
423 * Sets the receiver for {@link CompactRawContactsEditorView} callbacks.
424 */
425 public void setListener(Listener listener) {
426 mListener = listener;
427 }
428
Walter Jangcab3dce2015-02-09 17:48:03 -0800429 @Override
430 protected void onFinishInflate() {
431 super.onFinishInflate();
432
433 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
434 mLayoutInflater = (LayoutInflater)
435 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
436
Walter Jang708ea9e2015-09-10 15:42:05 -0700437 // Account header
438 mAccountHeaderContainer = findViewById(R.id.account_container);
439 mAccountHeaderType = (TextView) findViewById(R.id.account_type);
440 mAccountHeaderName = (TextView) findViewById(R.id.account_name);
Tingting Wang91cee282015-10-07 13:48:17 -0700441 mAccountHeaderIcon = (ImageView) findViewById(R.id.account_type_icon);
Walter Jang708ea9e2015-09-10 15:42:05 -0700442
443 // Account selector
444 mAccountSelectorContainer = findViewById(R.id.account_selector_container);
445 mAccountSelector = findViewById(R.id.account);
446 mAccountSelectorType = (TextView) findViewById(R.id.account_type_selector);
447 mAccountSelectorName = (TextView) findViewById(R.id.account_name_selector);
448
Tingting Wang655ad1a2015-10-05 17:51:14 -0700449 // Raw contacts selector
450 mRawContactContainer = findViewById(R.id.all_rawcontacts_accounts_container);
451 mRawContactSummary = (TextView) findViewById(R.id.rawcontacts_accounts_summary);
452 mPrimaryAccountIcon = (ImageView) findViewById(R.id.primary_account_icon);
453
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700454 mPhotoView = (CompactPhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangf5dfea42015-09-16 12:30:36 -0700455 mKindSectionViews = (LinearLayout) findViewById(R.id.kind_section_views);
Walter Jangb6ca2722015-02-20 11:10:25 -0800456 mMoreFields = findViewById(R.id.more_fields);
457 mMoreFields.setOnClickListener(this);
458 }
459
Walter Jangb6ca2722015-02-20 11:10:25 -0800460 @Override
461 public void onClick(View view) {
Walter Jangf5dfea42015-09-16 12:30:36 -0700462 if (view.getId() == R.id.more_fields) {
Walter Jangd6753152015-10-02 09:23:13 -0700463 showAllFields();
Walter Jangb6ca2722015-02-20 11:10:25 -0800464 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800465 }
466
467 @Override
468 public void setEnabled(boolean enabled) {
469 super.setEnabled(enabled);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700470 final int childCount = mKindSectionViews.getChildCount();
471 for (int i = 0; i < childCount; i++) {
472 mKindSectionViews.getChildAt(i).setEnabled(enabled);
Walter Jangcab3dce2015-02-09 17:48:03 -0800473 }
474 }
475
Walter Jang79658e12015-09-24 10:36:26 -0700476 @Override
477 public Parcelable onSaveInstanceState() {
478 final Parcelable superState = super.onSaveInstanceState();
479 final SavedState savedState = new SavedState(superState);
480 savedState.mIsExpanded = mIsExpanded;
481 return savedState;
482 }
483
484 @Override
485 public void onRestoreInstanceState(Parcelable state) {
486 if(!(state instanceof SavedState)) {
487 super.onRestoreInstanceState(state);
488 return;
489 }
490 final SavedState savedState = (SavedState) state;
491 super.onRestoreInstanceState(savedState.getSuperState());
492 mIsExpanded = savedState.mIsExpanded;
Walter Jangd6753152015-10-02 09:23:13 -0700493 if (mIsExpanded) {
494 showAllFields();
Walter Jang79658e12015-09-24 10:36:26 -0700495 }
496 }
497
Walter Jang3efae4a2015-02-18 11:12:00 -0800498 /**
Walter Jang31a74ad2015-10-02 19:17:39 -0700499 * Pass through to {@link CompactPhotoEditorView#setListener}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800500 */
Walter Jang31a74ad2015-10-02 19:17:39 -0700501 public void setPhotoListener(CompactPhotoEditorView.Listener listener) {
502 mPhotoView.setListener(listener);
Walter Jang3efae4a2015-02-18 11:12:00 -0800503 }
504
Walter Jang31a74ad2015-10-02 19:17:39 -0700505 public void removePhoto() {
Walter Jang45b86d52015-10-15 15:23:16 -0700506 mPhotoValuesDelta.setFromTemplate(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700507 mPhotoValuesDelta.put(Photo.PHOTO, (byte[]) null);
508
509 mPhotoView.removePhoto();
Walter Jang3efae4a2015-02-18 11:12:00 -0800510 }
511
512 /**
Walter Jang41b3ea12015-03-09 17:30:06 -0700513 * Pass through to {@link CompactPhotoEditorView#setFullSizedPhoto(Uri)}.
514 */
515 public void setFullSizePhoto(Uri photoUri) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700516 mPhotoView.setFullSizedPhoto(photoUri);
Walter Jang41b3ea12015-03-09 17:30:06 -0700517 }
518
Walter Jang31a74ad2015-10-02 19:17:39 -0700519 public void updatePhoto(Uri photoUri) {
Walter Jang45b86d52015-10-15 15:23:16 -0700520 mPhotoValuesDelta.setFromTemplate(false);
Walter Jang3f18d612015-10-07 16:01:05 -0700521
Walter Jang45b86d52015-10-15 15:23:16 -0700522 if (!mIsUserProfile) {
523 // Unset primary for all photos
524 unsetSuperPrimary();
525
526 // Mark the currently displayed photo as primary
527 mPhotoValuesDelta.setSuperPrimary(true);
528 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700529
530 mPhotoView.setFullSizedPhoto(photoUri);
531 }
532
Walter Jang3f18d612015-10-07 16:01:05 -0700533 private void unsetSuperPrimary() {
534 final List<KindSectionData> kindSectionDataList =
535 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
536 for (KindSectionData kindSectionData : kindSectionDataList) {
537 final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
538 for (ValuesDelta valuesDelta : valuesDeltaList) {
539 valuesDelta.setSuperPrimary(false);
540 }
541 }
542 }
543
Walter Jang41b3ea12015-03-09 17:30:06 -0700544 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800545 * Pass through to {@link CompactPhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800546 */
547 public boolean isWritablePhotoSet() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700548 return mPhotoView.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800549 }
550
551 /**
Walter Jang3efae4a2015-02-18 11:12:00 -0800552 * Get the raw contact ID for the CompactHeaderView photo.
553 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800554 public long getPhotoRawContactId() {
555 return mPhotoRawContactId;
556 }
557
Walter Jang4f5594a2015-10-06 18:40:31 -0700558 public StructuredNameEditorView getPrimaryNameEditorView() {
559 return mPrimaryNameEditorView;
560 }
561
Walter Jang31a74ad2015-10-02 19:17:39 -0700562 /**
563 * Returns a data holder for every non-default/non-empty photo from each raw contact, whether
564 * the raw contact is writable or not.
565 */
566 public ArrayList<CompactPhotoSelectionFragment.Photo> getPhotos() {
567 final ArrayList<CompactPhotoSelectionFragment.Photo> photos = new ArrayList<>();
568
Walter Jang3f18d612015-10-07 16:01:05 -0700569 final Bundle updatedPhotos = mListener == null ? null : mListener.getUpdatedPhotos();
570
Walter Jang31a74ad2015-10-02 19:17:39 -0700571 final List<KindSectionData> kindSectionDataList =
572 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
573 for (int i = 0; i < kindSectionDataList.size(); i++) {
574 final KindSectionData kindSectionData = kindSectionDataList.get(i);
575 final AccountType accountType = kindSectionData.getAccountType();
576 final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
577 if (valuesDeltaList == null || valuesDeltaList.isEmpty()) continue;
578 for (int j = 0; j < valuesDeltaList.size(); j++) {
579 final ValuesDelta valuesDelta = valuesDeltaList.get(j);
580 final Bitmap bitmap = EditorUiUtils.getPhotoBitmap(valuesDelta);
581 if (bitmap == null) continue;
582
583 final CompactPhotoSelectionFragment.Photo photo =
584 new CompactPhotoSelectionFragment.Photo();
585 photo.titleRes = accountType.titleRes;
586 photo.iconRes = accountType.iconRes;
587 photo.syncAdapterPackageName = accountType.syncAdapterPackageName;
588 photo.valuesDelta = valuesDelta;
589 photo.primary = valuesDelta.isSuperPrimary();
590 photo.kindSectionDataListIndex = i;
591 photo.valuesDeltaListIndex = j;
Walter Jang3f18d612015-10-07 16:01:05 -0700592
593 if (updatedPhotos != null) {
594 photo.updatedPhotoUri = (Uri) updatedPhotos.get(String.valueOf(
595 kindSectionData.getRawContactDelta().getRawContactId()));
596 }
597
Walter Jang31a74ad2015-10-02 19:17:39 -0700598 photos.add(photo);
599 }
600 }
601
602 return photos;
603 }
604
605 /**
606 * Marks the raw contact photo given as primary for the aggregate contact and updates the
607 * UI.
608 */
609 public void setPrimaryPhoto(CompactPhotoSelectionFragment.Photo photo) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700610 // Find the values delta to mark as primary
Walter Jang3f18d612015-10-07 16:01:05 -0700611 final KindSectionDataList kindSectionDataList =
612 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
Walter Jang31a74ad2015-10-02 19:17:39 -0700613 if (photo.kindSectionDataListIndex < 0
614 || photo.kindSectionDataListIndex >= kindSectionDataList.size()) {
615 wlog("Invalid kind section data list index");
616 return;
617 }
618 final KindSectionData kindSectionData =
619 kindSectionDataList.get(photo.kindSectionDataListIndex);
620 final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
621 if (photo.valuesDeltaListIndex >= valuesDeltaList.size()) {
622 wlog("Invalid values delta list index");
623 return;
624 }
625 final ValuesDelta valuesDelta = valuesDeltaList.get(photo.valuesDeltaListIndex);
626 valuesDelta.setFromTemplate(false);
Walter Jang45b86d52015-10-15 15:23:16 -0700627
628 if (!mIsUserProfile) {
629 // Unset primary for all other photos
630 unsetSuperPrimary();
631
632 valuesDelta.setSuperPrimary(true);
633 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700634
635 // Update the UI
636 mPhotoView.setPhoto(valuesDelta, mMaterialPalette);
637 }
638
Walter Jangd35e5ef2015-02-24 09:18:16 -0800639 public View getAggregationAnchorView() {
Walter Jangf10ca152015-09-22 15:23:55 -0700640 final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
Walter Jang192a01c2015-09-22 15:23:55 -0700641 StructuredName.CONTENT_ITEM_TYPE);
Walter Jangf10ca152015-09-22 15:23:55 -0700642 if (!kindSectionViews.isEmpty()) {
Walter Jang192a01c2015-09-22 15:23:55 -0700643 return mKindSectionViews.getChildAt(0).findViewById(R.id.anchor_view);
Walter Jangd35e5ef2015-02-24 09:18:16 -0800644 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700645 return null;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800646 }
647
Walter Jangf10ca152015-09-22 15:23:55 -0700648 public void setGroupMetaData(Cursor groupMetaData) {
649 final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
650 GroupMembership.CONTENT_ITEM_TYPE);
651 for (CompactKindSectionView kindSectionView : kindSectionViews) {
652 kindSectionView.setGroupMetaData(groupMetaData);
Walter Jangd6753152015-10-02 09:23:13 -0700653 if (mIsExpanded) {
654 kindSectionView.setHideWhenEmpty(false);
655 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
656 }
Walter Jangf10ca152015-09-22 15:23:55 -0700657 }
658 }
659
Walter Jangf46abd82015-02-20 16:52:04 -0800660 public void setState(RawContactDeltaList rawContactDeltas,
Walter Jang06f73a12015-06-17 11:15:48 -0700661 MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
Walter Jang4f5594a2015-10-06 18:40:31 -0700662 long photoId, String readOnlyDisplayName, boolean hasNewContact,
663 boolean isUserProfile, AccountWithDataSet primaryAccount) {
Walter Jang363d3fd2015-09-16 10:29:07 -0700664 mKindSectionDataMap.clear();
Walter Jangf5dfea42015-09-16 12:30:36 -0700665 mKindSectionViews.removeAllViews();
Walter Jang363d3fd2015-09-16 10:29:07 -0700666 mMoreFields.setVisibility(View.VISIBLE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800667
Walter Jangf46abd82015-02-20 16:52:04 -0800668 mMaterialPalette = materialPalette;
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700669 mViewIdGenerator = viewIdGenerator;
Walter Jang708ea9e2015-09-10 15:42:05 -0700670 mPhotoId = photoId;
Walter Jang4f5594a2015-10-06 18:40:31 -0700671 mReadOnlyDisplayName = readOnlyDisplayName;
Walter Jang708ea9e2015-09-10 15:42:05 -0700672 mHasNewContact = hasNewContact;
673 mIsUserProfile = isUserProfile;
674 mPrimaryAccount = primaryAccount;
675 if (mPrimaryAccount == null) {
676 mPrimaryAccount = ContactEditorUtils.getInstance(getContext()).getDefaultAccount();
677 }
678 vlog("state: primary " + mPrimaryAccount);
Walter Jangcab3dce2015-02-09 17:48:03 -0800679
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700680 // Parse the given raw contact deltas
681 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
682 elog("No raw contact deltas");
Walter Jang79658e12015-09-24 10:36:26 -0700683 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jangf5dfea42015-09-16 12:30:36 -0700684 return;
685 }
Walter Jangcbd431d2015-10-06 13:07:30 -0700686 parseRawContactDeltas(rawContactDeltas);
Walter Jang79658e12015-09-24 10:36:26 -0700687 if (mKindSectionDataMap.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700688 elog("No kind section data parsed from RawContactDelta(s)");
Walter Jang79658e12015-09-24 10:36:26 -0700689 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700690 return;
691 }
Walter Jangcbd431d2015-10-06 13:07:30 -0700692 mPrimaryRawContactDelta = mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE)
693 .getEntryToWrite(mPrimaryAccount, mHasNewContact).first.getRawContactDelta();
694 if (mPrimaryRawContactDelta != null) {
695 RawContactModifier.ensureKindExists(mPrimaryRawContactDelta,
696 mPrimaryRawContactDelta.getAccountType(mAccountTypeManager),
697 StructuredName.CONTENT_ITEM_TYPE);
698 RawContactModifier.ensureKindExists(mPrimaryRawContactDelta,
699 mPrimaryRawContactDelta.getAccountType(mAccountTypeManager),
700 Photo.CONTENT_ITEM_TYPE);
701 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700702
703 // Setup the view
Tingting Wang655ad1a2015-10-05 17:51:14 -0700704 addAccountInfo(rawContactDeltas);
Walter Jang363d3fd2015-09-16 10:29:07 -0700705 addPhotoView();
Walter Jangf5dfea42015-09-16 12:30:36 -0700706 addKindSectionViews();
Walter Jang4f5594a2015-10-06 18:40:31 -0700707 if (mHasNewContact) {
708 maybeCopyPrimaryDisplayName();
Walter Jangd6753152015-10-02 09:23:13 -0700709 }
Walter Jang4f5594a2015-10-06 18:40:31 -0700710 if (mIsExpanded) showAllFields();
Walter Jangd6753152015-10-02 09:23:13 -0700711
712 if (mListener != null) mListener.onEditorsBound();
Walter Jangcab3dce2015-02-09 17:48:03 -0800713 }
714
Walter Jangcbd431d2015-10-06 13:07:30 -0700715 private void parseRawContactDeltas(RawContactDeltaList rawContactDeltas) {
Walter Jang363d3fd2015-09-16 10:29:07 -0700716 // Build the kind section data list map
Walter Jang192a01c2015-09-22 15:23:55 -0700717 vlog("parse: " + rawContactDeltas.size() + " rawContactDelta(s)");
718 for (int j = 0; j < rawContactDeltas.size(); j++) {
719 final RawContactDelta rawContactDelta = rawContactDeltas.get(j);
720 vlog("parse: " + j + " rawContactDelta" + rawContactDelta);
Walter Jang363d3fd2015-09-16 10:29:07 -0700721 if (rawContactDelta == null || !rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800722 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jang363d3fd2015-09-16 10:29:07 -0700723 if (accountType == null) continue;
724 final List<DataKind> dataKinds = accountType.getSortedDataKinds();
725 final int dataKindSize = dataKinds == null ? 0 : dataKinds.size();
726 vlog("parse: " + dataKindSize + " dataKinds(s)");
727 for (int i = 0; i < dataKindSize; i++) {
728 final DataKind dataKind = dataKinds.get(i);
Walter Jang192a01c2015-09-22 15:23:55 -0700729 if (dataKind == null || !dataKind.editable) {
730 vlog("parse: " + i + " " + dataKind.mimeType + " dropped read-only");
731 continue;
732 }
733 final String mimeType = dataKind.mimeType;
734
735 // Skip psuedo mime types
736 if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
737 || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
738 vlog("parse: " + i + " " + dataKind.mimeType + " dropped pseudo type");
739 continue;
740 }
741
Walter Jang3f18d612015-10-07 16:01:05 -0700742 final KindSectionDataList kindSectionDataList =
Walter Jangcbd431d2015-10-06 13:07:30 -0700743 getOrCreateKindSectionDataList(mimeType);
Walter Jang363d3fd2015-09-16 10:29:07 -0700744 final KindSectionData kindSectionData =
745 new KindSectionData(accountType, dataKind, rawContactDelta);
746 kindSectionDataList.add(kindSectionData);
Walter Jang192a01c2015-09-22 15:23:55 -0700747
Walter Jang79658e12015-09-24 10:36:26 -0700748 // Note we must create nickname entries
Walter Jang192a01c2015-09-22 15:23:55 -0700749 if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jang79658e12015-09-24 10:36:26 -0700750 && kindSectionData.getValuesDeltas().isEmpty()) {
Walter Jang192a01c2015-09-22 15:23:55 -0700751 RawContactModifier.insertChild(rawContactDelta, dataKind);
752 }
753
Walter Jang363d3fd2015-09-16 10:29:07 -0700754 vlog("parse: " + i + " " + dataKind.mimeType + " " +
Walter Jang192a01c2015-09-22 15:23:55 -0700755 kindSectionData.getValuesDeltas().size() + " value(s)");
Walter Jangac679af2015-06-01 12:17:06 -0700756 }
757 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800758 }
759
Walter Jang3f18d612015-10-07 16:01:05 -0700760 private KindSectionDataList getOrCreateKindSectionDataList(String mimeType) {
Walter Jang192a01c2015-09-22 15:23:55 -0700761 // Put structured names and nicknames together
762 mimeType = Nickname.CONTENT_ITEM_TYPE.equals(mimeType)
763 ? StructuredName.CONTENT_ITEM_TYPE : mimeType;
Walter Jangcbd431d2015-10-06 13:07:30 -0700764 KindSectionDataList kindSectionDataList = mKindSectionDataMap.get(mimeType);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700765 if (kindSectionDataList == null) {
Walter Jangcbd431d2015-10-06 13:07:30 -0700766 kindSectionDataList = new KindSectionDataList();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700767 mKindSectionDataMap.put(mimeType, kindSectionDataList);
768 }
769 return kindSectionDataList;
770 }
771
Tingting Wang655ad1a2015-10-05 17:51:14 -0700772 private void addAccountInfo(RawContactDeltaList rawContactDeltas) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700773 if (mPrimaryRawContactDelta == null) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700774 mAccountHeaderContainer.setVisibility(View.GONE);
775 mAccountSelectorContainer.setVisibility(View.GONE);
776 return;
777 }
778
779 // Get the account information for the primary raw contact delta
780 final Pair<String,String> accountInfo = EditorUiUtils.getAccountInfo(getContext(),
781 mIsUserProfile, mPrimaryRawContactDelta.getAccountName(),
782 mPrimaryRawContactDelta.getAccountType(mAccountTypeManager));
783
784 // The account header and selector show the same information so both shouldn't be visible
785 // at the same time
786 final List<AccountWithDataSet> accounts =
787 AccountTypeManager.getInstance(getContext()).getAccounts(true);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700788 mRawContactContainer.setVisibility(View.GONE);
Walter Jang708ea9e2015-09-10 15:42:05 -0700789 if (mHasNewContact && !mIsUserProfile && accounts.size() > 1) {
790 mAccountHeaderContainer.setVisibility(View.GONE);
791 addAccountSelector(accountInfo);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700792 } else if (mHasNewContact && !mIsUserProfile) {
793 addAccountHeader(accountInfo);
794 mAccountSelectorContainer.setVisibility(View.GONE);
795 } else if (rawContactDeltas.size() > 1) {
796 mAccountHeaderContainer.setVisibility(View.GONE);
797 mAccountSelectorContainer.setVisibility(View.GONE);
798 addRawContactAccountSelector(rawContactDeltas);
Walter Jang708ea9e2015-09-10 15:42:05 -0700799 } else {
800 addAccountHeader(accountInfo);
801 mAccountSelectorContainer.setVisibility(View.GONE);
802 }
803 }
804
805 private void addAccountHeader(Pair<String,String> accountInfo) {
Walter Jang03cea2e2015-09-18 17:04:21 -0700806 if (TextUtils.isEmpty(accountInfo.first)) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700807 // Hide this view so the other text view will be centered vertically
808 mAccountHeaderName.setVisibility(View.GONE);
809 } else {
810 mAccountHeaderName.setVisibility(View.VISIBLE);
811 mAccountHeaderName.setText(accountInfo.first);
812 }
813 mAccountHeaderType.setText(accountInfo.second);
814
Tingting Wang91cee282015-10-07 13:48:17 -0700815 final AccountType primaryAccountType = mPrimaryRawContactDelta.getRawContactAccountType(
816 getContext());
817 mAccountHeaderIcon.setImageDrawable(primaryAccountType.getDisplayIcon(getContext()));
818
Walter Jang708ea9e2015-09-10 15:42:05 -0700819 mAccountHeaderContainer.setContentDescription(
820 EditorUiUtils.getAccountInfoContentDescription(
821 accountInfo.first, accountInfo.second));
822 }
823
824 private void addAccountSelector(Pair<String,String> accountInfo) {
825 mAccountSelectorContainer.setVisibility(View.VISIBLE);
826
Walter Jang03cea2e2015-09-18 17:04:21 -0700827 if (TextUtils.isEmpty(accountInfo.first)) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700828 // Hide this view so the other text view will be centered vertically
829 mAccountSelectorName.setVisibility(View.GONE);
830 } else {
831 mAccountSelectorName.setVisibility(View.VISIBLE);
832 mAccountSelectorName.setText(accountInfo.first);
833 }
834 mAccountSelectorType.setText(accountInfo.second);
835
836 mAccountSelectorContainer.setContentDescription(
837 EditorUiUtils.getAccountInfoContentDescription(
838 accountInfo.first, accountInfo.second));
839
840 mAccountSelector.setOnClickListener(new View.OnClickListener() {
841 @Override
842 public void onClick(View v) {
843 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
844 final AccountsListAdapter adapter =
845 new AccountsListAdapter(getContext(),
846 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE,
847 mPrimaryAccount);
848 popup.setWidth(mAccountSelectorContainer.getWidth());
849 popup.setAnchorView(mAccountSelectorContainer);
850 popup.setAdapter(adapter);
851 popup.setModal(true);
852 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
853 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
854 @Override
855 public void onItemClick(AdapterView<?> parent, View view, int position,
856 long id) {
857 UiClosables.closeQuietly(popup);
858 final AccountWithDataSet newAccount = adapter.getItem(position);
859 if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
860 mListener.onRebindEditorsForNewContact(
861 mPrimaryRawContactDelta,
862 mPrimaryAccount,
863 newAccount);
864 }
865 }
866 });
867 popup.show();
868 }
869 });
870 }
871
Tingting Wang655ad1a2015-10-05 17:51:14 -0700872 private void addRawContactAccountSelector(final RawContactDeltaList rawContactDeltas) {
873 mRawContactContainer.setVisibility(View.VISIBLE);
874
Tingting Wanga0b69402015-10-13 14:27:58 -0700875 Collections.sort(rawContactDeltas, new RawContactDeltaComparator(getContext()));
876
Tingting Wang655ad1a2015-10-05 17:51:14 -0700877 final String accountsSummary = getRawContactsAccountsSummary(
878 getContext(), rawContactDeltas);
879 mRawContactSummary.setText(accountsSummary);
880 mRawContactContainer.setContentDescription(accountsSummary);
881 if (mPrimaryRawContactDelta != null) {
882 final AccountType primaryAccountType = mPrimaryRawContactDelta.getRawContactAccountType(
883 getContext());
884 mPrimaryAccountIcon.setImageDrawable(primaryAccountType.getDisplayIcon(getContext()));
885 }
886
887 mRawContactContainer.setOnClickListener(new View.OnClickListener() {
888 @Override
889 public void onClick(View v) {
890 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
891 final RawContactAccountListAdapter adapter =
892 new RawContactAccountListAdapter(getContext(), rawContactDeltas);
893 popup.setWidth(mRawContactContainer.getWidth());
894 popup.setAnchorView(mRawContactContainer);
895 popup.setAdapter(adapter);
896 popup.setModal(true);
897 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
898 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
899 @Override
900 public void onItemClick(AdapterView<?> parent, View view, int position,
901 long id) {
902 UiClosables.closeQuietly(popup);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700903
904 if (mListener != null) {
Tingting Wang91cee282015-10-07 13:48:17 -0700905 final long rawContactId = adapter.getItemId(position);
906 final Uri rawContactUri = ContentUris.withAppendedId(
907 ContactsContract.RawContacts.CONTENT_URI, rawContactId);
908 final RawContactDelta rawContactDelta = adapter.getItem(position);
909 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
910 getContext());
911 final AccountType accountType = rawContactDelta.getAccountType(
912 accountTypes);
913 final boolean isReadOnly = !accountType.areContactsWritable();
914
915 mListener.onRawContactSelected(rawContactUri, rawContactId, isReadOnly);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700916 }
917 }
918 });
919 popup.show();
920 }
921 });
922 }
923
924 private static String getRawContactsAccountsSummary(
925 Context context, RawContactDeltaList rawContactDeltas) {
Tingting Wanga0b69402015-10-13 14:27:58 -0700926 final LinkedHashMap<String, Integer> accountTypeNumber = new LinkedHashMap<>();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700927 for (RawContactDelta rawContactDelta : rawContactDeltas) {
928 if (rawContactDelta.isVisible()) {
929 final AccountType accountType = rawContactDelta.getRawContactAccountType(context);
930 final String accountTypeLabel = accountType.getDisplayLabel(context).toString();
931 if (accountTypeNumber.containsKey(accountTypeLabel)) {
932 int number = accountTypeNumber.get(accountTypeLabel);
933 number++;
934 accountTypeNumber.put(accountTypeLabel, number);
935 } else {
936 accountTypeNumber.put(accountTypeLabel, 1);
937 }
938 }
939 }
940
Tingting Wanga0b69402015-10-13 14:27:58 -0700941 final LinkedHashSet<String> linkedAccounts = new LinkedHashSet<>();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700942 for (String accountTypeLabel : accountTypeNumber.keySet()) {
943 final String number = context.getResources().getQuantityString(
944 R.plurals.quickcontact_suggestion_account_type_number,
945 accountTypeNumber.get(accountTypeLabel),
946 accountTypeNumber.get(accountTypeLabel));
947 final String accountWithNumber = context.getResources().getString(
948 R.string.quickcontact_suggestion_account_type,
949 accountTypeLabel,
950 number);
951 linkedAccounts.add(accountWithNumber);
952 }
953 return TextUtils.join(",", linkedAccounts);
954 }
955
Walter Jang363d3fd2015-09-16 10:29:07 -0700956 private void addPhotoView() {
Walter Jang31a74ad2015-10-02 19:17:39 -0700957 // Get the kind section data and values delta that we will display in the photo view
Walter Jangcbd431d2015-10-06 13:07:30 -0700958 final KindSectionDataList kindSectionDataList =
959 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
960 final Pair<KindSectionData,ValuesDelta> photoToDisplay =
961 kindSectionDataList.getEntryToDisplay(mPhotoId);
962 if (photoToDisplay == null) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700963 wlog("photo: no kind section data parsed");
Walter Jangcbd431d2015-10-06 13:07:30 -0700964 mPhotoView.setVisibility(View.GONE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700965 return;
966 }
Walter Jang06f73a12015-06-17 11:15:48 -0700967
Walter Jang31a74ad2015-10-02 19:17:39 -0700968 // Set the photo view
Walter Jangcbd431d2015-10-06 13:07:30 -0700969 mPhotoView.setPhoto(photoToDisplay.second, mMaterialPalette);
Walter Jang31a74ad2015-10-02 19:17:39 -0700970
971 // Find the raw contact ID and values delta that will be written when the photo is edited
Walter Jangcbd431d2015-10-06 13:07:30 -0700972 final Pair<KindSectionData,ValuesDelta> photoToWrite = kindSectionDataList.getEntryToWrite(
973 mPrimaryAccount, mHasNewContact);
974 if (photoToWrite == null) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700975 mPhotoView.setReadOnly(true);
976 return;
977 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700978 mPhotoView.setReadOnly(false);
Walter Jangcbd431d2015-10-06 13:07:30 -0700979 mPhotoRawContactId = photoToWrite.first.getRawContactDelta().getRawContactId();
980 mPhotoValuesDelta = photoToWrite.second;
Walter Jang31a74ad2015-10-02 19:17:39 -0700981 }
982
Walter Jangf5dfea42015-09-16 12:30:36 -0700983 private void addKindSectionViews() {
984 // Sort the kinds
Walter Jangcbd431d2015-10-06 13:07:30 -0700985 final TreeSet<Map.Entry<String,KindSectionDataList>> entries =
Walter Jang192a01c2015-09-22 15:23:55 -0700986 new TreeSet<>(KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR);
Walter Jangf5dfea42015-09-16 12:30:36 -0700987 entries.addAll(mKindSectionDataMap.entrySet());
Walter Jang3efae4a2015-02-18 11:12:00 -0800988
Walter Jangf5dfea42015-09-16 12:30:36 -0700989 vlog("kind: " + entries.size() + " kindSection(s)");
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700990 int i = -1;
Walter Jangcbd431d2015-10-06 13:07:30 -0700991 for (Map.Entry<String, KindSectionDataList> entry : entries) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700992 i++;
993
Walter Jangf5dfea42015-09-16 12:30:36 -0700994 final String mimeType = entry.getKey();
Walter Jangcbd431d2015-10-06 13:07:30 -0700995 final KindSectionDataList kindSectionDataList = entry.getValue();
Walter Jangab50e6f2015-06-15 08:57:22 -0700996
Walter Jangf5dfea42015-09-16 12:30:36 -0700997 // Ignore mime types that we've already handled
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700998 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
999 vlog("kind: " + i + " " + mimeType + " dropped");
1000 continue;
1001 }
Walter Jangf5dfea42015-09-16 12:30:36 -07001002
Walter Jange5e7aef2015-10-15 14:52:42 -07001003 // Don't show more than one group editor on the compact editor.
1004 // Groups will still be editable for each raw contact individually on the full editor.
1005 if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)
1006 && kindSectionDataList.size() > 1) {
1007 vlog("kind: " + i + " " + mimeType + " dropped");
1008 continue;
1009 }
1010
Walter Jang192a01c2015-09-22 15:23:55 -07001011 if (kindSectionDataList != null && !kindSectionDataList.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -07001012 vlog("kind: " + i + " " + mimeType + ": " + kindSectionDataList.size() +
1013 " kindSectionData(s)");
1014
Walter Jangf5dfea42015-09-16 12:30:36 -07001015 final CompactKindSectionView kindSectionView = inflateKindSectionView(
1016 mKindSectionViews, kindSectionDataList, mimeType);
Walter Jangf5dfea42015-09-16 12:30:36 -07001017 mKindSectionViews.addView(kindSectionView);
Walter Jangf10ca152015-09-22 15:23:55 -07001018
1019 // Keep a pointer to all the KindSectionsViews for each mimeType
1020 getKindSectionViews(mimeType).add(kindSectionView);
Walter Jangcab3dce2015-02-09 17:48:03 -08001021 }
1022 }
1023 }
1024
Walter Jangf10ca152015-09-22 15:23:55 -07001025 private List<CompactKindSectionView> getKindSectionViews(String mimeType) {
1026 List<CompactKindSectionView> kindSectionViews = mKindSectionViewsMap.get(mimeType);
1027 if (kindSectionViews == null) {
1028 kindSectionViews = new ArrayList<>();
1029 mKindSectionViewsMap.put(mimeType, kindSectionViews);
1030 }
1031 return kindSectionViews;
1032 }
1033
Walter Jangf5dfea42015-09-16 12:30:36 -07001034 private CompactKindSectionView inflateKindSectionView(ViewGroup viewGroup,
Walter Jangcbd431d2015-10-06 13:07:30 -07001035 KindSectionDataList kindSectionDataList, String mimeType) {
Walter Jangf5dfea42015-09-16 12:30:36 -07001036 final CompactKindSectionView kindSectionView = (CompactKindSectionView)
1037 mLayoutInflater.inflate(R.layout.compact_item_kind_section, viewGroup,
1038 /* attachToRoot =*/ false);
Walter Jang45b86d52015-10-15 15:23:16 -07001039 kindSectionView.setIsUserProfile(mIsUserProfile);
Walter Jangf5dfea42015-09-16 12:30:36 -07001040
Walter Jang192a01c2015-09-22 15:23:55 -07001041 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangf5dfea42015-09-16 12:30:36 -07001042 || Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -07001043 // Phone numbers and email addresses are always displayed,
1044 // even if they are empty
Walter Jangf5dfea42015-09-16 12:30:36 -07001045 kindSectionView.setHideWhenEmpty(false);
1046 }
Walter Jangf5dfea42015-09-16 12:30:36 -07001047
Walter Jang192a01c2015-09-22 15:23:55 -07001048 // Since phone numbers and email addresses displayed even if they are empty,
1049 // they will be the only types you add new values to initially for new contacts
1050 kindSectionView.setShowOneEmptyEditor(true);
1051
1052 // Sort so the editors wind up in the order we want
1053 if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
1054 Collections.sort(kindSectionDataList, new NameEditorComparator(getContext(),
1055 mPrimaryRawContactDelta));
1056 } else {
1057 Collections.sort(kindSectionDataList, new EditorComparator(getContext()));
Walter Jang3e5ae0d2015-09-20 12:43:37 -07001058 }
1059
Walter Jang79658e12015-09-24 10:36:26 -07001060 kindSectionView.setState(kindSectionDataList, mViewIdGenerator, mListener);
Walter Jangf5dfea42015-09-16 12:30:36 -07001061
1062 return kindSectionView;
Walter Jangcab3dce2015-02-09 17:48:03 -08001063 }
1064
Walter Jang4f5594a2015-10-06 18:40:31 -07001065 private void maybeCopyPrimaryDisplayName() {
1066 if (TextUtils.isEmpty(mReadOnlyDisplayName)) return;
1067 final List<CompactKindSectionView> kindSectionViews
1068 = mKindSectionViewsMap.get(StructuredName.CONTENT_ITEM_TYPE);
1069 if (kindSectionViews.isEmpty()) return;
1070 final CompactKindSectionView primaryNameKindSectionView = kindSectionViews.get(0);
1071 if (primaryNameKindSectionView.isEmptyName()) {
1072 vlog("name: using read only display name as primary name");
1073 primaryNameKindSectionView.setName(mReadOnlyDisplayName);
1074 mPrimaryNameEditorView = primaryNameKindSectionView.getPrimaryNameEditorView();
1075 }
1076 }
1077
Walter Jangd6753152015-10-02 09:23:13 -07001078 private void showAllFields() {
Walter Jang79658e12015-09-24 10:36:26 -07001079 // Stop hiding empty editors and allow the user to enter values for all kinds now
1080 for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
1081 final CompactKindSectionView kindSectionView =
1082 (CompactKindSectionView) mKindSectionViews.getChildAt(i);
1083 kindSectionView.setHideWhenEmpty(false);
1084 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
1085 }
1086 mIsExpanded = true;
Walter Jang79658e12015-09-24 10:36:26 -07001087
Walter Jangf5dfea42015-09-16 12:30:36 -07001088 // Hide the more fields button
1089 mMoreFields.setVisibility(View.GONE);
Walter Jangcab3dce2015-02-09 17:48:03 -08001090 }
1091
Walter Jangbf63a6d2015-05-05 09:14:35 -07001092 private static void vlog(String message) {
1093 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1094 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -08001095 }
1096 }
Walter Jang363d3fd2015-09-16 10:29:07 -07001097
1098 private static void wlog(String message) {
1099 if (Log.isLoggable(TAG, Log.WARN)) {
1100 Log.w(TAG, message);
1101 }
1102 }
1103
1104 private static void elog(String message) {
1105 Log.e(TAG, message);
1106 }
Walter Jangcab3dce2015-02-09 17:48:03 -08001107}