blob: 40f670a76c093b42338b42c9e490d7eb79b63e73 [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;
Gary Mai9fa89872016-09-19 15:29:59 -070066import com.android.contacts.common.model.account.DeviceLocalAccountType;
67import com.android.contacts.common.model.account.SimAccountType;
Gary Maib6e28d22016-09-12 14:04:53 -070068import com.android.contacts.common.model.dataitem.DataKind;
69import com.android.contacts.common.util.AccountsListAdapter;
70import com.android.contacts.common.util.MaterialColorMapUtils;
71import com.android.contacts.util.UiClosables;
72
Walter Jang708774a2015-10-16 09:32:06 -070073import java.io.FileNotFoundException;
Walter Jangcab3dce2015-02-09 17:48:03 -080074import java.util.ArrayList;
Walter Jangf5dfea42015-09-16 12:30:36 -070075import java.util.Arrays;
Walter Jang192a01c2015-09-22 15:23:55 -070076import java.util.Collections;
Walter Jangf5dfea42015-09-16 12:30:36 -070077import java.util.Comparator;
Walter Jang19d985f2015-07-01 13:36:27 -070078import java.util.HashMap;
Walter Jangcab3dce2015-02-09 17:48:03 -080079import java.util.List;
Walter Jang19d985f2015-07-01 13:36:27 -070080import java.util.Map;
Walter Jangf5dfea42015-09-16 12:30:36 -070081import java.util.TreeSet;
Walter Jangcab3dce2015-02-09 17:48:03 -080082
83/**
Walter Jangf5dfea42015-09-16 12:30:36 -070084 * View to display information from multiple {@link RawContactDelta}s grouped together.
Walter Jangcab3dce2015-02-09 17:48:03 -080085 */
Walter Jangb6ca2722015-02-20 11:10:25 -080086public class CompactRawContactsEditorView extends LinearLayout implements View.OnClickListener {
Walter Jangcab3dce2015-02-09 17:48:03 -080087
Walter Jang4f5594a2015-10-06 18:40:31 -070088 static final String TAG = "CompactEditorView";
Walter Jangcab3dce2015-02-09 17:48:03 -080089
Walter Jang192a01c2015-09-22 15:23:55 -070090 private static final KindSectionDataMapEntryComparator
91 KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR = new KindSectionDataMapEntryComparator();
Walter Jangf5dfea42015-09-16 12:30:36 -070092
Walter Jangb6ca2722015-02-20 11:10:25 -080093 /**
94 * Callbacks for hosts of {@link CompactRawContactsEditorView}s.
95 */
96 public interface Listener {
97
98 /**
Walter Jang151f3e62015-02-26 15:29:40 -080099 * Invoked when the structured name editor field has changed.
100 *
101 * @param rawContactId The raw contact ID from the underlying {@link RawContactDelta}.
102 * @param valuesDelta The values from the underlying {@link RawContactDelta}.
103 */
104 public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta);
Walter Jang708ea9e2015-09-10 15:42:05 -0700105
106 /**
107 * Invoked when the compact editor should rebind editors for a new account.
108 *
109 * @param oldState Old data being edited.
110 * @param oldAccount Old account associated with oldState.
111 * @param newAccount New account to be used.
112 */
113 public void onRebindEditorsForNewContact(RawContactDelta oldState,
114 AccountWithDataSet oldAccount, AccountWithDataSet newAccount);
Walter Jang79658e12015-09-24 10:36:26 -0700115
116 /**
117 * Invoked when no editors could be bound for the contact.
118 */
119 public void onBindEditorsFailed();
Walter Jangd6753152015-10-02 09:23:13 -0700120
121 /**
122 * Invoked after editors have been bound for the contact.
123 */
124 public void onEditorsBound();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700125
126 /**
Tingting Wang5585c6e2015-10-14 11:19:58 -0700127 * Invoked when a rawcontact from linked contacts is selected in editor.
Tingting Wang655ad1a2015-10-05 17:51:14 -0700128 */
Tingting Wang91cee282015-10-07 13:48:17 -0700129 public void onRawContactSelected(Uri uri, long rawContactId, boolean isReadOnly);
Walter Jang3f18d612015-10-07 16:01:05 -0700130
131 /**
132 * Returns the map of raw contact IDs to newly taken or selected photos that have not
133 * yet been saved to CP2.
134 */
135 public Bundle getUpdatedPhotos();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700136 }
137
138 /**
139 * Used to list the account info for the given raw contacts list.
140 */
141 private static final class RawContactAccountListAdapter extends BaseAdapter {
142 private final LayoutInflater mInflater;
143 private final Context mContext;
144 private final RawContactDeltaList mRawContactDeltas;
145
146 public RawContactAccountListAdapter(Context context, RawContactDeltaList rawContactDeltas) {
147 mContext = context;
148 mRawContactDeltas = new RawContactDeltaList();
149 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang23709542015-10-22 12:50:58 -0700150 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
Tingting Wang655ad1a2015-10-05 17:51:14 -0700151 mRawContactDeltas.add(rawContactDelta);
152 }
153 }
154 mInflater = LayoutInflater.from(context);
155 }
156
157 @Override
158 public View getView(int position, View convertView, ViewGroup parent) {
159 final View resultView = convertView != null ? convertView
160 : mInflater.inflate(R.layout.account_selector_list_item, parent, false);
161
162 final RawContactDelta rawContactDelta = mRawContactDeltas.get(position);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700163
164 final TextView text1 = (TextView) resultView.findViewById(android.R.id.text1);
Walter Jang9488a762015-10-16 13:42:48 -0700165 final AccountType accountType = rawContactDelta.getRawContactAccountType(mContext);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700166 text1.setText(accountType.getDisplayLabel(mContext));
167
Tingting Wang655ad1a2015-10-05 17:51:14 -0700168 final TextView text2 = (TextView) resultView.findViewById(android.R.id.text2);
Walter Jang9488a762015-10-16 13:42:48 -0700169 final String accountName = rawContactDelta.getAccountName();
Gary Mai9fa89872016-09-19 15:29:59 -0700170 if (TextUtils.isEmpty(accountName) || accountType instanceof DeviceLocalAccountType
171 || accountType instanceof SimAccountType) {
Tingting Wanga0b69402015-10-13 14:27:58 -0700172 text2.setVisibility(View.GONE);
173 } else {
Walter Jang9488a762015-10-16 13:42:48 -0700174 // Truncate email addresses in the middle so we don't lose the domain
Tingting Wanga0b69402015-10-13 14:27:58 -0700175 text2.setText(accountName);
176 text2.setEllipsize(TextUtils.TruncateAt.MIDDLE);
177 }
Tingting Wang655ad1a2015-10-05 17:51:14 -0700178
179 final ImageView icon = (ImageView) resultView.findViewById(android.R.id.icon);
180 icon.setImageDrawable(accountType.getDisplayIcon(mContext));
181
182 return resultView;
183 }
184
185 @Override
186 public int getCount() {
187 return mRawContactDeltas.size();
188 }
189
190 @Override
191 public RawContactDelta getItem(int position) {
192 return mRawContactDeltas.get(position);
193 }
194
195 @Override
196 public long getItemId(int position) {
197 return getItem(position).getRawContactId();
198 }
Walter Jang151f3e62015-02-26 15:29:40 -0800199 }
200
Walter Jang192a01c2015-09-22 15:23:55 -0700201 /** Used to sort entire kind sections. */
202 private static final class KindSectionDataMapEntryComparator implements
Walter Jangcbd431d2015-10-06 13:07:30 -0700203 Comparator<Map.Entry<String,KindSectionDataList>> {
Walter Jangf5dfea42015-09-16 12:30:36 -0700204
Walter Jang192a01c2015-09-22 15:23:55 -0700205 final MimeTypeComparator mMimeTypeComparator = new MimeTypeComparator();
206
207 @Override
Walter Jangcbd431d2015-10-06 13:07:30 -0700208 public int compare(Map.Entry<String, KindSectionDataList> entry1,
209 Map.Entry<String, KindSectionDataList> entry2) {
Walter Jang192a01c2015-09-22 15:23:55 -0700210 if (entry1 == entry2) return 0;
211 if (entry1 == null) return -1;
212 if (entry2 == null) return 1;
213
214 final String mimeType1 = entry1.getKey();
215 final String mimeType2 = entry2.getKey();
216
217 return mMimeTypeComparator.compare(mimeType1, mimeType2);
218 }
219 }
220
221 /**
222 * Sorts kinds roughly the same as quick contacts; we diverge in the following ways:
223 * <ol>
224 * <li>All names are together at the top.</li>
225 * <li>IM is moved up after addresses</li>
226 * <li>SIP addresses are moved to below phone numbers</li>
Walter Jang79658e12015-09-24 10:36:26 -0700227 * <li>Group membership is placed at the end</li>
Walter Jang192a01c2015-09-22 15:23:55 -0700228 * </ol>
229 */
230 private static final class MimeTypeComparator implements Comparator<String> {
231
Walter Jangf5dfea42015-09-16 12:30:36 -0700232 private static final List<String> MIME_TYPE_ORDER = Arrays.asList(new String[] {
233 StructuredName.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700234 Nickname.CONTENT_ITEM_TYPE,
Walter Jang2ae21752015-09-25 11:25:38 -0700235 Organization.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700236 Phone.CONTENT_ITEM_TYPE,
237 SipAddress.CONTENT_ITEM_TYPE,
238 Email.CONTENT_ITEM_TYPE,
239 StructuredPostal.CONTENT_ITEM_TYPE,
240 Im.CONTENT_ITEM_TYPE,
241 Website.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700242 Event.CONTENT_ITEM_TYPE,
243 Relation.CONTENT_ITEM_TYPE,
Walter Jangf10ca152015-09-22 15:23:55 -0700244 Note.CONTENT_ITEM_TYPE,
245 GroupMembership.CONTENT_ITEM_TYPE
Walter Jangf5dfea42015-09-16 12:30:36 -0700246 });
247
248 @Override
Walter Jang192a01c2015-09-22 15:23:55 -0700249 public int compare(String mimeType1, String mimeType2) {
250 if (mimeType1 == mimeType2) return 0;
251 if (mimeType1 == null) return -1;
252 if (mimeType2 == null) return 1;
Walter Jangf5dfea42015-09-16 12:30:36 -0700253
254 int index1 = MIME_TYPE_ORDER.indexOf(mimeType1);
255 int index2 = MIME_TYPE_ORDER.indexOf(mimeType2);
256
257 // Fallback to alphabetical ordering of the mime type if both are not found
258 if (index1 < 0 && index2 < 0) return mimeType1.compareTo(mimeType2);
259 if (index1 < 0) return 1;
260 if (index2 < 0) return -1;
261
262 return index1 < index2 ? -1 : 1;
263 }
264 }
265
Walter Jang192a01c2015-09-22 15:23:55 -0700266 /**
267 * Sorts primary accounts and google account types before others.
268 */
269 private static final class EditorComparator implements Comparator<KindSectionData> {
270
271 private RawContactDeltaComparator mRawContactDeltaComparator;
272
273 private EditorComparator(Context context) {
274 mRawContactDeltaComparator = new RawContactDeltaComparator(context);
275 }
276
277 @Override
278 public int compare(KindSectionData kindSectionData1, KindSectionData kindSectionData2) {
279 if (kindSectionData1 == kindSectionData2) return 0;
280 if (kindSectionData1 == null) return -1;
281 if (kindSectionData2 == null) return 1;
282
283 final RawContactDelta rawContactDelta1 = kindSectionData1.getRawContactDelta();
284 final RawContactDelta rawContactDelta2 = kindSectionData2.getRawContactDelta();
285
286 if (rawContactDelta1 == rawContactDelta2) return 0;
287 if (rawContactDelta1 == null) return -1;
288 if (rawContactDelta2 == null) return 1;
289
290 return mRawContactDeltaComparator.compare(rawContactDelta1, rawContactDelta2);
291 }
292 }
293
Walter Jang79658e12015-09-24 10:36:26 -0700294 public static class SavedState extends BaseSavedState {
295
296 public static final Parcelable.Creator<SavedState> CREATOR =
297 new Parcelable.Creator<SavedState>() {
298 public SavedState createFromParcel(Parcel in) {
299 return new SavedState(in);
300 }
301 public SavedState[] newArray(int size) {
302 return new SavedState[size];
303 }
304 };
305
306 private boolean mIsExpanded;
307
308 public SavedState(Parcelable superState) {
309 super(superState);
310 }
311
312 private SavedState(Parcel in) {
313 super(in);
314 mIsExpanded = in.readInt() != 0;
315 }
316
317 @Override
318 public void writeToParcel(Parcel out, int flags) {
319 super.writeToParcel(out, flags);
320 out.writeInt(mIsExpanded ? 1 : 0);
321 }
322 }
323
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700324 private CompactRawContactsEditorView.Listener mListener;
Walter Jangb6ca2722015-02-20 11:10:25 -0800325
Walter Jangcab3dce2015-02-09 17:48:03 -0800326 private AccountTypeManager mAccountTypeManager;
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700327 private AccountDisplayInfoFactory mAccountDisplayInfoFactory;
Walter Jangcab3dce2015-02-09 17:48:03 -0800328 private LayoutInflater mLayoutInflater;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800329
Walter Jangcab3dce2015-02-09 17:48:03 -0800330 private ViewIdGenerator mViewIdGenerator;
Walter Jangf46abd82015-02-20 16:52:04 -0800331 private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
Walter Jang82acd422015-10-17 14:01:27 -0700332 private long mPhotoId = -1;
Walter Jang708ea9e2015-09-10 15:42:05 -0700333 private boolean mHasNewContact;
334 private boolean mIsUserProfile;
335 private AccountWithDataSet mPrimaryAccount;
Walter Jangcbd431d2015-10-06 13:07:30 -0700336 private Map<String,KindSectionDataList> mKindSectionDataMap = new HashMap<>();
Walter Jangcab3dce2015-02-09 17:48:03 -0800337
Walter Jang708ea9e2015-09-10 15:42:05 -0700338 // Account header
339 private View mAccountHeaderContainer;
340 private TextView mAccountHeaderType;
341 private TextView mAccountHeaderName;
Tingting Wang91cee282015-10-07 13:48:17 -0700342 private ImageView mAccountHeaderIcon;
Gary Mai46cb3102016-08-10 18:14:09 -0700343 private ImageView mAccountHeaderExpanderIcon;
Walter Jang708ea9e2015-09-10 15:42:05 -0700344
Tingting Wang655ad1a2015-10-05 17:51:14 -0700345 // Raw contacts selector
346 private View mRawContactContainer;
347 private TextView mRawContactSummary;
Tingting Wang655ad1a2015-10-05 17:51:14 -0700348
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700349 private CompactPhotoEditorView mPhotoView;
Walter Jangf5dfea42015-09-16 12:30:36 -0700350 private ViewGroup mKindSectionViews;
Walter Jangf10ca152015-09-22 15:23:55 -0700351 private Map<String,List<CompactKindSectionView>> mKindSectionViewsMap = new HashMap<>();
Walter Jangb6ca2722015-02-20 11:10:25 -0800352 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -0800353
Walter Jang79658e12015-09-24 10:36:26 -0700354 private boolean mIsExpanded;
Walter Jangcbd431d2015-10-06 13:07:30 -0700355
Walter Jang3efae4a2015-02-18 11:12:00 -0800356 private long mPhotoRawContactId;
Walter Jang31a74ad2015-10-02 19:17:39 -0700357 private ValuesDelta mPhotoValuesDelta;
Walter Jang82acd422015-10-17 14:01:27 -0700358
359 private Pair<KindSectionData, ValuesDelta> mPrimaryNameKindSectionData;
Walter Jang3efae4a2015-02-18 11:12:00 -0800360
Walter Jangcab3dce2015-02-09 17:48:03 -0800361 public CompactRawContactsEditorView(Context context) {
362 super(context);
363 }
364
365 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
366 super(context, attrs);
367 }
368
Walter Jangb6ca2722015-02-20 11:10:25 -0800369 /**
370 * Sets the receiver for {@link CompactRawContactsEditorView} callbacks.
371 */
372 public void setListener(Listener listener) {
373 mListener = listener;
374 }
375
Walter Jangcab3dce2015-02-09 17:48:03 -0800376 @Override
377 protected void onFinishInflate() {
378 super.onFinishInflate();
379
380 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700381 mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forWritableAccounts(getContext());
Walter Jangcab3dce2015-02-09 17:48:03 -0800382 mLayoutInflater = (LayoutInflater)
383 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
384
Walter Jang708ea9e2015-09-10 15:42:05 -0700385 // Account header
Gary Mai46cb3102016-08-10 18:14:09 -0700386 mAccountHeaderContainer = findViewById(R.id.account_header_container);
Walter Jang708ea9e2015-09-10 15:42:05 -0700387 mAccountHeaderType = (TextView) findViewById(R.id.account_type);
388 mAccountHeaderName = (TextView) findViewById(R.id.account_name);
Tingting Wang91cee282015-10-07 13:48:17 -0700389 mAccountHeaderIcon = (ImageView) findViewById(R.id.account_type_icon);
Gary Mai46cb3102016-08-10 18:14:09 -0700390 mAccountHeaderExpanderIcon = (ImageView) findViewById(R.id.account_expander_icon);
Walter Jang708ea9e2015-09-10 15:42:05 -0700391
Tingting Wang655ad1a2015-10-05 17:51:14 -0700392 // Raw contacts selector
393 mRawContactContainer = findViewById(R.id.all_rawcontacts_accounts_container);
394 mRawContactSummary = (TextView) findViewById(R.id.rawcontacts_accounts_summary);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700395
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700396 mPhotoView = (CompactPhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangf5dfea42015-09-16 12:30:36 -0700397 mKindSectionViews = (LinearLayout) findViewById(R.id.kind_section_views);
Walter Jangb6ca2722015-02-20 11:10:25 -0800398 mMoreFields = findViewById(R.id.more_fields);
399 mMoreFields.setOnClickListener(this);
400 }
401
Walter Jangb6ca2722015-02-20 11:10:25 -0800402 @Override
403 public void onClick(View view) {
Walter Jangf5dfea42015-09-16 12:30:36 -0700404 if (view.getId() == R.id.more_fields) {
Walter Jangd6753152015-10-02 09:23:13 -0700405 showAllFields();
Walter Jangb6ca2722015-02-20 11:10:25 -0800406 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800407 }
408
409 @Override
410 public void setEnabled(boolean enabled) {
411 super.setEnabled(enabled);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700412 final int childCount = mKindSectionViews.getChildCount();
413 for (int i = 0; i < childCount; i++) {
414 mKindSectionViews.getChildAt(i).setEnabled(enabled);
Walter Jangcab3dce2015-02-09 17:48:03 -0800415 }
416 }
417
Walter Jang79658e12015-09-24 10:36:26 -0700418 @Override
419 public Parcelable onSaveInstanceState() {
420 final Parcelable superState = super.onSaveInstanceState();
421 final SavedState savedState = new SavedState(superState);
422 savedState.mIsExpanded = mIsExpanded;
423 return savedState;
424 }
425
426 @Override
427 public void onRestoreInstanceState(Parcelable state) {
428 if(!(state instanceof SavedState)) {
429 super.onRestoreInstanceState(state);
430 return;
431 }
432 final SavedState savedState = (SavedState) state;
433 super.onRestoreInstanceState(savedState.getSuperState());
434 mIsExpanded = savedState.mIsExpanded;
Walter Jangd6753152015-10-02 09:23:13 -0700435 if (mIsExpanded) {
436 showAllFields();
Walter Jang79658e12015-09-24 10:36:26 -0700437 }
438 }
439
Walter Jang3efae4a2015-02-18 11:12:00 -0800440 /**
Walter Jang31a74ad2015-10-02 19:17:39 -0700441 * Pass through to {@link CompactPhotoEditorView#setListener}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800442 */
Walter Jang31a74ad2015-10-02 19:17:39 -0700443 public void setPhotoListener(CompactPhotoEditorView.Listener listener) {
444 mPhotoView.setListener(listener);
Walter Jang3efae4a2015-02-18 11:12:00 -0800445 }
446
Walter Jang31a74ad2015-10-02 19:17:39 -0700447 public void removePhoto() {
Walter Jang45b86d52015-10-15 15:23:16 -0700448 mPhotoValuesDelta.setFromTemplate(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700449 mPhotoValuesDelta.put(Photo.PHOTO, (byte[]) null);
450
451 mPhotoView.removePhoto();
Walter Jang3efae4a2015-02-18 11:12:00 -0800452 }
453
454 /**
Walter Jang41b3ea12015-03-09 17:30:06 -0700455 * Pass through to {@link CompactPhotoEditorView#setFullSizedPhoto(Uri)}.
456 */
457 public void setFullSizePhoto(Uri photoUri) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700458 mPhotoView.setFullSizedPhoto(photoUri);
Walter Jang41b3ea12015-03-09 17:30:06 -0700459 }
460
Walter Jang31a74ad2015-10-02 19:17:39 -0700461 public void updatePhoto(Uri photoUri) {
Walter Jang45b86d52015-10-15 15:23:16 -0700462 mPhotoValuesDelta.setFromTemplate(false);
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700463 // Unset primary for all photos
Wenyi Wang9086fb92015-11-16 09:58:20 -0800464 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700465 // Mark the currently displayed photo as primary
466 mPhotoValuesDelta.setSuperPrimary(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700467
Wenyi Wang4c9cf3a2015-10-15 17:58:23 -0700468 // Even though high-res photos cannot be saved by passing them via
469 // an EntityDeltaList (since they cause the Bundle size limit to be
470 // exceeded), we still pass a low-res thumbnail. This simplifies
471 // code all over the place, because we don't have to test whether
472 // there is a change in EITHER the delta-list OR a changed photo...
473 // this way, there is always a change in the delta-list.
474 try {
475 final byte[] bytes = EditorUiUtils.getCompressedThumbnailBitmapBytes(
476 getContext(), photoUri);
477 if (bytes != null) {
478 mPhotoValuesDelta.setPhoto(bytes);
479 }
480 } catch (FileNotFoundException e) {
481 elog("Failed to get bitmap from photo Uri");
482 }
483
Walter Jang31a74ad2015-10-02 19:17:39 -0700484 mPhotoView.setFullSizedPhoto(photoUri);
485 }
486
Wenyi Wang9086fb92015-11-16 09:58:20 -0800487 private void unsetSuperPrimaryFromAllPhotos() {
Walter Jang3f18d612015-10-07 16:01:05 -0700488 final List<KindSectionData> kindSectionDataList =
489 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
490 for (KindSectionData kindSectionData : kindSectionDataList) {
Wenyi Wang9086fb92015-11-16 09:58:20 -0800491 for (ValuesDelta valuesDelta : kindSectionData.getNonEmptyValuesDeltas()) {
492 valuesDelta.setSuperPrimary(false);
Walter Jang3f18d612015-10-07 16:01:05 -0700493 }
494 }
495 }
496
Walter Jang41b3ea12015-03-09 17:30:06 -0700497 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800498 * Pass through to {@link CompactPhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800499 */
500 public boolean isWritablePhotoSet() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700501 return mPhotoView.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800502 }
503
504 /**
Walter Jang3efae4a2015-02-18 11:12:00 -0800505 * Get the raw contact ID for the CompactHeaderView photo.
506 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800507 public long getPhotoRawContactId() {
508 return mPhotoRawContactId;
509 }
510
Walter Jang4f5594a2015-10-06 18:40:31 -0700511 public StructuredNameEditorView getPrimaryNameEditorView() {
Walter Jange3945952015-10-27 12:44:54 -0700512 final CompactKindSectionView primaryNameKindSectionView = getPrimaryNameKindSectionView();
513 return primaryNameKindSectionView == null
514 ? null : primaryNameKindSectionView.getPrimaryNameEditorView();
Walter Jang4f5594a2015-10-06 18:40:31 -0700515 }
516
Walter Jang31a74ad2015-10-02 19:17:39 -0700517 /**
518 * Returns a data holder for every non-default/non-empty photo from each raw contact, whether
519 * the raw contact is writable or not.
520 */
521 public ArrayList<CompactPhotoSelectionFragment.Photo> getPhotos() {
522 final ArrayList<CompactPhotoSelectionFragment.Photo> photos = new ArrayList<>();
523
Walter Jang3f18d612015-10-07 16:01:05 -0700524 final Bundle updatedPhotos = mListener == null ? null : mListener.getUpdatedPhotos();
525
Walter Jang31a74ad2015-10-02 19:17:39 -0700526 final List<KindSectionData> kindSectionDataList =
527 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
528 for (int i = 0; i < kindSectionDataList.size(); i++) {
529 final KindSectionData kindSectionData = kindSectionDataList.get(i);
530 final AccountType accountType = kindSectionData.getAccountType();
Walter Jang228e02f2015-11-13 09:11:01 -0800531 final List<ValuesDelta> valuesDeltas = kindSectionData.getNonEmptyValuesDeltas();
532 if (valuesDeltas.isEmpty()) continue;
533 for (int j = 0; j < valuesDeltas.size(); j++) {
534 final ValuesDelta valuesDelta = valuesDeltas.get(j);
Walter Jang31a74ad2015-10-02 19:17:39 -0700535 final Bitmap bitmap = EditorUiUtils.getPhotoBitmap(valuesDelta);
536 if (bitmap == null) continue;
537
538 final CompactPhotoSelectionFragment.Photo photo =
539 new CompactPhotoSelectionFragment.Photo();
540 photo.titleRes = accountType.titleRes;
541 photo.iconRes = accountType.iconRes;
542 photo.syncAdapterPackageName = accountType.syncAdapterPackageName;
543 photo.valuesDelta = valuesDelta;
544 photo.primary = valuesDelta.isSuperPrimary();
545 photo.kindSectionDataListIndex = i;
546 photo.valuesDeltaListIndex = j;
Walter Jangda258ff2015-11-17 11:06:02 -0800547 photo.photoId = valuesDelta.getId();
Walter Jang3f18d612015-10-07 16:01:05 -0700548
549 if (updatedPhotos != null) {
550 photo.updatedPhotoUri = (Uri) updatedPhotos.get(String.valueOf(
551 kindSectionData.getRawContactDelta().getRawContactId()));
552 }
553
Wenyi Wangd375c352016-03-28 15:12:45 -0700554 final CharSequence accountTypeLabel = accountType.getDisplayLabel(getContext());
555 photo.accountType = accountTypeLabel == null ? "" : accountTypeLabel.toString();
556 final String accountName = kindSectionData.getRawContactDelta().getAccountName();
557 photo.accountName = accountName == null ? "" : accountName;
Wenyi Wangab360172015-12-15 10:35:58 -0800558
Walter Jang31a74ad2015-10-02 19:17:39 -0700559 photos.add(photo);
560 }
561 }
562
563 return photos;
564 }
565
566 /**
567 * Marks the raw contact photo given as primary for the aggregate contact and updates the
568 * UI.
569 */
570 public void setPrimaryPhoto(CompactPhotoSelectionFragment.Photo photo) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700571 // Find the values delta to mark as primary
Walter Jang3f18d612015-10-07 16:01:05 -0700572 final KindSectionDataList kindSectionDataList =
573 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
Walter Jang31a74ad2015-10-02 19:17:39 -0700574 if (photo.kindSectionDataListIndex < 0
575 || photo.kindSectionDataListIndex >= kindSectionDataList.size()) {
576 wlog("Invalid kind section data list index");
577 return;
578 }
579 final KindSectionData kindSectionData =
580 kindSectionDataList.get(photo.kindSectionDataListIndex);
Walter Jang228e02f2015-11-13 09:11:01 -0800581 final List<ValuesDelta> valuesDeltaList = kindSectionData.getNonEmptyValuesDeltas();
Walter Jang31a74ad2015-10-02 19:17:39 -0700582 if (photo.valuesDeltaListIndex >= valuesDeltaList.size()) {
583 wlog("Invalid values delta list index");
584 return;
585 }
Walter Jangda258ff2015-11-17 11:06:02 -0800586
587 // Update values delta
Walter Jang31a74ad2015-10-02 19:17:39 -0700588 final ValuesDelta valuesDelta = valuesDeltaList.get(photo.valuesDeltaListIndex);
589 valuesDelta.setFromTemplate(false);
Wenyi Wang9086fb92015-11-16 09:58:20 -0800590 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700591 valuesDelta.setSuperPrimary(true);
Walter Jangda258ff2015-11-17 11:06:02 -0800592
Walter Jang31a74ad2015-10-02 19:17:39 -0700593 // Update the UI
594 mPhotoView.setPhoto(valuesDelta, mMaterialPalette);
595 }
596
Walter Jangd35e5ef2015-02-24 09:18:16 -0800597 public View getAggregationAnchorView() {
Walter Jangf10ca152015-09-22 15:23:55 -0700598 final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
Walter Jang192a01c2015-09-22 15:23:55 -0700599 StructuredName.CONTENT_ITEM_TYPE);
Walter Jangf10ca152015-09-22 15:23:55 -0700600 if (!kindSectionViews.isEmpty()) {
Walter Jang192a01c2015-09-22 15:23:55 -0700601 return mKindSectionViews.getChildAt(0).findViewById(R.id.anchor_view);
Walter Jangd35e5ef2015-02-24 09:18:16 -0800602 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700603 return null;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800604 }
605
Walter Jangf10ca152015-09-22 15:23:55 -0700606 public void setGroupMetaData(Cursor groupMetaData) {
607 final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
608 GroupMembership.CONTENT_ITEM_TYPE);
609 for (CompactKindSectionView kindSectionView : kindSectionViews) {
610 kindSectionView.setGroupMetaData(groupMetaData);
Walter Jangd6753152015-10-02 09:23:13 -0700611 if (mIsExpanded) {
612 kindSectionView.setHideWhenEmpty(false);
613 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
614 }
Walter Jangf10ca152015-09-22 15:23:55 -0700615 }
616 }
617
Walter Jangf46abd82015-02-20 16:52:04 -0800618 public void setState(RawContactDeltaList rawContactDeltas,
Walter Jang06f73a12015-06-17 11:15:48 -0700619 MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
Walter Jange3945952015-10-27 12:44:54 -0700620 long photoId, boolean hasNewContact, boolean isUserProfile,
621 AccountWithDataSet primaryAccount) {
Walter Jang363d3fd2015-09-16 10:29:07 -0700622 mKindSectionDataMap.clear();
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700623 mKindSectionViewsMap.clear();
Walter Jangf5dfea42015-09-16 12:30:36 -0700624 mKindSectionViews.removeAllViews();
Walter Jang363d3fd2015-09-16 10:29:07 -0700625 mMoreFields.setVisibility(View.VISIBLE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800626
Walter Jangf46abd82015-02-20 16:52:04 -0800627 mMaterialPalette = materialPalette;
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700628 mViewIdGenerator = viewIdGenerator;
Walter Jang708ea9e2015-09-10 15:42:05 -0700629 mPhotoId = photoId;
Tingting Wangf695eb32015-10-15 18:45:15 -0700630
Walter Jang708ea9e2015-09-10 15:42:05 -0700631 mHasNewContact = hasNewContact;
632 mIsUserProfile = isUserProfile;
633 mPrimaryAccount = primaryAccount;
634 if (mPrimaryAccount == null) {
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700635 mPrimaryAccount = ContactEditorUtils.create(getContext()).getOnlyOrDefaultAccount();
Walter Jang708ea9e2015-09-10 15:42:05 -0700636 }
637 vlog("state: primary " + mPrimaryAccount);
Walter Jangcab3dce2015-02-09 17:48:03 -0800638
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700639 // Parse the given raw contact deltas
640 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
641 elog("No raw contact deltas");
Walter Jang79658e12015-09-24 10:36:26 -0700642 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jangf5dfea42015-09-16 12:30:36 -0700643 return;
644 }
Walter Jangcbd431d2015-10-06 13:07:30 -0700645 parseRawContactDeltas(rawContactDeltas);
Walter Jang79658e12015-09-24 10:36:26 -0700646 if (mKindSectionDataMap.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700647 elog("No kind section data parsed from RawContactDelta(s)");
Walter Jang79658e12015-09-24 10:36:26 -0700648 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700649 return;
650 }
Walter Jang82acd422015-10-17 14:01:27 -0700651
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700652
Walter Jang82acd422015-10-17 14:01:27 -0700653 // Get the primary name kind section data
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700654 mPrimaryNameKindSectionData =
655 getOrCreateKindSectionDataList(StructuredName.CONTENT_ITEM_TYPE)
656 .getEntryToWrite(/* id =*/ -1, mPrimaryAccount, mIsUserProfile);
Walter Jang82acd422015-10-17 14:01:27 -0700657 if (mPrimaryNameKindSectionData != null) {
658 // Ensure that a structured name and photo exists
659 final RawContactDelta rawContactDelta =
660 mPrimaryNameKindSectionData.first.getRawContactDelta();
661 RawContactModifier.ensureKindExists(
662 rawContactDelta,
663 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700664 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang82acd422015-10-17 14:01:27 -0700665 RawContactModifier.ensureKindExists(
666 rawContactDelta,
667 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700668 Photo.CONTENT_ITEM_TYPE);
669 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700670
671 // Setup the view
Tingting Wang655ad1a2015-10-05 17:51:14 -0700672 addAccountInfo(rawContactDeltas);
Walter Jang363d3fd2015-09-16 10:29:07 -0700673 addPhotoView();
Walter Jangf5dfea42015-09-16 12:30:36 -0700674 addKindSectionViews();
Walter Jange3945952015-10-27 12:44:54 -0700675
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700676 mMoreFields.setVisibility(hasMoreFields() ? View.VISIBLE : View.GONE);
677
Walter Jang4f5594a2015-10-06 18:40:31 -0700678 if (mIsExpanded) showAllFields();
Walter Jangd6753152015-10-02 09:23:13 -0700679
680 if (mListener != null) mListener.onEditorsBound();
Walter Jangcab3dce2015-02-09 17:48:03 -0800681 }
682
Walter Jangcbd431d2015-10-06 13:07:30 -0700683 private void parseRawContactDeltas(RawContactDeltaList rawContactDeltas) {
Walter Jang363d3fd2015-09-16 10:29:07 -0700684 // Build the kind section data list map
Walter Jang192a01c2015-09-22 15:23:55 -0700685 vlog("parse: " + rawContactDeltas.size() + " rawContactDelta(s)");
686 for (int j = 0; j < rawContactDeltas.size(); j++) {
687 final RawContactDelta rawContactDelta = rawContactDeltas.get(j);
688 vlog("parse: " + j + " rawContactDelta" + rawContactDelta);
Walter Jang363d3fd2015-09-16 10:29:07 -0700689 if (rawContactDelta == null || !rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800690 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jang363d3fd2015-09-16 10:29:07 -0700691 if (accountType == null) continue;
692 final List<DataKind> dataKinds = accountType.getSortedDataKinds();
693 final int dataKindSize = dataKinds == null ? 0 : dataKinds.size();
694 vlog("parse: " + dataKindSize + " dataKinds(s)");
695 for (int i = 0; i < dataKindSize; i++) {
696 final DataKind dataKind = dataKinds.get(i);
Walter Jang192a01c2015-09-22 15:23:55 -0700697 if (dataKind == null || !dataKind.editable) {
698 vlog("parse: " + i + " " + dataKind.mimeType + " dropped read-only");
699 continue;
700 }
701 final String mimeType = dataKind.mimeType;
702
703 // Skip psuedo mime types
704 if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
705 || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
706 vlog("parse: " + i + " " + dataKind.mimeType + " dropped pseudo type");
707 continue;
708 }
709
Walter Jang3f18d612015-10-07 16:01:05 -0700710 final KindSectionDataList kindSectionDataList =
Walter Jangcbd431d2015-10-06 13:07:30 -0700711 getOrCreateKindSectionDataList(mimeType);
Walter Jang363d3fd2015-09-16 10:29:07 -0700712 final KindSectionData kindSectionData =
713 new KindSectionData(accountType, dataKind, rawContactDelta);
714 kindSectionDataList.add(kindSectionData);
Walter Jang192a01c2015-09-22 15:23:55 -0700715
Walter Jang363d3fd2015-09-16 10:29:07 -0700716 vlog("parse: " + i + " " + dataKind.mimeType + " " +
Walter Jang228e02f2015-11-13 09:11:01 -0800717 kindSectionData.getValuesDeltas().size() + " value(s) " +
Tingting Wangd482e0c2015-11-16 10:13:29 -0800718 kindSectionData.getNonEmptyValuesDeltas().size() + " non-empty value(s) " +
Wenyi Wang42d740c2015-11-13 14:23:54 -0800719 kindSectionData.getVisibleValuesDeltas().size() +
Walter Jang228e02f2015-11-13 09:11:01 -0800720 " visible value(s)");
Walter Jangac679af2015-06-01 12:17:06 -0700721 }
722 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800723 }
724
Walter Jang3f18d612015-10-07 16:01:05 -0700725 private KindSectionDataList getOrCreateKindSectionDataList(String mimeType) {
Walter Jangcbd431d2015-10-06 13:07:30 -0700726 KindSectionDataList kindSectionDataList = mKindSectionDataMap.get(mimeType);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700727 if (kindSectionDataList == null) {
Walter Jangcbd431d2015-10-06 13:07:30 -0700728 kindSectionDataList = new KindSectionDataList();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700729 mKindSectionDataMap.put(mimeType, kindSectionDataList);
730 }
731 return kindSectionDataList;
732 }
733
Tingting Wang655ad1a2015-10-05 17:51:14 -0700734 private void addAccountInfo(RawContactDeltaList rawContactDeltas) {
Walter Jang82acd422015-10-17 14:01:27 -0700735 mAccountHeaderContainer.setVisibility(View.GONE);
Walter Jang82acd422015-10-17 14:01:27 -0700736 mRawContactContainer.setVisibility(View.GONE);
737
738 if (mPrimaryNameKindSectionData == null) return;
739 final RawContactDelta rawContactDelta =
740 mPrimaryNameKindSectionData.first.getRawContactDelta();
Walter Jang708ea9e2015-09-10 15:42:05 -0700741
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700742 final AccountDisplayInfo account =
743 mAccountDisplayInfoFactory.getAccountDisplayInfoFor(rawContactDelta);
744
Walter Jang708ea9e2015-09-10 15:42:05 -0700745 // Get the account information for the primary raw contact delta
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700746 final String accountLabel = mIsUserProfile
747 ? EditorUiUtils.getAccountHeaderLabelForMyProfile(getContext(), account)
748 : account.getNameLabel().toString();
Walter Jang708ea9e2015-09-10 15:42:05 -0700749
Walter Jang23709542015-10-22 12:50:58 -0700750 // Either the account header or selector should be shown, not both.
Walter Jang708ea9e2015-09-10 15:42:05 -0700751 final List<AccountWithDataSet> accounts =
752 AccountTypeManager.getInstance(getContext()).getAccounts(true);
Walter Jang82acd422015-10-17 14:01:27 -0700753 if (mHasNewContact && !mIsUserProfile) {
754 if (accounts.size() > 1) {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700755 addAccountSelector(rawContactDelta, accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700756 } else {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700757 addAccountHeader(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700758 }
Tingting Wang1594c6f2015-10-26 17:39:16 -0700759 } else if (mIsUserProfile || !shouldHideAccountContainer(rawContactDeltas)) {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700760 addAccountHeader(accountLabel);
Walter Jang708ea9e2015-09-10 15:42:05 -0700761 }
Walter Jang23709542015-10-22 12:50:58 -0700762
763 // The raw contact selector should only display linked raw contacts that can be edited in
764 // the full editor (i.e. they are not newly created raw contacts)
Tingting Wang2bb85d22015-10-23 13:01:09 -0700765 final RawContactAccountListAdapter adapter = new RawContactAccountListAdapter(getContext(),
766 getRawContactDeltaListForSelector(rawContactDeltas));
767 if (adapter.getCount() > 0) {
768 final String accountsSummary = getResources().getQuantityString(
769 R.plurals.compact_editor_linked_contacts_selector_title,
770 adapter.getCount(), adapter.getCount());
Walter Jang23709542015-10-22 12:50:58 -0700771 addRawContactAccountSelector(accountsSummary, adapter);
772 }
Walter Jang708ea9e2015-09-10 15:42:05 -0700773 }
774
Tingting Wang2bb85d22015-10-23 13:01:09 -0700775 private RawContactDeltaList getRawContactDeltaListForSelector(
776 RawContactDeltaList rawContactDeltas) {
777 // Sort raw contacts so google accounts come first
778 Collections.sort(rawContactDeltas, new RawContactDeltaComparator(getContext()));
779
780 final RawContactDeltaList result = new RawContactDeltaList();
781 for (RawContactDelta rawContactDelta : rawContactDeltas) {
782 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
783 // Only add raw contacts that can be opened in the editor
784 result.add(rawContactDelta);
785 }
786 }
787 // Don't return a list of size 1 that would just open the raw contact being edited
788 // in the compact editor in the full editor
789 if (result.size() == 1 && result.get(0).getRawContactAccountType(
790 getContext()).areContactsWritable()) {
791 result.clear();
792 return result;
793 }
794 return result;
795 }
796
Tingting Wang1594c6f2015-10-26 17:39:16 -0700797 // Returns true if there are multiple writable rawcontacts and no read-only ones,
798 // or there are both writable and read-only rawcontacts.
799 private boolean shouldHideAccountContainer(RawContactDeltaList rawContactDeltas) {
Tingting Wang2bb85d22015-10-23 13:01:09 -0700800 int writable = 0;
801 int readonly = 0;
802 for (RawContactDelta rawContactDelta : rawContactDeltas) {
803 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
804 if (rawContactDelta.getRawContactAccountType(getContext()).areContactsWritable()) {
805 writable++;
806 } else {
807 readonly++;
808 }
809 }
810 }
811 return (writable > 1 || (writable > 0 && readonly > 0));
812 }
813
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700814 private void addAccountHeader(String accountLabel) {
Walter Jang82acd422015-10-17 14:01:27 -0700815 mAccountHeaderContainer.setVisibility(View.VISIBLE);
816
Walter Jangbe5e1b12015-10-17 11:38:29 -0700817 // Set the account name
Walter Jangbe5e1b12015-10-17 11:38:29 -0700818 mAccountHeaderName.setVisibility(View.VISIBLE);
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700819 mAccountHeaderName.setText(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700820
821 // Set the account type
822 final String selectorTitle = getResources().getString(
823 R.string.compact_editor_account_selector_title);
824 mAccountHeaderType.setText(selectorTitle);
825
826 // Set the icon
Walter Jang82acd422015-10-17 14:01:27 -0700827 if (mPrimaryNameKindSectionData != null) {
828 final RawContactDelta rawContactDelta =
829 mPrimaryNameKindSectionData.first.getRawContactDelta();
830 if (rawContactDelta != null) {
831 final AccountType accountType =
832 rawContactDelta.getRawContactAccountType(getContext());
833 mAccountHeaderIcon.setImageDrawable(accountType.getDisplayIcon(getContext()));
834 }
835 }
Walter Jangbe5e1b12015-10-17 11:38:29 -0700836
837 // Set the content description
838 mAccountHeaderContainer.setContentDescription(
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700839 EditorUiUtils.getAccountInfoContentDescription(accountLabel,
840 selectorTitle));
Walter Jangbe5e1b12015-10-17 11:38:29 -0700841 }
842
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700843 private void addAccountSelector(final RawContactDelta rawContactDelta, CharSequence nameLabel) {
Gary Mai46cb3102016-08-10 18:14:09 -0700844 // Show save to default account.
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700845 addAccountHeader(nameLabel.toString());
Gary Mai46cb3102016-08-10 18:14:09 -0700846 // Add handlers for choosing another account to save to.
847 mAccountHeaderExpanderIcon.setVisibility(View.VISIBLE);
848 mAccountHeaderContainer.setOnClickListener(new View.OnClickListener() {
Walter Jang708ea9e2015-09-10 15:42:05 -0700849 @Override
850 public void onClick(View v) {
851 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
852 final AccountsListAdapter adapter =
853 new AccountsListAdapter(getContext(),
854 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE,
855 mPrimaryAccount);
Gary Mai46cb3102016-08-10 18:14:09 -0700856 popup.setWidth(mAccountHeaderContainer.getWidth());
857 popup.setAnchorView(mAccountHeaderContainer);
Walter Jang708ea9e2015-09-10 15:42:05 -0700858 popup.setAdapter(adapter);
859 popup.setModal(true);
860 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
861 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
862 @Override
863 public void onItemClick(AdapterView<?> parent, View view, int position,
864 long id) {
865 UiClosables.closeQuietly(popup);
866 final AccountWithDataSet newAccount = adapter.getItem(position);
867 if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
868 mListener.onRebindEditorsForNewContact(
Walter Jang82acd422015-10-17 14:01:27 -0700869 rawContactDelta,
Walter Jang708ea9e2015-09-10 15:42:05 -0700870 mPrimaryAccount,
871 newAccount);
872 }
873 }
874 });
875 popup.show();
876 }
877 });
878 }
879
Walter Jang23709542015-10-22 12:50:58 -0700880 private void addRawContactAccountSelector(String accountsSummary,
881 final RawContactAccountListAdapter adapter) {
Tingting Wang655ad1a2015-10-05 17:51:14 -0700882 mRawContactContainer.setVisibility(View.VISIBLE);
883
Tingting Wang655ad1a2015-10-05 17:51:14 -0700884 mRawContactSummary.setText(accountsSummary);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700885
886 mRawContactContainer.setOnClickListener(new View.OnClickListener() {
887 @Override
888 public void onClick(View v) {
889 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700890 popup.setWidth(mRawContactContainer.getWidth());
891 popup.setAnchorView(mRawContactContainer);
892 popup.setAdapter(adapter);
893 popup.setModal(true);
894 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
895 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
896 @Override
897 public void onItemClick(AdapterView<?> parent, View view, int position,
898 long id) {
899 UiClosables.closeQuietly(popup);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700900
901 if (mListener != null) {
Tingting Wang91cee282015-10-07 13:48:17 -0700902 final long rawContactId = adapter.getItemId(position);
903 final Uri rawContactUri = ContentUris.withAppendedId(
904 ContactsContract.RawContacts.CONTENT_URI, rawContactId);
905 final RawContactDelta rawContactDelta = adapter.getItem(position);
906 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
907 getContext());
908 final AccountType accountType = rawContactDelta.getAccountType(
909 accountTypes);
910 final boolean isReadOnly = !accountType.areContactsWritable();
911
912 mListener.onRawContactSelected(rawContactUri, rawContactId, isReadOnly);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700913 }
914 }
915 });
916 popup.show();
917 }
918 });
919 }
920
Walter Jang363d3fd2015-09-16 10:29:07 -0700921 private void addPhotoView() {
Walter Jang31a74ad2015-10-02 19:17:39 -0700922 // Get the kind section data and values delta that we will display in the photo view
Walter Jangcbd431d2015-10-06 13:07:30 -0700923 final KindSectionDataList kindSectionDataList =
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700924 getOrCreateKindSectionDataList(Photo.CONTENT_ITEM_TYPE);
Walter Jangcbd431d2015-10-06 13:07:30 -0700925 final Pair<KindSectionData,ValuesDelta> photoToDisplay =
926 kindSectionDataList.getEntryToDisplay(mPhotoId);
927 if (photoToDisplay == null) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700928 wlog("photo: no kind section data parsed");
Walter Jangcbd431d2015-10-06 13:07:30 -0700929 mPhotoView.setVisibility(View.GONE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700930 return;
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700931 } else {
932 mPhotoView.setVisibility(View.VISIBLE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700933 }
Walter Jang06f73a12015-06-17 11:15:48 -0700934
Walter Jang31a74ad2015-10-02 19:17:39 -0700935 // Set the photo view
Walter Jangcbd431d2015-10-06 13:07:30 -0700936 mPhotoView.setPhoto(photoToDisplay.second, mMaterialPalette);
Walter Jang31a74ad2015-10-02 19:17:39 -0700937
938 // Find the raw contact ID and values delta that will be written when the photo is edited
Walter Jangda258ff2015-11-17 11:06:02 -0800939 final Pair<KindSectionData, ValuesDelta> photoToWrite = kindSectionDataList.getEntryToWrite(
Walter Jang82acd422015-10-17 14:01:27 -0700940 mPhotoId, mPrimaryAccount, mIsUserProfile);
Walter Jangda258ff2015-11-17 11:06:02 -0800941 if (photoToWrite == null) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700942 mPhotoView.setReadOnly(true);
943 return;
944 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700945 mPhotoView.setReadOnly(false);
Walter Jangda258ff2015-11-17 11:06:02 -0800946 mPhotoRawContactId = photoToWrite.first.getRawContactDelta().getRawContactId();
947 mPhotoValuesDelta = photoToWrite.second;
Walter Jang31a74ad2015-10-02 19:17:39 -0700948 }
949
Walter Jangf5dfea42015-09-16 12:30:36 -0700950 private void addKindSectionViews() {
951 // Sort the kinds
Walter Jangcbd431d2015-10-06 13:07:30 -0700952 final TreeSet<Map.Entry<String,KindSectionDataList>> entries =
Walter Jang192a01c2015-09-22 15:23:55 -0700953 new TreeSet<>(KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR);
Walter Jangf5dfea42015-09-16 12:30:36 -0700954 entries.addAll(mKindSectionDataMap.entrySet());
Walter Jang3efae4a2015-02-18 11:12:00 -0800955
Walter Jangf5dfea42015-09-16 12:30:36 -0700956 vlog("kind: " + entries.size() + " kindSection(s)");
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700957 int i = -1;
Walter Jangcbd431d2015-10-06 13:07:30 -0700958 for (Map.Entry<String, KindSectionDataList> entry : entries) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700959 i++;
960
Walter Jangf5dfea42015-09-16 12:30:36 -0700961 final String mimeType = entry.getKey();
Walter Jang82acd422015-10-17 14:01:27 -0700962
Tingting Wangf695eb32015-10-15 18:45:15 -0700963 if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang82acd422015-10-17 14:01:27 -0700964 if (mPrimaryNameKindSectionData == null) {
965 vlog("kind: " + i + " " + mimeType + " dropped");
966 continue;
Tingting Wangf695eb32015-10-15 18:45:15 -0700967 }
Walter Jang82acd422015-10-17 14:01:27 -0700968 vlog("kind: " + i + " " + mimeType + " using first entry only");
969 final KindSectionDataList kindSectionDataList = new KindSectionDataList();
970 kindSectionDataList.add(mPrimaryNameKindSectionData.first);
Walter Jangf5dfea42015-09-16 12:30:36 -0700971 final CompactKindSectionView kindSectionView = inflateKindSectionView(
Walter Jang82acd422015-10-17 14:01:27 -0700972 mKindSectionViews, kindSectionDataList, mimeType,
973 mPrimaryNameKindSectionData.second);
Walter Jangf5dfea42015-09-16 12:30:36 -0700974 mKindSectionViews.addView(kindSectionView);
Walter Jangf10ca152015-09-22 15:23:55 -0700975
976 // Keep a pointer to all the KindSectionsViews for each mimeType
977 getKindSectionViews(mimeType).add(kindSectionView);
Walter Jang82acd422015-10-17 14:01:27 -0700978 } else {
979 final KindSectionDataList kindSectionDataList = entry.getValue();
980
981 // Ignore mime types that we've already handled
982 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
983 vlog("kind: " + i + " " + mimeType + " dropped");
984 continue;
985 }
986
987 // Don't show more than one group editor on the compact editor.
988 // Groups will still be editable for each raw contact individually on the full editor.
989 if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)
990 && kindSectionDataList.size() > 1) {
991 vlog("kind: " + i + " " + mimeType + " dropped");
992 continue;
993 }
994
995 if (kindSectionDataList != null && !kindSectionDataList.isEmpty()) {
996 vlog("kind: " + i + " " + mimeType + " " + kindSectionDataList.size() +
997 " kindSectionData(s)");
998
999 final CompactKindSectionView kindSectionView = inflateKindSectionView(
1000 mKindSectionViews, kindSectionDataList, mimeType,
1001 /* primaryValueDelta =*/ null);
1002 mKindSectionViews.addView(kindSectionView);
1003
1004 // Keep a pointer to all the KindSectionsViews for each mimeType
1005 getKindSectionViews(mimeType).add(kindSectionView);
1006 }
Walter Jangcab3dce2015-02-09 17:48:03 -08001007 }
1008 }
1009 }
1010
Walter Jangf10ca152015-09-22 15:23:55 -07001011 private List<CompactKindSectionView> getKindSectionViews(String mimeType) {
1012 List<CompactKindSectionView> kindSectionViews = mKindSectionViewsMap.get(mimeType);
1013 if (kindSectionViews == null) {
1014 kindSectionViews = new ArrayList<>();
1015 mKindSectionViewsMap.put(mimeType, kindSectionViews);
1016 }
1017 return kindSectionViews;
1018 }
1019
Walter Jangf5dfea42015-09-16 12:30:36 -07001020 private CompactKindSectionView inflateKindSectionView(ViewGroup viewGroup,
Walter Jang82acd422015-10-17 14:01:27 -07001021 KindSectionDataList kindSectionDataList, String mimeType,
1022 ValuesDelta primaryValuesDelta) {
Walter Jangf5dfea42015-09-16 12:30:36 -07001023 final CompactKindSectionView kindSectionView = (CompactKindSectionView)
1024 mLayoutInflater.inflate(R.layout.compact_item_kind_section, viewGroup,
1025 /* attachToRoot =*/ false);
Walter Jang45b86d52015-10-15 15:23:16 -07001026 kindSectionView.setIsUserProfile(mIsUserProfile);
Walter Jangf5dfea42015-09-16 12:30:36 -07001027
Walter Jang192a01c2015-09-22 15:23:55 -07001028 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangf5dfea42015-09-16 12:30:36 -07001029 || Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -07001030 // Phone numbers and email addresses are always displayed,
1031 // even if they are empty
Walter Jangf5dfea42015-09-16 12:30:36 -07001032 kindSectionView.setHideWhenEmpty(false);
1033 }
Walter Jangf5dfea42015-09-16 12:30:36 -07001034
Walter Jang192a01c2015-09-22 15:23:55 -07001035 // Since phone numbers and email addresses displayed even if they are empty,
1036 // they will be the only types you add new values to initially for new contacts
1037 kindSectionView.setShowOneEmptyEditor(true);
1038
Walter Jang82acd422015-10-17 14:01:27 -07001039 // Sort non-name editors so they wind up in the order we want
1040 if (!StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -07001041 Collections.sort(kindSectionDataList, new EditorComparator(getContext()));
Walter Jang3e5ae0d2015-09-20 12:43:37 -07001042 }
1043
Walter Jang82acd422015-10-17 14:01:27 -07001044 kindSectionView.setState(kindSectionDataList, mViewIdGenerator, mListener,
1045 primaryValuesDelta);
Walter Jangf5dfea42015-09-16 12:30:36 -07001046
1047 return kindSectionView;
Walter Jangcab3dce2015-02-09 17:48:03 -08001048 }
1049
Walter Jange3945952015-10-27 12:44:54 -07001050 void maybeSetReadOnlyDisplayNameAsPrimary(String readOnlyDisplayName) {
1051 if (TextUtils.isEmpty(readOnlyDisplayName)) return;
1052 final CompactKindSectionView primaryNameKindSectionView = getPrimaryNameKindSectionView();
1053 if (primaryNameKindSectionView != null && primaryNameKindSectionView.isEmptyName()) {
1054 vlog("name: using read only display name as primary name");
1055 primaryNameKindSectionView.setName(readOnlyDisplayName);
1056 }
1057 }
1058
1059 private CompactKindSectionView getPrimaryNameKindSectionView() {
Walter Jang4f5594a2015-10-06 18:40:31 -07001060 final List<CompactKindSectionView> kindSectionViews
1061 = mKindSectionViewsMap.get(StructuredName.CONTENT_ITEM_TYPE);
Walter Jange3945952015-10-27 12:44:54 -07001062 return kindSectionViews == null || kindSectionViews.isEmpty()
1063 ? null : kindSectionViews.get(0);
Walter Jang4f5594a2015-10-06 18:40:31 -07001064 }
1065
Walter Jangd6753152015-10-02 09:23:13 -07001066 private void showAllFields() {
Walter Jang79658e12015-09-24 10:36:26 -07001067 // Stop hiding empty editors and allow the user to enter values for all kinds now
1068 for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
1069 final CompactKindSectionView kindSectionView =
1070 (CompactKindSectionView) mKindSectionViews.getChildAt(i);
1071 kindSectionView.setHideWhenEmpty(false);
1072 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
1073 }
1074 mIsExpanded = true;
Walter Jang79658e12015-09-24 10:36:26 -07001075
Walter Jangf5dfea42015-09-16 12:30:36 -07001076 // Hide the more fields button
1077 mMoreFields.setVisibility(View.GONE);
Walter Jangcab3dce2015-02-09 17:48:03 -08001078 }
1079
Marcus Hagerottcf7f2952016-09-06 13:49:17 -07001080 private boolean hasMoreFields() {
1081 for (List<CompactKindSectionView> sections : mKindSectionViewsMap.values()) {
1082 for (CompactKindSectionView section : sections) {
1083 if (section.getVisibility() != View.VISIBLE) {
1084 return true;
1085 }
1086 }
1087 }
1088 return false;
1089 }
1090
Walter Jangbf63a6d2015-05-05 09:14:35 -07001091 private static void vlog(String message) {
1092 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1093 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -08001094 }
1095 }
Walter Jang363d3fd2015-09-16 10:29:07 -07001096
1097 private static void wlog(String message) {
1098 if (Log.isLoggable(TAG, Log.WARN)) {
1099 Log.w(TAG, message);
1100 }
1101 }
1102
1103 private static void elog(String message) {
1104 Log.e(TAG, message);
1105 }
Walter Jangcab3dce2015-02-09 17:48:03 -08001106}