blob: 5c3c5659bd3023da806b0f9365bac74f2401ccba [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
Tingting Wang655ad1a2015-10-05 17:51:14 -070019import android.content.ContentUris;
Walter Jangcab3dce2015-02-09 17:48:03 -080020import android.content.Context;
Walter Jangf10ca152015-09-22 15:23:55 -070021import android.database.Cursor;
Walter Jang3efae4a2015-02-18 11:12:00 -080022import android.graphics.Bitmap;
Walter Jang41b3ea12015-03-09 17:30:06 -070023import android.net.Uri;
Walter Jang3f18d612015-10-07 16:01:05 -070024import android.os.Bundle;
Walter Jang79658e12015-09-24 10:36:26 -070025import android.os.Parcel;
26import android.os.Parcelable;
Tingting Wang655ad1a2015-10-05 17:51:14 -070027import android.provider.ContactsContract;
Walter Jangcab3dce2015-02-09 17:48:03 -080028import android.provider.ContactsContract.CommonDataKinds.Email;
Walter Jangf5dfea42015-09-16 12:30:36 -070029import android.provider.ContactsContract.CommonDataKinds.Event;
Walter Jangcab3dce2015-02-09 17:48:03 -080030import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Walter Jangf5dfea42015-09-16 12:30:36 -070031import android.provider.ContactsContract.CommonDataKinds.Im;
Walter Jang3efae4a2015-02-18 11:12:00 -080032import android.provider.ContactsContract.CommonDataKinds.Nickname;
Walter Jangf5dfea42015-09-16 12:30:36 -070033import android.provider.ContactsContract.CommonDataKinds.Note;
34import android.provider.ContactsContract.CommonDataKinds.Organization;
Walter Jangcab3dce2015-02-09 17:48:03 -080035import android.provider.ContactsContract.CommonDataKinds.Phone;
36import android.provider.ContactsContract.CommonDataKinds.Photo;
Walter Jangf5dfea42015-09-16 12:30:36 -070037import android.provider.ContactsContract.CommonDataKinds.Relation;
38import android.provider.ContactsContract.CommonDataKinds.SipAddress;
Walter Jangcab3dce2015-02-09 17:48:03 -080039import android.provider.ContactsContract.CommonDataKinds.StructuredName;
Walter Jangf5dfea42015-09-16 12:30:36 -070040import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
41import android.provider.ContactsContract.CommonDataKinds.Website;
Walter Jangcab3dce2015-02-09 17:48:03 -080042import android.text.TextUtils;
43import android.util.AttributeSet;
44import android.util.Log;
Walter Jang2d3f31c2015-06-18 23:15:31 -070045import android.util.Pair;
Walter Jangcab3dce2015-02-09 17:48:03 -080046import android.view.LayoutInflater;
Walter Jangb1c87622015-02-13 17:51:38 -080047import android.view.View;
Walter Jangcab3dce2015-02-09 17:48:03 -080048import android.view.ViewGroup;
Walter Jang708ea9e2015-09-10 15:42:05 -070049import android.widget.AdapterView;
Tingting Wang655ad1a2015-10-05 17:51:14 -070050import android.widget.BaseAdapter;
51import android.widget.ImageView;
Walter Jangcab3dce2015-02-09 17:48:03 -080052import android.widget.LinearLayout;
Walter Jang708ea9e2015-09-10 15:42:05 -070053import android.widget.ListPopupWindow;
Walter Jang2d3f31c2015-06-18 23:15:31 -070054import android.widget.TextView;
Walter Jangcab3dce2015-02-09 17:48:03 -080055
Gary Maib6e28d22016-09-12 14:04:53 -070056import com.android.contacts.R;
57import com.android.contacts.common.model.AccountTypeManager;
58import com.android.contacts.common.model.RawContactDelta;
59import com.android.contacts.common.model.RawContactDeltaList;
60import com.android.contacts.common.model.RawContactModifier;
61import com.android.contacts.common.model.ValuesDelta;
62import com.android.contacts.common.model.account.AccountDisplayInfo;
63import com.android.contacts.common.model.account.AccountDisplayInfoFactory;
64import com.android.contacts.common.model.account.AccountType;
65import com.android.contacts.common.model.account.AccountWithDataSet;
66import com.android.contacts.common.model.dataitem.DataKind;
67import com.android.contacts.common.util.AccountsListAdapter;
68import com.android.contacts.common.util.MaterialColorMapUtils;
69import com.android.contacts.util.UiClosables;
70
Walter Jang708774a2015-10-16 09:32:06 -070071import java.io.FileNotFoundException;
Walter Jangcab3dce2015-02-09 17:48:03 -080072import java.util.ArrayList;
Walter Jangf5dfea42015-09-16 12:30:36 -070073import java.util.Arrays;
Walter Jang192a01c2015-09-22 15:23:55 -070074import java.util.Collections;
Walter Jangf5dfea42015-09-16 12:30:36 -070075import java.util.Comparator;
Walter Jang19d985f2015-07-01 13:36:27 -070076import java.util.HashMap;
Walter Jangcab3dce2015-02-09 17:48:03 -080077import java.util.List;
Walter Jang19d985f2015-07-01 13:36:27 -070078import java.util.Map;
Walter Jangf5dfea42015-09-16 12:30:36 -070079import java.util.TreeSet;
Walter Jangcab3dce2015-02-09 17:48:03 -080080
81/**
Walter Jangf5dfea42015-09-16 12:30:36 -070082 * View to display information from multiple {@link RawContactDelta}s grouped together.
Walter Jangcab3dce2015-02-09 17:48:03 -080083 */
Walter Jangb6ca2722015-02-20 11:10:25 -080084public class CompactRawContactsEditorView extends LinearLayout implements View.OnClickListener {
Walter Jangcab3dce2015-02-09 17:48:03 -080085
Walter Jang4f5594a2015-10-06 18:40:31 -070086 static final String TAG = "CompactEditorView";
Walter Jangcab3dce2015-02-09 17:48:03 -080087
Walter Jang192a01c2015-09-22 15:23:55 -070088 private static final KindSectionDataMapEntryComparator
89 KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR = new KindSectionDataMapEntryComparator();
Walter Jangf5dfea42015-09-16 12:30:36 -070090
Walter Jangb6ca2722015-02-20 11:10:25 -080091 /**
92 * Callbacks for hosts of {@link CompactRawContactsEditorView}s.
93 */
94 public interface Listener {
95
96 /**
Walter Jang151f3e62015-02-26 15:29:40 -080097 * Invoked when the structured name editor field has changed.
98 *
99 * @param rawContactId The raw contact ID from the underlying {@link RawContactDelta}.
100 * @param valuesDelta The values from the underlying {@link RawContactDelta}.
101 */
102 public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta);
Walter Jang708ea9e2015-09-10 15:42:05 -0700103
104 /**
105 * Invoked when the compact editor should rebind editors for a new account.
106 *
107 * @param oldState Old data being edited.
108 * @param oldAccount Old account associated with oldState.
109 * @param newAccount New account to be used.
110 */
111 public void onRebindEditorsForNewContact(RawContactDelta oldState,
112 AccountWithDataSet oldAccount, AccountWithDataSet newAccount);
Walter Jang79658e12015-09-24 10:36:26 -0700113
114 /**
115 * Invoked when no editors could be bound for the contact.
116 */
117 public void onBindEditorsFailed();
Walter Jangd6753152015-10-02 09:23:13 -0700118
119 /**
120 * Invoked after editors have been bound for the contact.
121 */
122 public void onEditorsBound();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700123
124 /**
Tingting Wang5585c6e2015-10-14 11:19:58 -0700125 * Invoked when a rawcontact from linked contacts is selected in editor.
Tingting Wang655ad1a2015-10-05 17:51:14 -0700126 */
Tingting Wang91cee282015-10-07 13:48:17 -0700127 public void onRawContactSelected(Uri uri, long rawContactId, boolean isReadOnly);
Walter Jang3f18d612015-10-07 16:01:05 -0700128
129 /**
130 * Returns the map of raw contact IDs to newly taken or selected photos that have not
131 * yet been saved to CP2.
132 */
133 public Bundle getUpdatedPhotos();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700134 }
135
136 /**
137 * Used to list the account info for the given raw contacts list.
138 */
139 private static final class RawContactAccountListAdapter extends BaseAdapter {
140 private final LayoutInflater mInflater;
141 private final Context mContext;
142 private final RawContactDeltaList mRawContactDeltas;
143
144 public RawContactAccountListAdapter(Context context, RawContactDeltaList rawContactDeltas) {
145 mContext = context;
146 mRawContactDeltas = new RawContactDeltaList();
147 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang23709542015-10-22 12:50:58 -0700148 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
Tingting Wang655ad1a2015-10-05 17:51:14 -0700149 mRawContactDeltas.add(rawContactDelta);
150 }
151 }
152 mInflater = LayoutInflater.from(context);
153 }
154
155 @Override
156 public View getView(int position, View convertView, ViewGroup parent) {
157 final View resultView = convertView != null ? convertView
158 : mInflater.inflate(R.layout.account_selector_list_item, parent, false);
159
160 final RawContactDelta rawContactDelta = mRawContactDeltas.get(position);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700161
162 final TextView text1 = (TextView) resultView.findViewById(android.R.id.text1);
Walter Jang9488a762015-10-16 13:42:48 -0700163 final AccountType accountType = rawContactDelta.getRawContactAccountType(mContext);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700164 text1.setText(accountType.getDisplayLabel(mContext));
165
Tingting Wang655ad1a2015-10-05 17:51:14 -0700166 final TextView text2 = (TextView) resultView.findViewById(android.R.id.text2);
Walter Jang9488a762015-10-16 13:42:48 -0700167 final String accountName = rawContactDelta.getAccountName();
Tingting Wanga0b69402015-10-13 14:27:58 -0700168 if (TextUtils.isEmpty(accountName)) {
169 text2.setVisibility(View.GONE);
170 } else {
Walter Jang9488a762015-10-16 13:42:48 -0700171 // Truncate email addresses in the middle so we don't lose the domain
Tingting Wanga0b69402015-10-13 14:27:58 -0700172 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
Walter Jang79658e12015-09-24 10:36:26 -0700291 public static class SavedState extends BaseSavedState {
292
293 public static final Parcelable.Creator<SavedState> CREATOR =
294 new Parcelable.Creator<SavedState>() {
295 public SavedState createFromParcel(Parcel in) {
296 return new SavedState(in);
297 }
298 public SavedState[] newArray(int size) {
299 return new SavedState[size];
300 }
301 };
302
303 private boolean mIsExpanded;
304
305 public SavedState(Parcelable superState) {
306 super(superState);
307 }
308
309 private SavedState(Parcel in) {
310 super(in);
311 mIsExpanded = in.readInt() != 0;
312 }
313
314 @Override
315 public void writeToParcel(Parcel out, int flags) {
316 super.writeToParcel(out, flags);
317 out.writeInt(mIsExpanded ? 1 : 0);
318 }
319 }
320
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700321 private CompactRawContactsEditorView.Listener mListener;
Walter Jangb6ca2722015-02-20 11:10:25 -0800322
Walter Jangcab3dce2015-02-09 17:48:03 -0800323 private AccountTypeManager mAccountTypeManager;
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700324 private AccountDisplayInfoFactory mAccountDisplayInfoFactory;
Walter Jangcab3dce2015-02-09 17:48:03 -0800325 private LayoutInflater mLayoutInflater;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800326
Walter Jangcab3dce2015-02-09 17:48:03 -0800327 private ViewIdGenerator mViewIdGenerator;
Walter Jangf46abd82015-02-20 16:52:04 -0800328 private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
Walter Jang82acd422015-10-17 14:01:27 -0700329 private long mPhotoId = -1;
Walter Jang708ea9e2015-09-10 15:42:05 -0700330 private boolean mHasNewContact;
331 private boolean mIsUserProfile;
332 private AccountWithDataSet mPrimaryAccount;
Walter Jangcbd431d2015-10-06 13:07:30 -0700333 private Map<String,KindSectionDataList> mKindSectionDataMap = new HashMap<>();
Walter Jangcab3dce2015-02-09 17:48:03 -0800334
Walter Jang708ea9e2015-09-10 15:42:05 -0700335 // Account header
336 private View mAccountHeaderContainer;
337 private TextView mAccountHeaderType;
338 private TextView mAccountHeaderName;
Tingting Wang91cee282015-10-07 13:48:17 -0700339 private ImageView mAccountHeaderIcon;
Gary Mai46cb3102016-08-10 18:14:09 -0700340 private ImageView mAccountHeaderExpanderIcon;
Walter Jang708ea9e2015-09-10 15:42:05 -0700341
Tingting Wang655ad1a2015-10-05 17:51:14 -0700342 // Raw contacts selector
343 private View mRawContactContainer;
344 private TextView mRawContactSummary;
Tingting Wang655ad1a2015-10-05 17:51:14 -0700345
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700346 private CompactPhotoEditorView mPhotoView;
Walter Jangf5dfea42015-09-16 12:30:36 -0700347 private ViewGroup mKindSectionViews;
Walter Jangf10ca152015-09-22 15:23:55 -0700348 private Map<String,List<CompactKindSectionView>> mKindSectionViewsMap = new HashMap<>();
Walter Jangb6ca2722015-02-20 11:10:25 -0800349 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -0800350
Walter Jang79658e12015-09-24 10:36:26 -0700351 private boolean mIsExpanded;
Walter Jangcbd431d2015-10-06 13:07:30 -0700352
Walter Jang3efae4a2015-02-18 11:12:00 -0800353 private long mPhotoRawContactId;
Walter Jang31a74ad2015-10-02 19:17:39 -0700354 private ValuesDelta mPhotoValuesDelta;
Walter Jang82acd422015-10-17 14:01:27 -0700355
356 private Pair<KindSectionData, ValuesDelta> mPrimaryNameKindSectionData;
Walter Jang3efae4a2015-02-18 11:12:00 -0800357
Walter Jangcab3dce2015-02-09 17:48:03 -0800358 public CompactRawContactsEditorView(Context context) {
359 super(context);
360 }
361
362 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
363 super(context, attrs);
364 }
365
Walter Jangb6ca2722015-02-20 11:10:25 -0800366 /**
367 * Sets the receiver for {@link CompactRawContactsEditorView} callbacks.
368 */
369 public void setListener(Listener listener) {
370 mListener = listener;
371 }
372
Walter Jangcab3dce2015-02-09 17:48:03 -0800373 @Override
374 protected void onFinishInflate() {
375 super.onFinishInflate();
376
377 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700378 mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forWritableAccounts(getContext());
Walter Jangcab3dce2015-02-09 17:48:03 -0800379 mLayoutInflater = (LayoutInflater)
380 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
381
Walter Jang708ea9e2015-09-10 15:42:05 -0700382 // Account header
Gary Mai46cb3102016-08-10 18:14:09 -0700383 mAccountHeaderContainer = findViewById(R.id.account_header_container);
Walter Jang708ea9e2015-09-10 15:42:05 -0700384 mAccountHeaderType = (TextView) findViewById(R.id.account_type);
385 mAccountHeaderName = (TextView) findViewById(R.id.account_name);
Tingting Wang91cee282015-10-07 13:48:17 -0700386 mAccountHeaderIcon = (ImageView) findViewById(R.id.account_type_icon);
Gary Mai46cb3102016-08-10 18:14:09 -0700387 mAccountHeaderExpanderIcon = (ImageView) findViewById(R.id.account_expander_icon);
Walter Jang708ea9e2015-09-10 15:42:05 -0700388
Tingting Wang655ad1a2015-10-05 17:51:14 -0700389 // Raw contacts selector
390 mRawContactContainer = findViewById(R.id.all_rawcontacts_accounts_container);
391 mRawContactSummary = (TextView) findViewById(R.id.rawcontacts_accounts_summary);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700392
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700393 mPhotoView = (CompactPhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangf5dfea42015-09-16 12:30:36 -0700394 mKindSectionViews = (LinearLayout) findViewById(R.id.kind_section_views);
Walter Jangb6ca2722015-02-20 11:10:25 -0800395 mMoreFields = findViewById(R.id.more_fields);
396 mMoreFields.setOnClickListener(this);
397 }
398
Walter Jangb6ca2722015-02-20 11:10:25 -0800399 @Override
400 public void onClick(View view) {
Walter Jangf5dfea42015-09-16 12:30:36 -0700401 if (view.getId() == R.id.more_fields) {
Walter Jangd6753152015-10-02 09:23:13 -0700402 showAllFields();
Walter Jangb6ca2722015-02-20 11:10:25 -0800403 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800404 }
405
406 @Override
407 public void setEnabled(boolean enabled) {
408 super.setEnabled(enabled);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700409 final int childCount = mKindSectionViews.getChildCount();
410 for (int i = 0; i < childCount; i++) {
411 mKindSectionViews.getChildAt(i).setEnabled(enabled);
Walter Jangcab3dce2015-02-09 17:48:03 -0800412 }
413 }
414
Walter Jang79658e12015-09-24 10:36:26 -0700415 @Override
416 public Parcelable onSaveInstanceState() {
417 final Parcelable superState = super.onSaveInstanceState();
418 final SavedState savedState = new SavedState(superState);
419 savedState.mIsExpanded = mIsExpanded;
420 return savedState;
421 }
422
423 @Override
424 public void onRestoreInstanceState(Parcelable state) {
425 if(!(state instanceof SavedState)) {
426 super.onRestoreInstanceState(state);
427 return;
428 }
429 final SavedState savedState = (SavedState) state;
430 super.onRestoreInstanceState(savedState.getSuperState());
431 mIsExpanded = savedState.mIsExpanded;
Walter Jangd6753152015-10-02 09:23:13 -0700432 if (mIsExpanded) {
433 showAllFields();
Walter Jang79658e12015-09-24 10:36:26 -0700434 }
435 }
436
Walter Jang3efae4a2015-02-18 11:12:00 -0800437 /**
Walter Jang31a74ad2015-10-02 19:17:39 -0700438 * Pass through to {@link CompactPhotoEditorView#setListener}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800439 */
Walter Jang31a74ad2015-10-02 19:17:39 -0700440 public void setPhotoListener(CompactPhotoEditorView.Listener listener) {
441 mPhotoView.setListener(listener);
Walter Jang3efae4a2015-02-18 11:12:00 -0800442 }
443
Walter Jang31a74ad2015-10-02 19:17:39 -0700444 public void removePhoto() {
Walter Jang45b86d52015-10-15 15:23:16 -0700445 mPhotoValuesDelta.setFromTemplate(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700446 mPhotoValuesDelta.put(Photo.PHOTO, (byte[]) null);
447
448 mPhotoView.removePhoto();
Walter Jang3efae4a2015-02-18 11:12:00 -0800449 }
450
451 /**
Walter Jang41b3ea12015-03-09 17:30:06 -0700452 * Pass through to {@link CompactPhotoEditorView#setFullSizedPhoto(Uri)}.
453 */
454 public void setFullSizePhoto(Uri photoUri) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700455 mPhotoView.setFullSizedPhoto(photoUri);
Walter Jang41b3ea12015-03-09 17:30:06 -0700456 }
457
Walter Jang31a74ad2015-10-02 19:17:39 -0700458 public void updatePhoto(Uri photoUri) {
Walter Jang45b86d52015-10-15 15:23:16 -0700459 mPhotoValuesDelta.setFromTemplate(false);
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700460 // Unset primary for all photos
Wenyi Wang9086fb92015-11-16 09:58:20 -0800461 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700462 // Mark the currently displayed photo as primary
463 mPhotoValuesDelta.setSuperPrimary(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700464
Wenyi Wang4c9cf3a2015-10-15 17:58:23 -0700465 // Even though high-res photos cannot be saved by passing them via
466 // an EntityDeltaList (since they cause the Bundle size limit to be
467 // exceeded), we still pass a low-res thumbnail. This simplifies
468 // code all over the place, because we don't have to test whether
469 // there is a change in EITHER the delta-list OR a changed photo...
470 // this way, there is always a change in the delta-list.
471 try {
472 final byte[] bytes = EditorUiUtils.getCompressedThumbnailBitmapBytes(
473 getContext(), photoUri);
474 if (bytes != null) {
475 mPhotoValuesDelta.setPhoto(bytes);
476 }
477 } catch (FileNotFoundException e) {
478 elog("Failed to get bitmap from photo Uri");
479 }
480
Walter Jang31a74ad2015-10-02 19:17:39 -0700481 mPhotoView.setFullSizedPhoto(photoUri);
482 }
483
Wenyi Wang9086fb92015-11-16 09:58:20 -0800484 private void unsetSuperPrimaryFromAllPhotos() {
Walter Jang3f18d612015-10-07 16:01:05 -0700485 final List<KindSectionData> kindSectionDataList =
486 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
487 for (KindSectionData kindSectionData : kindSectionDataList) {
Wenyi Wang9086fb92015-11-16 09:58:20 -0800488 for (ValuesDelta valuesDelta : kindSectionData.getNonEmptyValuesDeltas()) {
489 valuesDelta.setSuperPrimary(false);
Walter Jang3f18d612015-10-07 16:01:05 -0700490 }
491 }
492 }
493
Walter Jang41b3ea12015-03-09 17:30:06 -0700494 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800495 * Pass through to {@link CompactPhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800496 */
497 public boolean isWritablePhotoSet() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700498 return mPhotoView.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800499 }
500
501 /**
Walter Jang3efae4a2015-02-18 11:12:00 -0800502 * Get the raw contact ID for the CompactHeaderView photo.
503 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800504 public long getPhotoRawContactId() {
505 return mPhotoRawContactId;
506 }
507
Walter Jang4f5594a2015-10-06 18:40:31 -0700508 public StructuredNameEditorView getPrimaryNameEditorView() {
Walter Jange3945952015-10-27 12:44:54 -0700509 final CompactKindSectionView primaryNameKindSectionView = getPrimaryNameKindSectionView();
510 return primaryNameKindSectionView == null
511 ? null : primaryNameKindSectionView.getPrimaryNameEditorView();
Walter Jang4f5594a2015-10-06 18:40:31 -0700512 }
513
Walter Jang31a74ad2015-10-02 19:17:39 -0700514 /**
515 * Returns a data holder for every non-default/non-empty photo from each raw contact, whether
516 * the raw contact is writable or not.
517 */
518 public ArrayList<CompactPhotoSelectionFragment.Photo> getPhotos() {
519 final ArrayList<CompactPhotoSelectionFragment.Photo> photos = new ArrayList<>();
520
Walter Jang3f18d612015-10-07 16:01:05 -0700521 final Bundle updatedPhotos = mListener == null ? null : mListener.getUpdatedPhotos();
522
Walter Jang31a74ad2015-10-02 19:17:39 -0700523 final List<KindSectionData> kindSectionDataList =
524 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
525 for (int i = 0; i < kindSectionDataList.size(); i++) {
526 final KindSectionData kindSectionData = kindSectionDataList.get(i);
527 final AccountType accountType = kindSectionData.getAccountType();
Walter Jang228e02f2015-11-13 09:11:01 -0800528 final List<ValuesDelta> valuesDeltas = kindSectionData.getNonEmptyValuesDeltas();
529 if (valuesDeltas.isEmpty()) continue;
530 for (int j = 0; j < valuesDeltas.size(); j++) {
531 final ValuesDelta valuesDelta = valuesDeltas.get(j);
Walter Jang31a74ad2015-10-02 19:17:39 -0700532 final Bitmap bitmap = EditorUiUtils.getPhotoBitmap(valuesDelta);
533 if (bitmap == null) continue;
534
535 final CompactPhotoSelectionFragment.Photo photo =
536 new CompactPhotoSelectionFragment.Photo();
537 photo.titleRes = accountType.titleRes;
538 photo.iconRes = accountType.iconRes;
539 photo.syncAdapterPackageName = accountType.syncAdapterPackageName;
540 photo.valuesDelta = valuesDelta;
541 photo.primary = valuesDelta.isSuperPrimary();
542 photo.kindSectionDataListIndex = i;
543 photo.valuesDeltaListIndex = j;
Walter Jangda258ff2015-11-17 11:06:02 -0800544 photo.photoId = valuesDelta.getId();
Walter Jang3f18d612015-10-07 16:01:05 -0700545
546 if (updatedPhotos != null) {
547 photo.updatedPhotoUri = (Uri) updatedPhotos.get(String.valueOf(
548 kindSectionData.getRawContactDelta().getRawContactId()));
549 }
550
Wenyi Wangd375c352016-03-28 15:12:45 -0700551 final CharSequence accountTypeLabel = accountType.getDisplayLabel(getContext());
552 photo.accountType = accountTypeLabel == null ? "" : accountTypeLabel.toString();
553 final String accountName = kindSectionData.getRawContactDelta().getAccountName();
554 photo.accountName = accountName == null ? "" : accountName;
Wenyi Wangab360172015-12-15 10:35:58 -0800555
Walter Jang31a74ad2015-10-02 19:17:39 -0700556 photos.add(photo);
557 }
558 }
559
560 return photos;
561 }
562
563 /**
564 * Marks the raw contact photo given as primary for the aggregate contact and updates the
565 * UI.
566 */
567 public void setPrimaryPhoto(CompactPhotoSelectionFragment.Photo photo) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700568 // Find the values delta to mark as primary
Walter Jang3f18d612015-10-07 16:01:05 -0700569 final KindSectionDataList kindSectionDataList =
570 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
Walter Jang31a74ad2015-10-02 19:17:39 -0700571 if (photo.kindSectionDataListIndex < 0
572 || photo.kindSectionDataListIndex >= kindSectionDataList.size()) {
573 wlog("Invalid kind section data list index");
574 return;
575 }
576 final KindSectionData kindSectionData =
577 kindSectionDataList.get(photo.kindSectionDataListIndex);
Walter Jang228e02f2015-11-13 09:11:01 -0800578 final List<ValuesDelta> valuesDeltaList = kindSectionData.getNonEmptyValuesDeltas();
Walter Jang31a74ad2015-10-02 19:17:39 -0700579 if (photo.valuesDeltaListIndex >= valuesDeltaList.size()) {
580 wlog("Invalid values delta list index");
581 return;
582 }
Walter Jangda258ff2015-11-17 11:06:02 -0800583
584 // Update values delta
Walter Jang31a74ad2015-10-02 19:17:39 -0700585 final ValuesDelta valuesDelta = valuesDeltaList.get(photo.valuesDeltaListIndex);
586 valuesDelta.setFromTemplate(false);
Wenyi Wang9086fb92015-11-16 09:58:20 -0800587 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700588 valuesDelta.setSuperPrimary(true);
Walter Jangda258ff2015-11-17 11:06:02 -0800589
Walter Jang31a74ad2015-10-02 19:17:39 -0700590 // Update the UI
591 mPhotoView.setPhoto(valuesDelta, mMaterialPalette);
592 }
593
Walter Jangd35e5ef2015-02-24 09:18:16 -0800594 public View getAggregationAnchorView() {
Walter Jangf10ca152015-09-22 15:23:55 -0700595 final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
Walter Jang192a01c2015-09-22 15:23:55 -0700596 StructuredName.CONTENT_ITEM_TYPE);
Walter Jangf10ca152015-09-22 15:23:55 -0700597 if (!kindSectionViews.isEmpty()) {
Walter Jang192a01c2015-09-22 15:23:55 -0700598 return mKindSectionViews.getChildAt(0).findViewById(R.id.anchor_view);
Walter Jangd35e5ef2015-02-24 09:18:16 -0800599 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700600 return null;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800601 }
602
Walter Jangf10ca152015-09-22 15:23:55 -0700603 public void setGroupMetaData(Cursor groupMetaData) {
604 final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
605 GroupMembership.CONTENT_ITEM_TYPE);
606 for (CompactKindSectionView kindSectionView : kindSectionViews) {
607 kindSectionView.setGroupMetaData(groupMetaData);
Walter Jangd6753152015-10-02 09:23:13 -0700608 if (mIsExpanded) {
609 kindSectionView.setHideWhenEmpty(false);
610 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
611 }
Walter Jangf10ca152015-09-22 15:23:55 -0700612 }
613 }
614
Walter Jangf46abd82015-02-20 16:52:04 -0800615 public void setState(RawContactDeltaList rawContactDeltas,
Walter Jang06f73a12015-06-17 11:15:48 -0700616 MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
Walter Jange3945952015-10-27 12:44:54 -0700617 long photoId, boolean hasNewContact, boolean isUserProfile,
618 AccountWithDataSet primaryAccount) {
Walter Jang363d3fd2015-09-16 10:29:07 -0700619 mKindSectionDataMap.clear();
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700620 mKindSectionViewsMap.clear();
Walter Jangf5dfea42015-09-16 12:30:36 -0700621 mKindSectionViews.removeAllViews();
Walter Jang363d3fd2015-09-16 10:29:07 -0700622 mMoreFields.setVisibility(View.VISIBLE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800623
Walter Jangf46abd82015-02-20 16:52:04 -0800624 mMaterialPalette = materialPalette;
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700625 mViewIdGenerator = viewIdGenerator;
Walter Jang708ea9e2015-09-10 15:42:05 -0700626 mPhotoId = photoId;
Tingting Wangf695eb32015-10-15 18:45:15 -0700627
Walter Jang708ea9e2015-09-10 15:42:05 -0700628 mHasNewContact = hasNewContact;
629 mIsUserProfile = isUserProfile;
630 mPrimaryAccount = primaryAccount;
631 if (mPrimaryAccount == null) {
632 mPrimaryAccount = ContactEditorUtils.getInstance(getContext()).getDefaultAccount();
633 }
634 vlog("state: primary " + mPrimaryAccount);
Walter Jangcab3dce2015-02-09 17:48:03 -0800635
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700636 // Parse the given raw contact deltas
637 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
638 elog("No raw contact deltas");
Walter Jang79658e12015-09-24 10:36:26 -0700639 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jangf5dfea42015-09-16 12:30:36 -0700640 return;
641 }
Walter Jangcbd431d2015-10-06 13:07:30 -0700642 parseRawContactDeltas(rawContactDeltas);
Walter Jang79658e12015-09-24 10:36:26 -0700643 if (mKindSectionDataMap.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700644 elog("No kind section data parsed from RawContactDelta(s)");
Walter Jang79658e12015-09-24 10:36:26 -0700645 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700646 return;
647 }
Walter Jang82acd422015-10-17 14:01:27 -0700648
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700649
Walter Jang82acd422015-10-17 14:01:27 -0700650 // Get the primary name kind section data
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700651 mPrimaryNameKindSectionData =
652 getOrCreateKindSectionDataList(StructuredName.CONTENT_ITEM_TYPE)
653 .getEntryToWrite(/* id =*/ -1, mPrimaryAccount, mIsUserProfile);
Walter Jang82acd422015-10-17 14:01:27 -0700654 if (mPrimaryNameKindSectionData != null) {
655 // Ensure that a structured name and photo exists
656 final RawContactDelta rawContactDelta =
657 mPrimaryNameKindSectionData.first.getRawContactDelta();
658 RawContactModifier.ensureKindExists(
659 rawContactDelta,
660 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700661 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang82acd422015-10-17 14:01:27 -0700662 RawContactModifier.ensureKindExists(
663 rawContactDelta,
664 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700665 Photo.CONTENT_ITEM_TYPE);
666 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700667
668 // Setup the view
Tingting Wang655ad1a2015-10-05 17:51:14 -0700669 addAccountInfo(rawContactDeltas);
Walter Jang363d3fd2015-09-16 10:29:07 -0700670 addPhotoView();
Walter Jangf5dfea42015-09-16 12:30:36 -0700671 addKindSectionViews();
Walter Jange3945952015-10-27 12:44:54 -0700672
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700673 mMoreFields.setVisibility(hasMoreFields() ? View.VISIBLE : View.GONE);
674
Walter Jang4f5594a2015-10-06 18:40:31 -0700675 if (mIsExpanded) showAllFields();
Walter Jangd6753152015-10-02 09:23:13 -0700676
677 if (mListener != null) mListener.onEditorsBound();
Walter Jangcab3dce2015-02-09 17:48:03 -0800678 }
679
Walter Jangcbd431d2015-10-06 13:07:30 -0700680 private void parseRawContactDeltas(RawContactDeltaList rawContactDeltas) {
Walter Jang363d3fd2015-09-16 10:29:07 -0700681 // Build the kind section data list map
Walter Jang192a01c2015-09-22 15:23:55 -0700682 vlog("parse: " + rawContactDeltas.size() + " rawContactDelta(s)");
683 for (int j = 0; j < rawContactDeltas.size(); j++) {
684 final RawContactDelta rawContactDelta = rawContactDeltas.get(j);
685 vlog("parse: " + j + " rawContactDelta" + rawContactDelta);
Walter Jang363d3fd2015-09-16 10:29:07 -0700686 if (rawContactDelta == null || !rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800687 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jang363d3fd2015-09-16 10:29:07 -0700688 if (accountType == null) continue;
689 final List<DataKind> dataKinds = accountType.getSortedDataKinds();
690 final int dataKindSize = dataKinds == null ? 0 : dataKinds.size();
691 vlog("parse: " + dataKindSize + " dataKinds(s)");
692 for (int i = 0; i < dataKindSize; i++) {
693 final DataKind dataKind = dataKinds.get(i);
Walter Jang192a01c2015-09-22 15:23:55 -0700694 if (dataKind == null || !dataKind.editable) {
695 vlog("parse: " + i + " " + dataKind.mimeType + " dropped read-only");
696 continue;
697 }
698 final String mimeType = dataKind.mimeType;
699
700 // Skip psuedo mime types
701 if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
702 || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
703 vlog("parse: " + i + " " + dataKind.mimeType + " dropped pseudo type");
704 continue;
705 }
706
Walter Jang3f18d612015-10-07 16:01:05 -0700707 final KindSectionDataList kindSectionDataList =
Walter Jangcbd431d2015-10-06 13:07:30 -0700708 getOrCreateKindSectionDataList(mimeType);
Walter Jang363d3fd2015-09-16 10:29:07 -0700709 final KindSectionData kindSectionData =
710 new KindSectionData(accountType, dataKind, rawContactDelta);
711 kindSectionDataList.add(kindSectionData);
Walter Jang192a01c2015-09-22 15:23:55 -0700712
Walter Jang363d3fd2015-09-16 10:29:07 -0700713 vlog("parse: " + i + " " + dataKind.mimeType + " " +
Walter Jang228e02f2015-11-13 09:11:01 -0800714 kindSectionData.getValuesDeltas().size() + " value(s) " +
Tingting Wangd482e0c2015-11-16 10:13:29 -0800715 kindSectionData.getNonEmptyValuesDeltas().size() + " non-empty value(s) " +
Wenyi Wang42d740c2015-11-13 14:23:54 -0800716 kindSectionData.getVisibleValuesDeltas().size() +
Walter Jang228e02f2015-11-13 09:11:01 -0800717 " visible value(s)");
Walter Jangac679af2015-06-01 12:17:06 -0700718 }
719 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800720 }
721
Walter Jang3f18d612015-10-07 16:01:05 -0700722 private KindSectionDataList getOrCreateKindSectionDataList(String mimeType) {
Walter Jangcbd431d2015-10-06 13:07:30 -0700723 KindSectionDataList kindSectionDataList = mKindSectionDataMap.get(mimeType);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700724 if (kindSectionDataList == null) {
Walter Jangcbd431d2015-10-06 13:07:30 -0700725 kindSectionDataList = new KindSectionDataList();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700726 mKindSectionDataMap.put(mimeType, kindSectionDataList);
727 }
728 return kindSectionDataList;
729 }
730
Tingting Wang655ad1a2015-10-05 17:51:14 -0700731 private void addAccountInfo(RawContactDeltaList rawContactDeltas) {
Walter Jang82acd422015-10-17 14:01:27 -0700732 mAccountHeaderContainer.setVisibility(View.GONE);
Walter Jang82acd422015-10-17 14:01:27 -0700733 mRawContactContainer.setVisibility(View.GONE);
734
735 if (mPrimaryNameKindSectionData == null) return;
736 final RawContactDelta rawContactDelta =
737 mPrimaryNameKindSectionData.first.getRawContactDelta();
Walter Jang708ea9e2015-09-10 15:42:05 -0700738
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700739 final AccountDisplayInfo account =
740 mAccountDisplayInfoFactory.getAccountDisplayInfoFor(rawContactDelta);
741
Walter Jang708ea9e2015-09-10 15:42:05 -0700742 // Get the account information for the primary raw contact delta
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700743 final String accountLabel = mIsUserProfile
744 ? EditorUiUtils.getAccountHeaderLabelForMyProfile(getContext(), account)
745 : account.getNameLabel().toString();
Walter Jang708ea9e2015-09-10 15:42:05 -0700746
Walter Jang23709542015-10-22 12:50:58 -0700747 // Either the account header or selector should be shown, not both.
Walter Jang708ea9e2015-09-10 15:42:05 -0700748 final List<AccountWithDataSet> accounts =
749 AccountTypeManager.getInstance(getContext()).getAccounts(true);
Walter Jang82acd422015-10-17 14:01:27 -0700750 if (mHasNewContact && !mIsUserProfile) {
751 if (accounts.size() > 1) {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700752 addAccountSelector(rawContactDelta, accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700753 } else {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700754 addAccountHeader(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700755 }
Tingting Wang1594c6f2015-10-26 17:39:16 -0700756 } else if (mIsUserProfile || !shouldHideAccountContainer(rawContactDeltas)) {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700757 addAccountHeader(accountLabel);
Walter Jang708ea9e2015-09-10 15:42:05 -0700758 }
Walter Jang23709542015-10-22 12:50:58 -0700759
760 // The raw contact selector should only display linked raw contacts that can be edited in
761 // the full editor (i.e. they are not newly created raw contacts)
Tingting Wang2bb85d22015-10-23 13:01:09 -0700762 final RawContactAccountListAdapter adapter = new RawContactAccountListAdapter(getContext(),
763 getRawContactDeltaListForSelector(rawContactDeltas));
764 if (adapter.getCount() > 0) {
765 final String accountsSummary = getResources().getQuantityString(
766 R.plurals.compact_editor_linked_contacts_selector_title,
767 adapter.getCount(), adapter.getCount());
Walter Jang23709542015-10-22 12:50:58 -0700768 addRawContactAccountSelector(accountsSummary, adapter);
769 }
Walter Jang708ea9e2015-09-10 15:42:05 -0700770 }
771
Tingting Wang2bb85d22015-10-23 13:01:09 -0700772 private RawContactDeltaList getRawContactDeltaListForSelector(
773 RawContactDeltaList rawContactDeltas) {
774 // Sort raw contacts so google accounts come first
775 Collections.sort(rawContactDeltas, new RawContactDeltaComparator(getContext()));
776
777 final RawContactDeltaList result = new RawContactDeltaList();
778 for (RawContactDelta rawContactDelta : rawContactDeltas) {
779 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
780 // Only add raw contacts that can be opened in the editor
781 result.add(rawContactDelta);
782 }
783 }
784 // Don't return a list of size 1 that would just open the raw contact being edited
785 // in the compact editor in the full editor
786 if (result.size() == 1 && result.get(0).getRawContactAccountType(
787 getContext()).areContactsWritable()) {
788 result.clear();
789 return result;
790 }
791 return result;
792 }
793
Tingting Wang1594c6f2015-10-26 17:39:16 -0700794 // Returns true if there are multiple writable rawcontacts and no read-only ones,
795 // or there are both writable and read-only rawcontacts.
796 private boolean shouldHideAccountContainer(RawContactDeltaList rawContactDeltas) {
Tingting Wang2bb85d22015-10-23 13:01:09 -0700797 int writable = 0;
798 int readonly = 0;
799 for (RawContactDelta rawContactDelta : rawContactDeltas) {
800 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
801 if (rawContactDelta.getRawContactAccountType(getContext()).areContactsWritable()) {
802 writable++;
803 } else {
804 readonly++;
805 }
806 }
807 }
808 return (writable > 1 || (writable > 0 && readonly > 0));
809 }
810
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700811 private void addAccountHeader(String accountLabel) {
Walter Jang82acd422015-10-17 14:01:27 -0700812 mAccountHeaderContainer.setVisibility(View.VISIBLE);
813
Walter Jangbe5e1b12015-10-17 11:38:29 -0700814 // Set the account name
Walter Jangbe5e1b12015-10-17 11:38:29 -0700815 mAccountHeaderName.setVisibility(View.VISIBLE);
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700816 mAccountHeaderName.setText(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700817
818 // Set the account type
819 final String selectorTitle = getResources().getString(
820 R.string.compact_editor_account_selector_title);
821 mAccountHeaderType.setText(selectorTitle);
822
823 // Set the icon
Walter Jang82acd422015-10-17 14:01:27 -0700824 if (mPrimaryNameKindSectionData != null) {
825 final RawContactDelta rawContactDelta =
826 mPrimaryNameKindSectionData.first.getRawContactDelta();
827 if (rawContactDelta != null) {
828 final AccountType accountType =
829 rawContactDelta.getRawContactAccountType(getContext());
830 mAccountHeaderIcon.setImageDrawable(accountType.getDisplayIcon(getContext()));
831 }
832 }
Walter Jangbe5e1b12015-10-17 11:38:29 -0700833
834 // Set the content description
835 mAccountHeaderContainer.setContentDescription(
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700836 EditorUiUtils.getAccountInfoContentDescription(accountLabel,
837 selectorTitle));
Walter Jangbe5e1b12015-10-17 11:38:29 -0700838 }
839
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700840 private void addAccountSelector(final RawContactDelta rawContactDelta, CharSequence nameLabel) {
Gary Mai46cb3102016-08-10 18:14:09 -0700841 // Show save to default account.
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700842 addAccountHeader(nameLabel.toString());
Gary Mai46cb3102016-08-10 18:14:09 -0700843 // Add handlers for choosing another account to save to.
844 mAccountHeaderExpanderIcon.setVisibility(View.VISIBLE);
845 mAccountHeaderContainer.setOnClickListener(new View.OnClickListener() {
Walter Jang708ea9e2015-09-10 15:42:05 -0700846 @Override
847 public void onClick(View v) {
848 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
849 final AccountsListAdapter adapter =
850 new AccountsListAdapter(getContext(),
851 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE,
852 mPrimaryAccount);
Gary Mai46cb3102016-08-10 18:14:09 -0700853 popup.setWidth(mAccountHeaderContainer.getWidth());
854 popup.setAnchorView(mAccountHeaderContainer);
Walter Jang708ea9e2015-09-10 15:42:05 -0700855 popup.setAdapter(adapter);
856 popup.setModal(true);
857 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
858 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
859 @Override
860 public void onItemClick(AdapterView<?> parent, View view, int position,
861 long id) {
862 UiClosables.closeQuietly(popup);
863 final AccountWithDataSet newAccount = adapter.getItem(position);
864 if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
865 mListener.onRebindEditorsForNewContact(
Walter Jang82acd422015-10-17 14:01:27 -0700866 rawContactDelta,
Walter Jang708ea9e2015-09-10 15:42:05 -0700867 mPrimaryAccount,
868 newAccount);
869 }
870 }
871 });
872 popup.show();
873 }
874 });
875 }
876
Walter Jang23709542015-10-22 12:50:58 -0700877 private void addRawContactAccountSelector(String accountsSummary,
878 final RawContactAccountListAdapter adapter) {
Tingting Wang655ad1a2015-10-05 17:51:14 -0700879 mRawContactContainer.setVisibility(View.VISIBLE);
880
Tingting Wang655ad1a2015-10-05 17:51:14 -0700881 mRawContactSummary.setText(accountsSummary);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700882
883 mRawContactContainer.setOnClickListener(new View.OnClickListener() {
884 @Override
885 public void onClick(View v) {
886 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700887 popup.setWidth(mRawContactContainer.getWidth());
888 popup.setAnchorView(mRawContactContainer);
889 popup.setAdapter(adapter);
890 popup.setModal(true);
891 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
892 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
893 @Override
894 public void onItemClick(AdapterView<?> parent, View view, int position,
895 long id) {
896 UiClosables.closeQuietly(popup);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700897
898 if (mListener != null) {
Tingting Wang91cee282015-10-07 13:48:17 -0700899 final long rawContactId = adapter.getItemId(position);
900 final Uri rawContactUri = ContentUris.withAppendedId(
901 ContactsContract.RawContacts.CONTENT_URI, rawContactId);
902 final RawContactDelta rawContactDelta = adapter.getItem(position);
903 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
904 getContext());
905 final AccountType accountType = rawContactDelta.getAccountType(
906 accountTypes);
907 final boolean isReadOnly = !accountType.areContactsWritable();
908
909 mListener.onRawContactSelected(rawContactUri, rawContactId, isReadOnly);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700910 }
911 }
912 });
913 popup.show();
914 }
915 });
916 }
917
Walter Jang363d3fd2015-09-16 10:29:07 -0700918 private void addPhotoView() {
Walter Jang31a74ad2015-10-02 19:17:39 -0700919 // Get the kind section data and values delta that we will display in the photo view
Walter Jangcbd431d2015-10-06 13:07:30 -0700920 final KindSectionDataList kindSectionDataList =
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700921 getOrCreateKindSectionDataList(Photo.CONTENT_ITEM_TYPE);
Walter Jangcbd431d2015-10-06 13:07:30 -0700922 final Pair<KindSectionData,ValuesDelta> photoToDisplay =
923 kindSectionDataList.getEntryToDisplay(mPhotoId);
924 if (photoToDisplay == null) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700925 wlog("photo: no kind section data parsed");
Walter Jangcbd431d2015-10-06 13:07:30 -0700926 mPhotoView.setVisibility(View.GONE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700927 return;
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700928 } else {
929 mPhotoView.setVisibility(View.VISIBLE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700930 }
Walter Jang06f73a12015-06-17 11:15:48 -0700931
Walter Jang31a74ad2015-10-02 19:17:39 -0700932 // Set the photo view
Walter Jangcbd431d2015-10-06 13:07:30 -0700933 mPhotoView.setPhoto(photoToDisplay.second, mMaterialPalette);
Walter Jang31a74ad2015-10-02 19:17:39 -0700934
935 // Find the raw contact ID and values delta that will be written when the photo is edited
Walter Jangda258ff2015-11-17 11:06:02 -0800936 final Pair<KindSectionData, ValuesDelta> photoToWrite = kindSectionDataList.getEntryToWrite(
Walter Jang82acd422015-10-17 14:01:27 -0700937 mPhotoId, mPrimaryAccount, mIsUserProfile);
Walter Jangda258ff2015-11-17 11:06:02 -0800938 if (photoToWrite == null) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700939 mPhotoView.setReadOnly(true);
940 return;
941 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700942 mPhotoView.setReadOnly(false);
Walter Jangda258ff2015-11-17 11:06:02 -0800943 mPhotoRawContactId = photoToWrite.first.getRawContactDelta().getRawContactId();
944 mPhotoValuesDelta = photoToWrite.second;
Walter Jang31a74ad2015-10-02 19:17:39 -0700945 }
946
Walter Jangf5dfea42015-09-16 12:30:36 -0700947 private void addKindSectionViews() {
948 // Sort the kinds
Walter Jangcbd431d2015-10-06 13:07:30 -0700949 final TreeSet<Map.Entry<String,KindSectionDataList>> entries =
Walter Jang192a01c2015-09-22 15:23:55 -0700950 new TreeSet<>(KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR);
Walter Jangf5dfea42015-09-16 12:30:36 -0700951 entries.addAll(mKindSectionDataMap.entrySet());
Walter Jang3efae4a2015-02-18 11:12:00 -0800952
Walter Jangf5dfea42015-09-16 12:30:36 -0700953 vlog("kind: " + entries.size() + " kindSection(s)");
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700954 int i = -1;
Walter Jangcbd431d2015-10-06 13:07:30 -0700955 for (Map.Entry<String, KindSectionDataList> entry : entries) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700956 i++;
957
Walter Jangf5dfea42015-09-16 12:30:36 -0700958 final String mimeType = entry.getKey();
Walter Jang82acd422015-10-17 14:01:27 -0700959
Tingting Wangf695eb32015-10-15 18:45:15 -0700960 if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang82acd422015-10-17 14:01:27 -0700961 if (mPrimaryNameKindSectionData == null) {
962 vlog("kind: " + i + " " + mimeType + " dropped");
963 continue;
Tingting Wangf695eb32015-10-15 18:45:15 -0700964 }
Walter Jang82acd422015-10-17 14:01:27 -0700965 vlog("kind: " + i + " " + mimeType + " using first entry only");
966 final KindSectionDataList kindSectionDataList = new KindSectionDataList();
967 kindSectionDataList.add(mPrimaryNameKindSectionData.first);
Walter Jangf5dfea42015-09-16 12:30:36 -0700968 final CompactKindSectionView kindSectionView = inflateKindSectionView(
Walter Jang82acd422015-10-17 14:01:27 -0700969 mKindSectionViews, kindSectionDataList, mimeType,
970 mPrimaryNameKindSectionData.second);
Walter Jangf5dfea42015-09-16 12:30:36 -0700971 mKindSectionViews.addView(kindSectionView);
Walter Jangf10ca152015-09-22 15:23:55 -0700972
973 // Keep a pointer to all the KindSectionsViews for each mimeType
974 getKindSectionViews(mimeType).add(kindSectionView);
Walter Jang82acd422015-10-17 14:01:27 -0700975 } else {
976 final KindSectionDataList kindSectionDataList = entry.getValue();
977
978 // Ignore mime types that we've already handled
979 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
980 vlog("kind: " + i + " " + mimeType + " dropped");
981 continue;
982 }
983
984 // Don't show more than one group editor on the compact editor.
985 // Groups will still be editable for each raw contact individually on the full editor.
986 if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)
987 && kindSectionDataList.size() > 1) {
988 vlog("kind: " + i + " " + mimeType + " dropped");
989 continue;
990 }
991
992 if (kindSectionDataList != null && !kindSectionDataList.isEmpty()) {
993 vlog("kind: " + i + " " + mimeType + " " + kindSectionDataList.size() +
994 " kindSectionData(s)");
995
996 final CompactKindSectionView kindSectionView = inflateKindSectionView(
997 mKindSectionViews, kindSectionDataList, mimeType,
998 /* primaryValueDelta =*/ null);
999 mKindSectionViews.addView(kindSectionView);
1000
1001 // Keep a pointer to all the KindSectionsViews for each mimeType
1002 getKindSectionViews(mimeType).add(kindSectionView);
1003 }
Walter Jangcab3dce2015-02-09 17:48:03 -08001004 }
1005 }
1006 }
1007
Walter Jangf10ca152015-09-22 15:23:55 -07001008 private List<CompactKindSectionView> getKindSectionViews(String mimeType) {
1009 List<CompactKindSectionView> kindSectionViews = mKindSectionViewsMap.get(mimeType);
1010 if (kindSectionViews == null) {
1011 kindSectionViews = new ArrayList<>();
1012 mKindSectionViewsMap.put(mimeType, kindSectionViews);
1013 }
1014 return kindSectionViews;
1015 }
1016
Walter Jangf5dfea42015-09-16 12:30:36 -07001017 private CompactKindSectionView inflateKindSectionView(ViewGroup viewGroup,
Walter Jang82acd422015-10-17 14:01:27 -07001018 KindSectionDataList kindSectionDataList, String mimeType,
1019 ValuesDelta primaryValuesDelta) {
Walter Jangf5dfea42015-09-16 12:30:36 -07001020 final CompactKindSectionView kindSectionView = (CompactKindSectionView)
1021 mLayoutInflater.inflate(R.layout.compact_item_kind_section, viewGroup,
1022 /* attachToRoot =*/ false);
Walter Jang45b86d52015-10-15 15:23:16 -07001023 kindSectionView.setIsUserProfile(mIsUserProfile);
Walter Jangf5dfea42015-09-16 12:30:36 -07001024
Walter Jang192a01c2015-09-22 15:23:55 -07001025 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangf5dfea42015-09-16 12:30:36 -07001026 || Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -07001027 // Phone numbers and email addresses are always displayed,
1028 // even if they are empty
Walter Jangf5dfea42015-09-16 12:30:36 -07001029 kindSectionView.setHideWhenEmpty(false);
1030 }
Walter Jangf5dfea42015-09-16 12:30:36 -07001031
Walter Jang192a01c2015-09-22 15:23:55 -07001032 // Since phone numbers and email addresses displayed even if they are empty,
1033 // they will be the only types you add new values to initially for new contacts
1034 kindSectionView.setShowOneEmptyEditor(true);
1035
Walter Jang82acd422015-10-17 14:01:27 -07001036 // Sort non-name editors so they wind up in the order we want
1037 if (!StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -07001038 Collections.sort(kindSectionDataList, new EditorComparator(getContext()));
Walter Jang3e5ae0d2015-09-20 12:43:37 -07001039 }
1040
Walter Jang82acd422015-10-17 14:01:27 -07001041 kindSectionView.setState(kindSectionDataList, mViewIdGenerator, mListener,
1042 primaryValuesDelta);
Walter Jangf5dfea42015-09-16 12:30:36 -07001043
1044 return kindSectionView;
Walter Jangcab3dce2015-02-09 17:48:03 -08001045 }
1046
Walter Jange3945952015-10-27 12:44:54 -07001047 void maybeSetReadOnlyDisplayNameAsPrimary(String readOnlyDisplayName) {
1048 if (TextUtils.isEmpty(readOnlyDisplayName)) return;
1049 final CompactKindSectionView primaryNameKindSectionView = getPrimaryNameKindSectionView();
1050 if (primaryNameKindSectionView != null && primaryNameKindSectionView.isEmptyName()) {
1051 vlog("name: using read only display name as primary name");
1052 primaryNameKindSectionView.setName(readOnlyDisplayName);
1053 }
1054 }
1055
1056 private CompactKindSectionView getPrimaryNameKindSectionView() {
Walter Jang4f5594a2015-10-06 18:40:31 -07001057 final List<CompactKindSectionView> kindSectionViews
1058 = mKindSectionViewsMap.get(StructuredName.CONTENT_ITEM_TYPE);
Walter Jange3945952015-10-27 12:44:54 -07001059 return kindSectionViews == null || kindSectionViews.isEmpty()
1060 ? null : kindSectionViews.get(0);
Walter Jang4f5594a2015-10-06 18:40:31 -07001061 }
1062
Walter Jangd6753152015-10-02 09:23:13 -07001063 private void showAllFields() {
Walter Jang79658e12015-09-24 10:36:26 -07001064 // Stop hiding empty editors and allow the user to enter values for all kinds now
1065 for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
1066 final CompactKindSectionView kindSectionView =
1067 (CompactKindSectionView) mKindSectionViews.getChildAt(i);
1068 kindSectionView.setHideWhenEmpty(false);
1069 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
1070 }
1071 mIsExpanded = true;
Walter Jang79658e12015-09-24 10:36:26 -07001072
Walter Jangf5dfea42015-09-16 12:30:36 -07001073 // Hide the more fields button
1074 mMoreFields.setVisibility(View.GONE);
Walter Jangcab3dce2015-02-09 17:48:03 -08001075 }
1076
Marcus Hagerottcf7f2952016-09-06 13:49:17 -07001077 private boolean hasMoreFields() {
1078 for (List<CompactKindSectionView> sections : mKindSectionViewsMap.values()) {
1079 for (CompactKindSectionView section : sections) {
1080 if (section.getVisibility() != View.VISIBLE) {
1081 return true;
1082 }
1083 }
1084 }
1085 return false;
1086 }
1087
Walter Jangbf63a6d2015-05-05 09:14:35 -07001088 private static void vlog(String message) {
1089 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1090 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -08001091 }
1092 }
Walter Jang363d3fd2015-09-16 10:29:07 -07001093
1094 private static void wlog(String message) {
1095 if (Log.isLoggable(TAG, Log.WARN)) {
1096 Log.w(TAG, message);
1097 }
1098 }
1099
1100 private static void elog(String message) {
1101 Log.e(TAG, message);
1102 }
Walter Jangcab3dce2015-02-09 17:48:03 -08001103}