blob: 900889edb9fa29431babf105de329fead1b8fcc4 [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
Walter Jang708774a2015-10-16 09:32:06 -070069import java.io.FileNotFoundException;
Walter Jangcab3dce2015-02-09 17:48:03 -080070import java.util.ArrayList;
Walter Jangf5dfea42015-09-16 12:30:36 -070071import java.util.Arrays;
Walter Jang192a01c2015-09-22 15:23:55 -070072import java.util.Collections;
Walter Jangf5dfea42015-09-16 12:30:36 -070073import java.util.Comparator;
Walter Jang19d985f2015-07-01 13:36:27 -070074import java.util.HashMap;
Walter Jangcab3dce2015-02-09 17:48:03 -080075import java.util.List;
Walter Jang19d985f2015-07-01 13:36:27 -070076import java.util.Map;
Walter Jangf5dfea42015-09-16 12:30:36 -070077import java.util.TreeSet;
Walter Jangcab3dce2015-02-09 17:48:03 -080078
79/**
Walter Jangf5dfea42015-09-16 12:30:36 -070080 * View to display information from multiple {@link RawContactDelta}s grouped together.
Walter Jangcab3dce2015-02-09 17:48:03 -080081 */
Walter Jangb6ca2722015-02-20 11:10:25 -080082public class CompactRawContactsEditorView extends LinearLayout implements View.OnClickListener {
Walter Jangcab3dce2015-02-09 17:48:03 -080083
Walter Jang4f5594a2015-10-06 18:40:31 -070084 static final String TAG = "CompactEditorView";
Walter Jangcab3dce2015-02-09 17:48:03 -080085
Walter Jang192a01c2015-09-22 15:23:55 -070086 private static final KindSectionDataMapEntryComparator
87 KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR = new KindSectionDataMapEntryComparator();
Walter Jangf5dfea42015-09-16 12:30:36 -070088
Walter Jangb6ca2722015-02-20 11:10:25 -080089 /**
90 * Callbacks for hosts of {@link CompactRawContactsEditorView}s.
91 */
92 public interface Listener {
93
94 /**
Walter Jang151f3e62015-02-26 15:29:40 -080095 * Invoked when the structured name editor field has changed.
96 *
97 * @param rawContactId The raw contact ID from the underlying {@link RawContactDelta}.
98 * @param valuesDelta The values from the underlying {@link RawContactDelta}.
99 */
100 public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta);
Walter Jang708ea9e2015-09-10 15:42:05 -0700101
102 /**
103 * Invoked when the compact editor should rebind editors for a new account.
104 *
105 * @param oldState Old data being edited.
106 * @param oldAccount Old account associated with oldState.
107 * @param newAccount New account to be used.
108 */
109 public void onRebindEditorsForNewContact(RawContactDelta oldState,
110 AccountWithDataSet oldAccount, AccountWithDataSet newAccount);
Walter Jang79658e12015-09-24 10:36:26 -0700111
112 /**
113 * Invoked when no editors could be bound for the contact.
114 */
115 public void onBindEditorsFailed();
Walter Jangd6753152015-10-02 09:23:13 -0700116
117 /**
118 * Invoked after editors have been bound for the contact.
119 */
120 public void onEditorsBound();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700121
122 /**
Tingting Wang5585c6e2015-10-14 11:19:58 -0700123 * Invoked when a rawcontact from linked contacts is selected in editor.
Tingting Wang655ad1a2015-10-05 17:51:14 -0700124 */
Tingting Wang91cee282015-10-07 13:48:17 -0700125 public void onRawContactSelected(Uri uri, long rawContactId, boolean isReadOnly);
Walter Jang3f18d612015-10-07 16:01:05 -0700126
127 /**
128 * Returns the map of raw contact IDs to newly taken or selected photos that have not
129 * yet been saved to CP2.
130 */
131 public Bundle getUpdatedPhotos();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700132 }
133
134 /**
135 * Used to list the account info for the given raw contacts list.
136 */
137 private static final class RawContactAccountListAdapter extends BaseAdapter {
138 private final LayoutInflater mInflater;
139 private final Context mContext;
140 private final RawContactDeltaList mRawContactDeltas;
141
142 public RawContactAccountListAdapter(Context context, RawContactDeltaList rawContactDeltas) {
143 mContext = context;
144 mRawContactDeltas = new RawContactDeltaList();
145 for (RawContactDelta rawContactDelta : rawContactDeltas) {
146 if (rawContactDelta.isVisible()) {
147 mRawContactDeltas.add(rawContactDelta);
148 }
149 }
150 mInflater = LayoutInflater.from(context);
151 }
152
153 @Override
154 public View getView(int position, View convertView, ViewGroup parent) {
155 final View resultView = convertView != null ? convertView
156 : mInflater.inflate(R.layout.account_selector_list_item, parent, false);
157
158 final RawContactDelta rawContactDelta = mRawContactDeltas.get(position);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700159
160 final TextView text1 = (TextView) resultView.findViewById(android.R.id.text1);
Walter Jang9488a762015-10-16 13:42:48 -0700161 final AccountType accountType = rawContactDelta.getRawContactAccountType(mContext);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700162 text1.setText(accountType.getDisplayLabel(mContext));
163
Tingting Wang655ad1a2015-10-05 17:51:14 -0700164 final TextView text2 = (TextView) resultView.findViewById(android.R.id.text2);
Walter Jang9488a762015-10-16 13:42:48 -0700165 final String accountName = rawContactDelta.getAccountName();
Tingting Wanga0b69402015-10-13 14:27:58 -0700166 if (TextUtils.isEmpty(accountName)) {
167 text2.setVisibility(View.GONE);
168 } else {
Walter Jang9488a762015-10-16 13:42:48 -0700169 // Truncate email addresses in the middle so we don't lose the domain
Tingting Wanga0b69402015-10-13 14:27:58 -0700170 text2.setText(accountName);
171 text2.setEllipsize(TextUtils.TruncateAt.MIDDLE);
172 }
Tingting Wang655ad1a2015-10-05 17:51:14 -0700173
174 final ImageView icon = (ImageView) resultView.findViewById(android.R.id.icon);
175 icon.setImageDrawable(accountType.getDisplayIcon(mContext));
176
177 return resultView;
178 }
179
180 @Override
181 public int getCount() {
182 return mRawContactDeltas.size();
183 }
184
185 @Override
186 public RawContactDelta getItem(int position) {
187 return mRawContactDeltas.get(position);
188 }
189
190 @Override
191 public long getItemId(int position) {
192 return getItem(position).getRawContactId();
193 }
Walter Jang151f3e62015-02-26 15:29:40 -0800194 }
195
Walter Jang192a01c2015-09-22 15:23:55 -0700196 /** Used to sort entire kind sections. */
197 private static final class KindSectionDataMapEntryComparator implements
Walter Jangcbd431d2015-10-06 13:07:30 -0700198 Comparator<Map.Entry<String,KindSectionDataList>> {
Walter Jangf5dfea42015-09-16 12:30:36 -0700199
Walter Jang192a01c2015-09-22 15:23:55 -0700200 final MimeTypeComparator mMimeTypeComparator = new MimeTypeComparator();
201
202 @Override
Walter Jangcbd431d2015-10-06 13:07:30 -0700203 public int compare(Map.Entry<String, KindSectionDataList> entry1,
204 Map.Entry<String, KindSectionDataList> entry2) {
Walter Jang192a01c2015-09-22 15:23:55 -0700205 if (entry1 == entry2) return 0;
206 if (entry1 == null) return -1;
207 if (entry2 == null) return 1;
208
209 final String mimeType1 = entry1.getKey();
210 final String mimeType2 = entry2.getKey();
211
212 return mMimeTypeComparator.compare(mimeType1, mimeType2);
213 }
214 }
215
216 /**
217 * Sorts kinds roughly the same as quick contacts; we diverge in the following ways:
218 * <ol>
219 * <li>All names are together at the top.</li>
220 * <li>IM is moved up after addresses</li>
221 * <li>SIP addresses are moved to below phone numbers</li>
Walter Jang79658e12015-09-24 10:36:26 -0700222 * <li>Group membership is placed at the end</li>
Walter Jang192a01c2015-09-22 15:23:55 -0700223 * </ol>
224 */
225 private static final class MimeTypeComparator implements Comparator<String> {
226
Walter Jangf5dfea42015-09-16 12:30:36 -0700227 private static final List<String> MIME_TYPE_ORDER = Arrays.asList(new String[] {
228 StructuredName.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700229 Nickname.CONTENT_ITEM_TYPE,
Walter Jang2ae21752015-09-25 11:25:38 -0700230 Organization.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700231 Phone.CONTENT_ITEM_TYPE,
232 SipAddress.CONTENT_ITEM_TYPE,
233 Email.CONTENT_ITEM_TYPE,
234 StructuredPostal.CONTENT_ITEM_TYPE,
235 Im.CONTENT_ITEM_TYPE,
236 Website.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700237 Event.CONTENT_ITEM_TYPE,
238 Relation.CONTENT_ITEM_TYPE,
Walter Jangf10ca152015-09-22 15:23:55 -0700239 Note.CONTENT_ITEM_TYPE,
240 GroupMembership.CONTENT_ITEM_TYPE
Walter Jangf5dfea42015-09-16 12:30:36 -0700241 });
242
243 @Override
Walter Jang192a01c2015-09-22 15:23:55 -0700244 public int compare(String mimeType1, String mimeType2) {
245 if (mimeType1 == mimeType2) return 0;
246 if (mimeType1 == null) return -1;
247 if (mimeType2 == null) return 1;
Walter Jangf5dfea42015-09-16 12:30:36 -0700248
249 int index1 = MIME_TYPE_ORDER.indexOf(mimeType1);
250 int index2 = MIME_TYPE_ORDER.indexOf(mimeType2);
251
252 // Fallback to alphabetical ordering of the mime type if both are not found
253 if (index1 < 0 && index2 < 0) return mimeType1.compareTo(mimeType2);
254 if (index1 < 0) return 1;
255 if (index2 < 0) return -1;
256
257 return index1 < index2 ? -1 : 1;
258 }
259 }
260
Walter Jang192a01c2015-09-22 15:23:55 -0700261 /**
262 * Sorts primary accounts and google account types before others.
263 */
264 private static final class EditorComparator implements Comparator<KindSectionData> {
265
266 private RawContactDeltaComparator mRawContactDeltaComparator;
267
268 private EditorComparator(Context context) {
269 mRawContactDeltaComparator = new RawContactDeltaComparator(context);
270 }
271
272 @Override
273 public int compare(KindSectionData kindSectionData1, KindSectionData kindSectionData2) {
274 if (kindSectionData1 == kindSectionData2) return 0;
275 if (kindSectionData1 == null) return -1;
276 if (kindSectionData2 == null) return 1;
277
278 final RawContactDelta rawContactDelta1 = kindSectionData1.getRawContactDelta();
279 final RawContactDelta rawContactDelta2 = kindSectionData2.getRawContactDelta();
280
281 if (rawContactDelta1 == rawContactDelta2) return 0;
282 if (rawContactDelta1 == null) return -1;
283 if (rawContactDelta2 == null) return 1;
284
285 return mRawContactDeltaComparator.compare(rawContactDelta1, rawContactDelta2);
286 }
287 }
288
289 /**
290 * Sorts primary account names first, followed by google account types, and other account
291 * types last. For names from the same account we order structured names before nicknames,
292 * but still keep names from the same account together.
293 */
294 private static final class NameEditorComparator implements Comparator<KindSectionData> {
295
Walter Jang4f5594a2015-10-06 18:40:31 -0700296 private final RawContactDeltaComparator mRawContactDeltaComparator;
297 private final MimeTypeComparator mMimeTypeComparator;
298 private final RawContactDelta mPrimaryRawContactDelta;
Walter Jang192a01c2015-09-22 15:23:55 -0700299
300 private NameEditorComparator(Context context, RawContactDelta primaryRawContactDelta) {
301 mRawContactDeltaComparator = new RawContactDeltaComparator(context);
302 mMimeTypeComparator = new MimeTypeComparator();
303 mPrimaryRawContactDelta = primaryRawContactDelta;
304 }
305
306 @Override
307 public int compare(KindSectionData kindSectionData1, KindSectionData kindSectionData2) {
308 if (kindSectionData1 == kindSectionData2) return 0;
309 if (kindSectionData1 == null) return -1;
310 if (kindSectionData2 == null) return 1;
311
312 final RawContactDelta rawContactDelta1 = kindSectionData1.getRawContactDelta();
313 final RawContactDelta rawContactDelta2 = kindSectionData2.getRawContactDelta();
314
315 if (rawContactDelta1 == rawContactDelta2) return 0;
316 if (rawContactDelta1 == null) return -1;
317 if (rawContactDelta2 == null) return 1;
318
319 final boolean isRawContactDelta1Primary =
320 mPrimaryRawContactDelta.equals(rawContactDelta1);
321 final boolean isRawContactDelta2Primary =
322 mPrimaryRawContactDelta.equals(rawContactDelta2);
323
324 // If both names are from the primary account, sort my by mime type
325 if (isRawContactDelta1Primary && isRawContactDelta2Primary) {
326 final String mimeType1 = kindSectionData1.getDataKind().mimeType;
327 final String mimeType2 = kindSectionData2.getDataKind().mimeType;
328 return mMimeTypeComparator.compare(mimeType1, mimeType2);
329 }
330
331 // The primary account name should be before all others
Walter Jang79658e12015-09-24 10:36:26 -0700332 if (isRawContactDelta1Primary) return -1;
333 if (isRawContactDelta2Primary) return 1;
Walter Jang192a01c2015-09-22 15:23:55 -0700334
335 return mRawContactDeltaComparator.compare(rawContactDelta1, rawContactDelta2);
336 }
337 }
338
Walter Jang79658e12015-09-24 10:36:26 -0700339 public static class SavedState extends BaseSavedState {
340
341 public static final Parcelable.Creator<SavedState> CREATOR =
342 new Parcelable.Creator<SavedState>() {
343 public SavedState createFromParcel(Parcel in) {
344 return new SavedState(in);
345 }
346 public SavedState[] newArray(int size) {
347 return new SavedState[size];
348 }
349 };
350
351 private boolean mIsExpanded;
352
353 public SavedState(Parcelable superState) {
354 super(superState);
355 }
356
357 private SavedState(Parcel in) {
358 super(in);
359 mIsExpanded = in.readInt() != 0;
360 }
361
362 @Override
363 public void writeToParcel(Parcel out, int flags) {
364 super.writeToParcel(out, flags);
365 out.writeInt(mIsExpanded ? 1 : 0);
366 }
367 }
368
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700369 private CompactRawContactsEditorView.Listener mListener;
Walter Jangb6ca2722015-02-20 11:10:25 -0800370
Walter Jangcab3dce2015-02-09 17:48:03 -0800371 private AccountTypeManager mAccountTypeManager;
372 private LayoutInflater mLayoutInflater;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800373
Walter Jangcab3dce2015-02-09 17:48:03 -0800374 private ViewIdGenerator mViewIdGenerator;
Walter Jangf46abd82015-02-20 16:52:04 -0800375 private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
Walter Jang708ea9e2015-09-10 15:42:05 -0700376 private long mPhotoId;
Walter Jang4f5594a2015-10-06 18:40:31 -0700377 private String mReadOnlyDisplayName;
Walter Jang708ea9e2015-09-10 15:42:05 -0700378 private boolean mHasNewContact;
379 private boolean mIsUserProfile;
380 private AccountWithDataSet mPrimaryAccount;
381 private RawContactDelta mPrimaryRawContactDelta;
Walter Jangcbd431d2015-10-06 13:07:30 -0700382 private Map<String,KindSectionDataList> mKindSectionDataMap = new HashMap<>();
Walter Jangcab3dce2015-02-09 17:48:03 -0800383
Walter Jang708ea9e2015-09-10 15:42:05 -0700384 // Account header
385 private View mAccountHeaderContainer;
386 private TextView mAccountHeaderType;
387 private TextView mAccountHeaderName;
Tingting Wang91cee282015-10-07 13:48:17 -0700388 private ImageView mAccountHeaderIcon;
Walter Jang708ea9e2015-09-10 15:42:05 -0700389
390 // Account selector
391 private View mAccountSelectorContainer;
392 private View mAccountSelector;
393 private TextView mAccountSelectorType;
394 private TextView mAccountSelectorName;
395
Tingting Wang655ad1a2015-10-05 17:51:14 -0700396 // Raw contacts selector
397 private View mRawContactContainer;
398 private TextView mRawContactSummary;
Tingting Wang655ad1a2015-10-05 17:51:14 -0700399
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700400 private CompactPhotoEditorView mPhotoView;
Walter Jangf5dfea42015-09-16 12:30:36 -0700401 private ViewGroup mKindSectionViews;
Walter Jangf10ca152015-09-22 15:23:55 -0700402 private Map<String,List<CompactKindSectionView>> mKindSectionViewsMap = new HashMap<>();
Walter Jangb6ca2722015-02-20 11:10:25 -0800403 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -0800404
Walter Jang79658e12015-09-24 10:36:26 -0700405 private boolean mIsExpanded;
Walter Jangcbd431d2015-10-06 13:07:30 -0700406
Walter Jang3efae4a2015-02-18 11:12:00 -0800407 private long mPhotoRawContactId;
Walter Jang31a74ad2015-10-02 19:17:39 -0700408 private ValuesDelta mPhotoValuesDelta;
Walter Jang4f5594a2015-10-06 18:40:31 -0700409 private StructuredNameEditorView mPrimaryNameEditorView;
Walter Jang3efae4a2015-02-18 11:12:00 -0800410
Walter Jangcab3dce2015-02-09 17:48:03 -0800411 public CompactRawContactsEditorView(Context context) {
412 super(context);
413 }
414
415 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
416 super(context, attrs);
417 }
418
Walter Jangb6ca2722015-02-20 11:10:25 -0800419 /**
420 * Sets the receiver for {@link CompactRawContactsEditorView} callbacks.
421 */
422 public void setListener(Listener listener) {
423 mListener = listener;
424 }
425
Walter Jangcab3dce2015-02-09 17:48:03 -0800426 @Override
427 protected void onFinishInflate() {
428 super.onFinishInflate();
429
430 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
431 mLayoutInflater = (LayoutInflater)
432 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
433
Walter Jang708ea9e2015-09-10 15:42:05 -0700434 // Account header
435 mAccountHeaderContainer = findViewById(R.id.account_container);
436 mAccountHeaderType = (TextView) findViewById(R.id.account_type);
437 mAccountHeaderName = (TextView) findViewById(R.id.account_name);
Tingting Wang91cee282015-10-07 13:48:17 -0700438 mAccountHeaderIcon = (ImageView) findViewById(R.id.account_type_icon);
Walter Jang708ea9e2015-09-10 15:42:05 -0700439
440 // Account selector
441 mAccountSelectorContainer = findViewById(R.id.account_selector_container);
442 mAccountSelector = findViewById(R.id.account);
443 mAccountSelectorType = (TextView) findViewById(R.id.account_type_selector);
444 mAccountSelectorName = (TextView) findViewById(R.id.account_name_selector);
445
Tingting Wang655ad1a2015-10-05 17:51:14 -0700446 // Raw contacts selector
447 mRawContactContainer = findViewById(R.id.all_rawcontacts_accounts_container);
448 mRawContactSummary = (TextView) findViewById(R.id.rawcontacts_accounts_summary);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700449
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700450 mPhotoView = (CompactPhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangf5dfea42015-09-16 12:30:36 -0700451 mKindSectionViews = (LinearLayout) findViewById(R.id.kind_section_views);
Walter Jangb6ca2722015-02-20 11:10:25 -0800452 mMoreFields = findViewById(R.id.more_fields);
453 mMoreFields.setOnClickListener(this);
454 }
455
Walter Jangb6ca2722015-02-20 11:10:25 -0800456 @Override
457 public void onClick(View view) {
Walter Jangf5dfea42015-09-16 12:30:36 -0700458 if (view.getId() == R.id.more_fields) {
Walter Jangd6753152015-10-02 09:23:13 -0700459 showAllFields();
Walter Jangb6ca2722015-02-20 11:10:25 -0800460 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800461 }
462
463 @Override
464 public void setEnabled(boolean enabled) {
465 super.setEnabled(enabled);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700466 final int childCount = mKindSectionViews.getChildCount();
467 for (int i = 0; i < childCount; i++) {
468 mKindSectionViews.getChildAt(i).setEnabled(enabled);
Walter Jangcab3dce2015-02-09 17:48:03 -0800469 }
470 }
471
Walter Jang79658e12015-09-24 10:36:26 -0700472 @Override
473 public Parcelable onSaveInstanceState() {
474 final Parcelable superState = super.onSaveInstanceState();
475 final SavedState savedState = new SavedState(superState);
476 savedState.mIsExpanded = mIsExpanded;
477 return savedState;
478 }
479
480 @Override
481 public void onRestoreInstanceState(Parcelable state) {
482 if(!(state instanceof SavedState)) {
483 super.onRestoreInstanceState(state);
484 return;
485 }
486 final SavedState savedState = (SavedState) state;
487 super.onRestoreInstanceState(savedState.getSuperState());
488 mIsExpanded = savedState.mIsExpanded;
Walter Jangd6753152015-10-02 09:23:13 -0700489 if (mIsExpanded) {
490 showAllFields();
Walter Jang79658e12015-09-24 10:36:26 -0700491 }
492 }
493
Walter Jang3efae4a2015-02-18 11:12:00 -0800494 /**
Walter Jang31a74ad2015-10-02 19:17:39 -0700495 * Pass through to {@link CompactPhotoEditorView#setListener}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800496 */
Walter Jang31a74ad2015-10-02 19:17:39 -0700497 public void setPhotoListener(CompactPhotoEditorView.Listener listener) {
498 mPhotoView.setListener(listener);
Walter Jang3efae4a2015-02-18 11:12:00 -0800499 }
500
Walter Jang31a74ad2015-10-02 19:17:39 -0700501 public void removePhoto() {
Walter Jang45b86d52015-10-15 15:23:16 -0700502 mPhotoValuesDelta.setFromTemplate(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700503 mPhotoValuesDelta.put(Photo.PHOTO, (byte[]) null);
504
505 mPhotoView.removePhoto();
Walter Jang3efae4a2015-02-18 11:12:00 -0800506 }
507
508 /**
Walter Jang41b3ea12015-03-09 17:30:06 -0700509 * Pass through to {@link CompactPhotoEditorView#setFullSizedPhoto(Uri)}.
510 */
511 public void setFullSizePhoto(Uri photoUri) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700512 mPhotoView.setFullSizedPhoto(photoUri);
Walter Jang41b3ea12015-03-09 17:30:06 -0700513 }
514
Walter Jang31a74ad2015-10-02 19:17:39 -0700515 public void updatePhoto(Uri photoUri) {
Walter Jang45b86d52015-10-15 15:23:16 -0700516 mPhotoValuesDelta.setFromTemplate(false);
Walter Jang3f18d612015-10-07 16:01:05 -0700517
Walter Jang45b86d52015-10-15 15:23:16 -0700518 if (!mIsUserProfile) {
519 // Unset primary for all photos
520 unsetSuperPrimary();
521
522 // Mark the currently displayed photo as primary
523 mPhotoValuesDelta.setSuperPrimary(true);
524 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700525
Wenyi Wang4c9cf3a2015-10-15 17:58:23 -0700526 // Even though high-res photos cannot be saved by passing them via
527 // an EntityDeltaList (since they cause the Bundle size limit to be
528 // exceeded), we still pass a low-res thumbnail. This simplifies
529 // code all over the place, because we don't have to test whether
530 // there is a change in EITHER the delta-list OR a changed photo...
531 // this way, there is always a change in the delta-list.
532 try {
533 final byte[] bytes = EditorUiUtils.getCompressedThumbnailBitmapBytes(
534 getContext(), photoUri);
535 if (bytes != null) {
536 mPhotoValuesDelta.setPhoto(bytes);
537 }
538 } catch (FileNotFoundException e) {
539 elog("Failed to get bitmap from photo Uri");
540 }
541
Walter Jang31a74ad2015-10-02 19:17:39 -0700542 mPhotoView.setFullSizedPhoto(photoUri);
543 }
544
Walter Jang3f18d612015-10-07 16:01:05 -0700545 private void unsetSuperPrimary() {
546 final List<KindSectionData> kindSectionDataList =
547 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
548 for (KindSectionData kindSectionData : kindSectionDataList) {
549 final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
550 for (ValuesDelta valuesDelta : valuesDeltaList) {
551 valuesDelta.setSuperPrimary(false);
552 }
553 }
554 }
555
Walter Jang41b3ea12015-03-09 17:30:06 -0700556 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800557 * Pass through to {@link CompactPhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800558 */
559 public boolean isWritablePhotoSet() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700560 return mPhotoView.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800561 }
562
563 /**
Walter Jang3efae4a2015-02-18 11:12:00 -0800564 * Get the raw contact ID for the CompactHeaderView photo.
565 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800566 public long getPhotoRawContactId() {
567 return mPhotoRawContactId;
568 }
569
Walter Jang4f5594a2015-10-06 18:40:31 -0700570 public StructuredNameEditorView getPrimaryNameEditorView() {
571 return mPrimaryNameEditorView;
572 }
573
Walter Jang31a74ad2015-10-02 19:17:39 -0700574 /**
575 * Returns a data holder for every non-default/non-empty photo from each raw contact, whether
576 * the raw contact is writable or not.
577 */
578 public ArrayList<CompactPhotoSelectionFragment.Photo> getPhotos() {
579 final ArrayList<CompactPhotoSelectionFragment.Photo> photos = new ArrayList<>();
580
Walter Jang3f18d612015-10-07 16:01:05 -0700581 final Bundle updatedPhotos = mListener == null ? null : mListener.getUpdatedPhotos();
582
Walter Jang31a74ad2015-10-02 19:17:39 -0700583 final List<KindSectionData> kindSectionDataList =
584 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
585 for (int i = 0; i < kindSectionDataList.size(); i++) {
586 final KindSectionData kindSectionData = kindSectionDataList.get(i);
587 final AccountType accountType = kindSectionData.getAccountType();
588 final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
589 if (valuesDeltaList == null || valuesDeltaList.isEmpty()) continue;
590 for (int j = 0; j < valuesDeltaList.size(); j++) {
591 final ValuesDelta valuesDelta = valuesDeltaList.get(j);
592 final Bitmap bitmap = EditorUiUtils.getPhotoBitmap(valuesDelta);
593 if (bitmap == null) continue;
594
595 final CompactPhotoSelectionFragment.Photo photo =
596 new CompactPhotoSelectionFragment.Photo();
597 photo.titleRes = accountType.titleRes;
598 photo.iconRes = accountType.iconRes;
599 photo.syncAdapterPackageName = accountType.syncAdapterPackageName;
600 photo.valuesDelta = valuesDelta;
601 photo.primary = valuesDelta.isSuperPrimary();
602 photo.kindSectionDataListIndex = i;
603 photo.valuesDeltaListIndex = j;
Walter Jang3f18d612015-10-07 16:01:05 -0700604
605 if (updatedPhotos != null) {
606 photo.updatedPhotoUri = (Uri) updatedPhotos.get(String.valueOf(
607 kindSectionData.getRawContactDelta().getRawContactId()));
608 }
609
Walter Jang31a74ad2015-10-02 19:17:39 -0700610 photos.add(photo);
611 }
612 }
613
614 return photos;
615 }
616
617 /**
618 * Marks the raw contact photo given as primary for the aggregate contact and updates the
619 * UI.
620 */
621 public void setPrimaryPhoto(CompactPhotoSelectionFragment.Photo photo) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700622 // Find the values delta to mark as primary
Walter Jang3f18d612015-10-07 16:01:05 -0700623 final KindSectionDataList kindSectionDataList =
624 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
Walter Jang31a74ad2015-10-02 19:17:39 -0700625 if (photo.kindSectionDataListIndex < 0
626 || photo.kindSectionDataListIndex >= kindSectionDataList.size()) {
627 wlog("Invalid kind section data list index");
628 return;
629 }
630 final KindSectionData kindSectionData =
631 kindSectionDataList.get(photo.kindSectionDataListIndex);
632 final List<ValuesDelta> valuesDeltaList = kindSectionData.getValuesDeltas();
633 if (photo.valuesDeltaListIndex >= valuesDeltaList.size()) {
634 wlog("Invalid values delta list index");
635 return;
636 }
637 final ValuesDelta valuesDelta = valuesDeltaList.get(photo.valuesDeltaListIndex);
638 valuesDelta.setFromTemplate(false);
Walter Jang45b86d52015-10-15 15:23:16 -0700639
640 if (!mIsUserProfile) {
641 // Unset primary for all other photos
642 unsetSuperPrimary();
643
644 valuesDelta.setSuperPrimary(true);
645 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700646
647 // Update the UI
648 mPhotoView.setPhoto(valuesDelta, mMaterialPalette);
649 }
650
Walter Jangd35e5ef2015-02-24 09:18:16 -0800651 public View getAggregationAnchorView() {
Walter Jangf10ca152015-09-22 15:23:55 -0700652 final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
Walter Jang192a01c2015-09-22 15:23:55 -0700653 StructuredName.CONTENT_ITEM_TYPE);
Walter Jangf10ca152015-09-22 15:23:55 -0700654 if (!kindSectionViews.isEmpty()) {
Walter Jang192a01c2015-09-22 15:23:55 -0700655 return mKindSectionViews.getChildAt(0).findViewById(R.id.anchor_view);
Walter Jangd35e5ef2015-02-24 09:18:16 -0800656 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700657 return null;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800658 }
659
Walter Jangf10ca152015-09-22 15:23:55 -0700660 public void setGroupMetaData(Cursor groupMetaData) {
661 final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
662 GroupMembership.CONTENT_ITEM_TYPE);
663 for (CompactKindSectionView kindSectionView : kindSectionViews) {
664 kindSectionView.setGroupMetaData(groupMetaData);
Walter Jangd6753152015-10-02 09:23:13 -0700665 if (mIsExpanded) {
666 kindSectionView.setHideWhenEmpty(false);
667 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
668 }
Walter Jangf10ca152015-09-22 15:23:55 -0700669 }
670 }
671
Walter Jangf46abd82015-02-20 16:52:04 -0800672 public void setState(RawContactDeltaList rawContactDeltas,
Walter Jang06f73a12015-06-17 11:15:48 -0700673 MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
Walter Jang4f5594a2015-10-06 18:40:31 -0700674 long photoId, String readOnlyDisplayName, boolean hasNewContact,
675 boolean isUserProfile, AccountWithDataSet primaryAccount) {
Walter Jang363d3fd2015-09-16 10:29:07 -0700676 mKindSectionDataMap.clear();
Walter Jangf5dfea42015-09-16 12:30:36 -0700677 mKindSectionViews.removeAllViews();
Walter Jang363d3fd2015-09-16 10:29:07 -0700678 mMoreFields.setVisibility(View.VISIBLE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800679
Walter Jangf46abd82015-02-20 16:52:04 -0800680 mMaterialPalette = materialPalette;
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700681 mViewIdGenerator = viewIdGenerator;
Walter Jang708ea9e2015-09-10 15:42:05 -0700682 mPhotoId = photoId;
Walter Jang4f5594a2015-10-06 18:40:31 -0700683 mReadOnlyDisplayName = readOnlyDisplayName;
Tingting Wangf695eb32015-10-15 18:45:15 -0700684
Walter Jang708ea9e2015-09-10 15:42:05 -0700685 mHasNewContact = hasNewContact;
686 mIsUserProfile = isUserProfile;
687 mPrimaryAccount = primaryAccount;
688 if (mPrimaryAccount == null) {
689 mPrimaryAccount = ContactEditorUtils.getInstance(getContext()).getDefaultAccount();
690 }
691 vlog("state: primary " + mPrimaryAccount);
Walter Jangcab3dce2015-02-09 17:48:03 -0800692
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700693 // Parse the given raw contact deltas
694 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
695 elog("No raw contact deltas");
Walter Jang79658e12015-09-24 10:36:26 -0700696 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jangf5dfea42015-09-16 12:30:36 -0700697 return;
698 }
Walter Jangcbd431d2015-10-06 13:07:30 -0700699 parseRawContactDeltas(rawContactDeltas);
Walter Jang79658e12015-09-24 10:36:26 -0700700 if (mKindSectionDataMap.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700701 elog("No kind section data parsed from RawContactDelta(s)");
Walter Jang79658e12015-09-24 10:36:26 -0700702 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700703 return;
704 }
Walter Jangcbd431d2015-10-06 13:07:30 -0700705 mPrimaryRawContactDelta = mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE)
706 .getEntryToWrite(mPrimaryAccount, mHasNewContact).first.getRawContactDelta();
707 if (mPrimaryRawContactDelta != null) {
708 RawContactModifier.ensureKindExists(mPrimaryRawContactDelta,
709 mPrimaryRawContactDelta.getAccountType(mAccountTypeManager),
710 StructuredName.CONTENT_ITEM_TYPE);
711 RawContactModifier.ensureKindExists(mPrimaryRawContactDelta,
712 mPrimaryRawContactDelta.getAccountType(mAccountTypeManager),
713 Photo.CONTENT_ITEM_TYPE);
714 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700715
716 // Setup the view
Tingting Wang655ad1a2015-10-05 17:51:14 -0700717 addAccountInfo(rawContactDeltas);
Walter Jang363d3fd2015-09-16 10:29:07 -0700718 addPhotoView();
Walter Jangf5dfea42015-09-16 12:30:36 -0700719 addKindSectionViews();
Walter Jang4f5594a2015-10-06 18:40:31 -0700720 if (mHasNewContact) {
721 maybeCopyPrimaryDisplayName();
Walter Jangd6753152015-10-02 09:23:13 -0700722 }
Walter Jang4f5594a2015-10-06 18:40:31 -0700723 if (mIsExpanded) showAllFields();
Walter Jangd6753152015-10-02 09:23:13 -0700724
725 if (mListener != null) mListener.onEditorsBound();
Walter Jangcab3dce2015-02-09 17:48:03 -0800726 }
727
Walter Jangcbd431d2015-10-06 13:07:30 -0700728 private void parseRawContactDeltas(RawContactDeltaList rawContactDeltas) {
Walter Jang363d3fd2015-09-16 10:29:07 -0700729 // Build the kind section data list map
Walter Jang192a01c2015-09-22 15:23:55 -0700730 vlog("parse: " + rawContactDeltas.size() + " rawContactDelta(s)");
731 for (int j = 0; j < rawContactDeltas.size(); j++) {
732 final RawContactDelta rawContactDelta = rawContactDeltas.get(j);
733 vlog("parse: " + j + " rawContactDelta" + rawContactDelta);
Walter Jang363d3fd2015-09-16 10:29:07 -0700734 if (rawContactDelta == null || !rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800735 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jang363d3fd2015-09-16 10:29:07 -0700736 if (accountType == null) continue;
737 final List<DataKind> dataKinds = accountType.getSortedDataKinds();
738 final int dataKindSize = dataKinds == null ? 0 : dataKinds.size();
739 vlog("parse: " + dataKindSize + " dataKinds(s)");
740 for (int i = 0; i < dataKindSize; i++) {
741 final DataKind dataKind = dataKinds.get(i);
Walter Jang192a01c2015-09-22 15:23:55 -0700742 if (dataKind == null || !dataKind.editable) {
743 vlog("parse: " + i + " " + dataKind.mimeType + " dropped read-only");
744 continue;
745 }
746 final String mimeType = dataKind.mimeType;
747
748 // Skip psuedo mime types
749 if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
750 || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
751 vlog("parse: " + i + " " + dataKind.mimeType + " dropped pseudo type");
752 continue;
753 }
754
Walter Jang3f18d612015-10-07 16:01:05 -0700755 final KindSectionDataList kindSectionDataList =
Walter Jangcbd431d2015-10-06 13:07:30 -0700756 getOrCreateKindSectionDataList(mimeType);
Walter Jang363d3fd2015-09-16 10:29:07 -0700757 final KindSectionData kindSectionData =
758 new KindSectionData(accountType, dataKind, rawContactDelta);
759 kindSectionDataList.add(kindSectionData);
Walter Jang192a01c2015-09-22 15:23:55 -0700760
Walter Jang363d3fd2015-09-16 10:29:07 -0700761 vlog("parse: " + i + " " + dataKind.mimeType + " " +
Walter Jang192a01c2015-09-22 15:23:55 -0700762 kindSectionData.getValuesDeltas().size() + " value(s)");
Walter Jangac679af2015-06-01 12:17:06 -0700763 }
764 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800765 }
766
Walter Jang3f18d612015-10-07 16:01:05 -0700767 private KindSectionDataList getOrCreateKindSectionDataList(String mimeType) {
Walter Jangcbd431d2015-10-06 13:07:30 -0700768 KindSectionDataList kindSectionDataList = mKindSectionDataMap.get(mimeType);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700769 if (kindSectionDataList == null) {
Walter Jangcbd431d2015-10-06 13:07:30 -0700770 kindSectionDataList = new KindSectionDataList();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700771 mKindSectionDataMap.put(mimeType, kindSectionDataList);
772 }
773 return kindSectionDataList;
774 }
775
Tingting Wang655ad1a2015-10-05 17:51:14 -0700776 private void addAccountInfo(RawContactDeltaList rawContactDeltas) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700777 if (mPrimaryRawContactDelta == null) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700778 mAccountHeaderContainer.setVisibility(View.GONE);
779 mAccountSelectorContainer.setVisibility(View.GONE);
780 return;
781 }
782
783 // Get the account information for the primary raw contact delta
Walter Jangbe5e1b12015-10-17 11:38:29 -0700784 final Pair<String,String> accountInfo = mIsUserProfile
785 ? EditorUiUtils.getLocalAccountInfo(getContext(),
786 mPrimaryRawContactDelta.getAccountName(),
787 mPrimaryRawContactDelta.getAccountType(mAccountTypeManager))
788 : EditorUiUtils.getAccountInfo(getContext(),
789 mPrimaryRawContactDelta.getAccountName(),
790 mPrimaryRawContactDelta.getAccountType(mAccountTypeManager));
Walter Jang708ea9e2015-09-10 15:42:05 -0700791
792 // The account header and selector show the same information so both shouldn't be visible
793 // at the same time
794 final List<AccountWithDataSet> accounts =
795 AccountTypeManager.getInstance(getContext()).getAccounts(true);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700796 mRawContactContainer.setVisibility(View.GONE);
Walter Jang708ea9e2015-09-10 15:42:05 -0700797 if (mHasNewContact && !mIsUserProfile && accounts.size() > 1) {
798 mAccountHeaderContainer.setVisibility(View.GONE);
799 addAccountSelector(accountInfo);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700800 } else if (mHasNewContact && !mIsUserProfile) {
801 addAccountHeader(accountInfo);
802 mAccountSelectorContainer.setVisibility(View.GONE);
803 } else if (rawContactDeltas.size() > 1) {
804 mAccountHeaderContainer.setVisibility(View.GONE);
805 mAccountSelectorContainer.setVisibility(View.GONE);
806 addRawContactAccountSelector(rawContactDeltas);
Walter Jang708ea9e2015-09-10 15:42:05 -0700807 } else {
Walter Jangbe5e1b12015-10-17 11:38:29 -0700808 if (mIsUserProfile) {
809 addLocalAccountHeader(accountInfo);
810 } else {
811 addAccountHeader(accountInfo);
812 }
Walter Jang708ea9e2015-09-10 15:42:05 -0700813 mAccountSelectorContainer.setVisibility(View.GONE);
814 }
815 }
816
Walter Jangbe5e1b12015-10-17 11:38:29 -0700817 private void addLocalAccountHeader(Pair<String,String> accountInfo) {
818 // Set the account name
819 final String accountName = TextUtils.isEmpty(accountInfo.first)
820 ? accountInfo.second : accountInfo.first;
821 mAccountHeaderName.setVisibility(View.VISIBLE);
822 mAccountHeaderName.setText(accountName);
823
824 // Set the account type
825 final String selectorTitle = getResources().getString(
826 R.string.compact_editor_account_selector_title);
827 mAccountHeaderType.setText(selectorTitle);
828
829 // Set the icon
830 final AccountType primaryAccountType =
831 mPrimaryRawContactDelta.getRawContactAccountType(getContext());
832 mAccountHeaderIcon.setImageDrawable(primaryAccountType.getDisplayIcon(getContext()));
833
834 // Set the content description
835 mAccountHeaderContainer.setContentDescription(
836 EditorUiUtils.getAccountInfoContentDescription(accountName, selectorTitle));
837 }
838
Walter Jang708ea9e2015-09-10 15:42:05 -0700839 private void addAccountHeader(Pair<String,String> accountInfo) {
Walter Jangbe5e1b12015-10-17 11:38:29 -0700840 // Set the account name
Walter Jang03cea2e2015-09-18 17:04:21 -0700841 if (TextUtils.isEmpty(accountInfo.first)) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700842 // Hide this view so the other text view will be centered vertically
843 mAccountHeaderName.setVisibility(View.GONE);
844 } else {
845 mAccountHeaderName.setVisibility(View.VISIBLE);
846 mAccountHeaderName.setText(accountInfo.first);
847 }
Walter Jang9488a762015-10-16 13:42:48 -0700848
Walter Jangbe5e1b12015-10-17 11:38:29 -0700849 // Set the account type
Walter Jang9488a762015-10-16 13:42:48 -0700850 final String selectorTitle = getResources().getString(
851 R.string.compact_editor_account_selector_title);
852 mAccountHeaderType.setText(selectorTitle);
Walter Jang708ea9e2015-09-10 15:42:05 -0700853
Walter Jangbe5e1b12015-10-17 11:38:29 -0700854 // Set the icon
Tingting Wang91cee282015-10-07 13:48:17 -0700855 final AccountType primaryAccountType = mPrimaryRawContactDelta.getRawContactAccountType(
856 getContext());
857 mAccountHeaderIcon.setImageDrawable(primaryAccountType.getDisplayIcon(getContext()));
858
Walter Jangbe5e1b12015-10-17 11:38:29 -0700859 // Set the content description
Walter Jang708ea9e2015-09-10 15:42:05 -0700860 mAccountHeaderContainer.setContentDescription(
861 EditorUiUtils.getAccountInfoContentDescription(
Walter Jang9488a762015-10-16 13:42:48 -0700862 accountInfo.first, selectorTitle));
Walter Jang708ea9e2015-09-10 15:42:05 -0700863 }
864
865 private void addAccountSelector(Pair<String,String> accountInfo) {
866 mAccountSelectorContainer.setVisibility(View.VISIBLE);
867
Walter Jang03cea2e2015-09-18 17:04:21 -0700868 if (TextUtils.isEmpty(accountInfo.first)) {
Walter Jang708ea9e2015-09-10 15:42:05 -0700869 // Hide this view so the other text view will be centered vertically
870 mAccountSelectorName.setVisibility(View.GONE);
871 } else {
872 mAccountSelectorName.setVisibility(View.VISIBLE);
873 mAccountSelectorName.setText(accountInfo.first);
874 }
Walter Jang9488a762015-10-16 13:42:48 -0700875
876 final String selectorTitle = getResources().getString(
877 R.string.compact_editor_account_selector_title);
878 mAccountSelectorType.setText(selectorTitle);
Walter Jang708ea9e2015-09-10 15:42:05 -0700879
880 mAccountSelectorContainer.setContentDescription(
881 EditorUiUtils.getAccountInfoContentDescription(
Walter Jang9488a762015-10-16 13:42:48 -0700882 accountInfo.first, selectorTitle));
Walter Jang708ea9e2015-09-10 15:42:05 -0700883
884 mAccountSelector.setOnClickListener(new View.OnClickListener() {
885 @Override
886 public void onClick(View v) {
887 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
888 final AccountsListAdapter adapter =
889 new AccountsListAdapter(getContext(),
890 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE,
891 mPrimaryAccount);
892 popup.setWidth(mAccountSelectorContainer.getWidth());
893 popup.setAnchorView(mAccountSelectorContainer);
894 popup.setAdapter(adapter);
895 popup.setModal(true);
896 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
897 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
898 @Override
899 public void onItemClick(AdapterView<?> parent, View view, int position,
900 long id) {
901 UiClosables.closeQuietly(popup);
902 final AccountWithDataSet newAccount = adapter.getItem(position);
903 if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
904 mListener.onRebindEditorsForNewContact(
905 mPrimaryRawContactDelta,
906 mPrimaryAccount,
907 newAccount);
908 }
909 }
910 });
911 popup.show();
912 }
913 });
914 }
915
Tingting Wang655ad1a2015-10-05 17:51:14 -0700916 private void addRawContactAccountSelector(final RawContactDeltaList rawContactDeltas) {
917 mRawContactContainer.setVisibility(View.VISIBLE);
918
Tingting Wanga0b69402015-10-13 14:27:58 -0700919 Collections.sort(rawContactDeltas, new RawContactDeltaComparator(getContext()));
920
Walter Jang9488a762015-10-16 13:42:48 -0700921 final String accountsSummary = getResources().getString(
922 R.string.compact_editor_linked_contacts_selector_title, rawContactDeltas.size());
Tingting Wang655ad1a2015-10-05 17:51:14 -0700923 mRawContactSummary.setText(accountsSummary);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700924
925 mRawContactContainer.setOnClickListener(new View.OnClickListener() {
926 @Override
927 public void onClick(View v) {
928 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
929 final RawContactAccountListAdapter adapter =
930 new RawContactAccountListAdapter(getContext(), rawContactDeltas);
931 popup.setWidth(mRawContactContainer.getWidth());
932 popup.setAnchorView(mRawContactContainer);
933 popup.setAdapter(adapter);
934 popup.setModal(true);
935 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
936 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
937 @Override
938 public void onItemClick(AdapterView<?> parent, View view, int position,
939 long id) {
940 UiClosables.closeQuietly(popup);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700941
942 if (mListener != null) {
Tingting Wang91cee282015-10-07 13:48:17 -0700943 final long rawContactId = adapter.getItemId(position);
944 final Uri rawContactUri = ContentUris.withAppendedId(
945 ContactsContract.RawContacts.CONTENT_URI, rawContactId);
946 final RawContactDelta rawContactDelta = adapter.getItem(position);
947 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
948 getContext());
949 final AccountType accountType = rawContactDelta.getAccountType(
950 accountTypes);
951 final boolean isReadOnly = !accountType.areContactsWritable();
952
953 mListener.onRawContactSelected(rawContactUri, rawContactId, isReadOnly);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700954 }
955 }
956 });
957 popup.show();
958 }
959 });
960 }
961
Walter Jang363d3fd2015-09-16 10:29:07 -0700962 private void addPhotoView() {
Walter Jang31a74ad2015-10-02 19:17:39 -0700963 // Get the kind section data and values delta that we will display in the photo view
Walter Jangcbd431d2015-10-06 13:07:30 -0700964 final KindSectionDataList kindSectionDataList =
965 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
966 final Pair<KindSectionData,ValuesDelta> photoToDisplay =
967 kindSectionDataList.getEntryToDisplay(mPhotoId);
968 if (photoToDisplay == null) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700969 wlog("photo: no kind section data parsed");
Walter Jangcbd431d2015-10-06 13:07:30 -0700970 mPhotoView.setVisibility(View.GONE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700971 return;
972 }
Walter Jang06f73a12015-06-17 11:15:48 -0700973
Walter Jang31a74ad2015-10-02 19:17:39 -0700974 // Set the photo view
Walter Jangcbd431d2015-10-06 13:07:30 -0700975 mPhotoView.setPhoto(photoToDisplay.second, mMaterialPalette);
Walter Jang31a74ad2015-10-02 19:17:39 -0700976
977 // Find the raw contact ID and values delta that will be written when the photo is edited
Walter Jangcbd431d2015-10-06 13:07:30 -0700978 final Pair<KindSectionData,ValuesDelta> photoToWrite = kindSectionDataList.getEntryToWrite(
979 mPrimaryAccount, mHasNewContact);
980 if (photoToWrite == null) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700981 mPhotoView.setReadOnly(true);
982 return;
983 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700984 mPhotoView.setReadOnly(false);
Walter Jangcbd431d2015-10-06 13:07:30 -0700985 mPhotoRawContactId = photoToWrite.first.getRawContactDelta().getRawContactId();
986 mPhotoValuesDelta = photoToWrite.second;
Walter Jang31a74ad2015-10-02 19:17:39 -0700987 }
988
Walter Jangf5dfea42015-09-16 12:30:36 -0700989 private void addKindSectionViews() {
990 // Sort the kinds
Walter Jangcbd431d2015-10-06 13:07:30 -0700991 final TreeSet<Map.Entry<String,KindSectionDataList>> entries =
Walter Jang192a01c2015-09-22 15:23:55 -0700992 new TreeSet<>(KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR);
Walter Jangf5dfea42015-09-16 12:30:36 -0700993 entries.addAll(mKindSectionDataMap.entrySet());
Walter Jang3efae4a2015-02-18 11:12:00 -0800994
Walter Jangf5dfea42015-09-16 12:30:36 -0700995 vlog("kind: " + entries.size() + " kindSection(s)");
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700996 int i = -1;
Walter Jangcbd431d2015-10-06 13:07:30 -0700997 for (Map.Entry<String, KindSectionDataList> entry : entries) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700998 i++;
999
Walter Jangf5dfea42015-09-16 12:30:36 -07001000 final String mimeType = entry.getKey();
Tingting Wangf695eb32015-10-15 18:45:15 -07001001 KindSectionDataList kindSectionDataList = entry.getValue();
1002 if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
1003 // Only show one name editor view. Select one matched name to display according to
1004 // the order: super primary name, first non-empty name, first empty name, null.
1005 // The name should be the name displayed in Quick contact.
1006 final Pair<KindSectionData,ValuesDelta> nameToDisplay =
1007 kindSectionDataList.getEntryToDisplay(0L);
1008 if (nameToDisplay != null) {
1009 final KindSectionData nameKindSectionData = nameToDisplay.first;
1010 kindSectionDataList = new KindSectionDataList();
1011 kindSectionDataList.add(nameKindSectionData);
1012 }
1013 }
1014
Walter Jangab50e6f2015-06-15 08:57:22 -07001015
Walter Jangf5dfea42015-09-16 12:30:36 -07001016 // Ignore mime types that we've already handled
Walter Jang3e5ae0d2015-09-20 12:43:37 -07001017 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
1018 vlog("kind: " + i + " " + mimeType + " dropped");
1019 continue;
1020 }
Walter Jangf5dfea42015-09-16 12:30:36 -07001021
Walter Jange5e7aef2015-10-15 14:52:42 -07001022 // Don't show more than one group editor on the compact editor.
1023 // Groups will still be editable for each raw contact individually on the full editor.
1024 if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)
1025 && kindSectionDataList.size() > 1) {
1026 vlog("kind: " + i + " " + mimeType + " dropped");
1027 continue;
1028 }
1029
Walter Jang192a01c2015-09-22 15:23:55 -07001030 if (kindSectionDataList != null && !kindSectionDataList.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -07001031 vlog("kind: " + i + " " + mimeType + ": " + kindSectionDataList.size() +
1032 " kindSectionData(s)");
1033
Walter Jangf5dfea42015-09-16 12:30:36 -07001034 final CompactKindSectionView kindSectionView = inflateKindSectionView(
1035 mKindSectionViews, kindSectionDataList, mimeType);
Walter Jangf5dfea42015-09-16 12:30:36 -07001036 mKindSectionViews.addView(kindSectionView);
Walter Jangf10ca152015-09-22 15:23:55 -07001037
1038 // Keep a pointer to all the KindSectionsViews for each mimeType
1039 getKindSectionViews(mimeType).add(kindSectionView);
Walter Jangcab3dce2015-02-09 17:48:03 -08001040 }
1041 }
1042 }
1043
Walter Jangf10ca152015-09-22 15:23:55 -07001044 private List<CompactKindSectionView> getKindSectionViews(String mimeType) {
1045 List<CompactKindSectionView> kindSectionViews = mKindSectionViewsMap.get(mimeType);
1046 if (kindSectionViews == null) {
1047 kindSectionViews = new ArrayList<>();
1048 mKindSectionViewsMap.put(mimeType, kindSectionViews);
1049 }
1050 return kindSectionViews;
1051 }
1052
Walter Jangf5dfea42015-09-16 12:30:36 -07001053 private CompactKindSectionView inflateKindSectionView(ViewGroup viewGroup,
Walter Jangcbd431d2015-10-06 13:07:30 -07001054 KindSectionDataList kindSectionDataList, String mimeType) {
Walter Jangf5dfea42015-09-16 12:30:36 -07001055 final CompactKindSectionView kindSectionView = (CompactKindSectionView)
1056 mLayoutInflater.inflate(R.layout.compact_item_kind_section, viewGroup,
1057 /* attachToRoot =*/ false);
Walter Jang45b86d52015-10-15 15:23:16 -07001058 kindSectionView.setIsUserProfile(mIsUserProfile);
Walter Jangf5dfea42015-09-16 12:30:36 -07001059
Walter Jang192a01c2015-09-22 15:23:55 -07001060 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangf5dfea42015-09-16 12:30:36 -07001061 || Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -07001062 // Phone numbers and email addresses are always displayed,
1063 // even if they are empty
Walter Jangf5dfea42015-09-16 12:30:36 -07001064 kindSectionView.setHideWhenEmpty(false);
1065 }
Walter Jangf5dfea42015-09-16 12:30:36 -07001066
Walter Jang192a01c2015-09-22 15:23:55 -07001067 // Since phone numbers and email addresses displayed even if they are empty,
1068 // they will be the only types you add new values to initially for new contacts
1069 kindSectionView.setShowOneEmptyEditor(true);
1070
1071 // Sort so the editors wind up in the order we want
1072 if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
1073 Collections.sort(kindSectionDataList, new NameEditorComparator(getContext(),
1074 mPrimaryRawContactDelta));
1075 } else {
1076 Collections.sort(kindSectionDataList, new EditorComparator(getContext()));
Walter Jang3e5ae0d2015-09-20 12:43:37 -07001077 }
1078
Walter Jang79658e12015-09-24 10:36:26 -07001079 kindSectionView.setState(kindSectionDataList, mViewIdGenerator, mListener);
Walter Jangf5dfea42015-09-16 12:30:36 -07001080
1081 return kindSectionView;
Walter Jangcab3dce2015-02-09 17:48:03 -08001082 }
1083
Walter Jang4f5594a2015-10-06 18:40:31 -07001084 private void maybeCopyPrimaryDisplayName() {
1085 if (TextUtils.isEmpty(mReadOnlyDisplayName)) return;
1086 final List<CompactKindSectionView> kindSectionViews
1087 = mKindSectionViewsMap.get(StructuredName.CONTENT_ITEM_TYPE);
1088 if (kindSectionViews.isEmpty()) return;
1089 final CompactKindSectionView primaryNameKindSectionView = kindSectionViews.get(0);
1090 if (primaryNameKindSectionView.isEmptyName()) {
1091 vlog("name: using read only display name as primary name");
1092 primaryNameKindSectionView.setName(mReadOnlyDisplayName);
1093 mPrimaryNameEditorView = primaryNameKindSectionView.getPrimaryNameEditorView();
1094 }
1095 }
1096
Walter Jangd6753152015-10-02 09:23:13 -07001097 private void showAllFields() {
Walter Jang79658e12015-09-24 10:36:26 -07001098 // Stop hiding empty editors and allow the user to enter values for all kinds now
1099 for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
1100 final CompactKindSectionView kindSectionView =
1101 (CompactKindSectionView) mKindSectionViews.getChildAt(i);
1102 kindSectionView.setHideWhenEmpty(false);
1103 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
1104 }
1105 mIsExpanded = true;
Walter Jang79658e12015-09-24 10:36:26 -07001106
Walter Jangf5dfea42015-09-16 12:30:36 -07001107 // Hide the more fields button
1108 mMoreFields.setVisibility(View.GONE);
Walter Jangcab3dce2015-02-09 17:48:03 -08001109 }
1110
Walter Jangbf63a6d2015-05-05 09:14:35 -07001111 private static void vlog(String message) {
1112 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1113 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -08001114 }
1115 }
Walter Jang363d3fd2015-09-16 10:29:07 -07001116
1117 private static void wlog(String message) {
1118 if (Log.isLoggable(TAG, Log.WARN)) {
1119 Log.w(TAG, message);
1120 }
1121 }
1122
1123 private static void elog(String message) {
1124 Log.e(TAG, message);
1125 }
Walter Jangcab3dce2015-02-09 17:48:03 -08001126}