blob: 884cc5132f1c46f95ea4845dd60705b625ecb601 [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
Marcus Hagerottfac695a2016-08-24 17:02:40 -070019import com.android.contacts.common.model.account.AccountDisplayInfo;
20import com.android.contacts.common.model.account.AccountDisplayInfoFactory;
Walter Jangcab3dce2015-02-09 17:48:03 -080021import com.android.contacts.R;
22import com.android.contacts.common.model.AccountTypeManager;
23import com.android.contacts.common.model.RawContactDelta;
24import com.android.contacts.common.model.RawContactDeltaList;
Walter Jange720fde2015-02-17 10:54:14 -080025import com.android.contacts.common.model.RawContactModifier;
Walter Jangcab3dce2015-02-09 17:48:03 -080026import com.android.contacts.common.model.ValuesDelta;
27import com.android.contacts.common.model.account.AccountType;
Walter Jang2d3f31c2015-06-18 23:15:31 -070028import com.android.contacts.common.model.account.AccountWithDataSet;
Walter Jangcab3dce2015-02-09 17:48:03 -080029import com.android.contacts.common.model.dataitem.DataKind;
Walter Jang708ea9e2015-09-10 15:42:05 -070030import com.android.contacts.common.util.AccountsListAdapter;
Walter Jangf46abd82015-02-20 16:52:04 -080031import com.android.contacts.common.util.MaterialColorMapUtils;
Walter Jang708ea9e2015-09-10 15:42:05 -070032import com.android.contacts.util.UiClosables;
Walter Jangcab3dce2015-02-09 17:48:03 -080033
Tingting Wang655ad1a2015-10-05 17:51:14 -070034import android.content.ContentUris;
Walter Jangcab3dce2015-02-09 17:48:03 -080035import android.content.Context;
Walter Jangf10ca152015-09-22 15:23:55 -070036import android.database.Cursor;
Walter Jang3efae4a2015-02-18 11:12:00 -080037import android.graphics.Bitmap;
Walter Jang41b3ea12015-03-09 17:30:06 -070038import android.net.Uri;
Walter Jang3f18d612015-10-07 16:01:05 -070039import android.os.Bundle;
Walter Jang79658e12015-09-24 10:36:26 -070040import android.os.Parcel;
41import android.os.Parcelable;
Tingting Wang655ad1a2015-10-05 17:51:14 -070042import android.provider.ContactsContract;
Walter Jangcab3dce2015-02-09 17:48:03 -080043import android.provider.ContactsContract.CommonDataKinds.Email;
Walter Jangf5dfea42015-09-16 12:30:36 -070044import android.provider.ContactsContract.CommonDataKinds.Event;
Walter Jangcab3dce2015-02-09 17:48:03 -080045import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Walter Jangf5dfea42015-09-16 12:30:36 -070046import android.provider.ContactsContract.CommonDataKinds.Im;
Walter Jang3efae4a2015-02-18 11:12:00 -080047import android.provider.ContactsContract.CommonDataKinds.Nickname;
Walter Jangf5dfea42015-09-16 12:30:36 -070048import android.provider.ContactsContract.CommonDataKinds.Note;
49import android.provider.ContactsContract.CommonDataKinds.Organization;
Walter Jangcab3dce2015-02-09 17:48:03 -080050import android.provider.ContactsContract.CommonDataKinds.Phone;
51import android.provider.ContactsContract.CommonDataKinds.Photo;
Walter Jangf5dfea42015-09-16 12:30:36 -070052import android.provider.ContactsContract.CommonDataKinds.Relation;
53import android.provider.ContactsContract.CommonDataKinds.SipAddress;
Walter Jangcab3dce2015-02-09 17:48:03 -080054import android.provider.ContactsContract.CommonDataKinds.StructuredName;
Walter Jangf5dfea42015-09-16 12:30:36 -070055import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
56import android.provider.ContactsContract.CommonDataKinds.Website;
Walter Jangcab3dce2015-02-09 17:48:03 -080057import android.text.TextUtils;
58import android.util.AttributeSet;
59import android.util.Log;
Walter Jang2d3f31c2015-06-18 23:15:31 -070060import android.util.Pair;
Walter Jangcab3dce2015-02-09 17:48:03 -080061import android.view.LayoutInflater;
Walter Jangb1c87622015-02-13 17:51:38 -080062import android.view.View;
Walter Jangcab3dce2015-02-09 17:48:03 -080063import android.view.ViewGroup;
Walter Jang708ea9e2015-09-10 15:42:05 -070064import android.widget.AdapterView;
Tingting Wang655ad1a2015-10-05 17:51:14 -070065import android.widget.BaseAdapter;
66import android.widget.ImageView;
Walter Jangcab3dce2015-02-09 17:48:03 -080067import android.widget.LinearLayout;
Walter Jang708ea9e2015-09-10 15:42:05 -070068import android.widget.ListPopupWindow;
Walter Jang2d3f31c2015-06-18 23:15:31 -070069import android.widget.TextView;
Walter Jangcab3dce2015-02-09 17:48:03 -080070
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();
Walter Jangf5dfea42015-09-16 12:30:36 -0700620 mKindSectionViews.removeAllViews();
Walter Jang363d3fd2015-09-16 10:29:07 -0700621 mMoreFields.setVisibility(View.VISIBLE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800622
Walter Jangf46abd82015-02-20 16:52:04 -0800623 mMaterialPalette = materialPalette;
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700624 mViewIdGenerator = viewIdGenerator;
Walter Jang708ea9e2015-09-10 15:42:05 -0700625 mPhotoId = photoId;
Tingting Wangf695eb32015-10-15 18:45:15 -0700626
Walter Jang708ea9e2015-09-10 15:42:05 -0700627 mHasNewContact = hasNewContact;
628 mIsUserProfile = isUserProfile;
629 mPrimaryAccount = primaryAccount;
630 if (mPrimaryAccount == null) {
631 mPrimaryAccount = ContactEditorUtils.getInstance(getContext()).getDefaultAccount();
632 }
633 vlog("state: primary " + mPrimaryAccount);
Walter Jangcab3dce2015-02-09 17:48:03 -0800634
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700635 // Parse the given raw contact deltas
636 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
637 elog("No raw contact deltas");
Walter Jang79658e12015-09-24 10:36:26 -0700638 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jangf5dfea42015-09-16 12:30:36 -0700639 return;
640 }
Walter Jangcbd431d2015-10-06 13:07:30 -0700641 parseRawContactDeltas(rawContactDeltas);
Walter Jang79658e12015-09-24 10:36:26 -0700642 if (mKindSectionDataMap.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700643 elog("No kind section data parsed from RawContactDelta(s)");
Walter Jang79658e12015-09-24 10:36:26 -0700644 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700645 return;
646 }
Walter Jang82acd422015-10-17 14:01:27 -0700647
648 // Get the primary name kind section data
649 mPrimaryNameKindSectionData = mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE)
650 .getEntryToWrite(/* id =*/ -1, mPrimaryAccount, mIsUserProfile);
651 if (mPrimaryNameKindSectionData != null) {
652 // Ensure that a structured name and photo exists
653 final RawContactDelta rawContactDelta =
654 mPrimaryNameKindSectionData.first.getRawContactDelta();
655 RawContactModifier.ensureKindExists(
656 rawContactDelta,
657 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700658 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang82acd422015-10-17 14:01:27 -0700659 RawContactModifier.ensureKindExists(
660 rawContactDelta,
661 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700662 Photo.CONTENT_ITEM_TYPE);
663 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700664
665 // Setup the view
Tingting Wang655ad1a2015-10-05 17:51:14 -0700666 addAccountInfo(rawContactDeltas);
Walter Jang363d3fd2015-09-16 10:29:07 -0700667 addPhotoView();
Walter Jangf5dfea42015-09-16 12:30:36 -0700668 addKindSectionViews();
Walter Jange3945952015-10-27 12:44:54 -0700669
Walter Jang4f5594a2015-10-06 18:40:31 -0700670 if (mIsExpanded) showAllFields();
Walter Jangd6753152015-10-02 09:23:13 -0700671
672 if (mListener != null) mListener.onEditorsBound();
Walter Jangcab3dce2015-02-09 17:48:03 -0800673 }
674
Walter Jangcbd431d2015-10-06 13:07:30 -0700675 private void parseRawContactDeltas(RawContactDeltaList rawContactDeltas) {
Walter Jang363d3fd2015-09-16 10:29:07 -0700676 // Build the kind section data list map
Walter Jang192a01c2015-09-22 15:23:55 -0700677 vlog("parse: " + rawContactDeltas.size() + " rawContactDelta(s)");
678 for (int j = 0; j < rawContactDeltas.size(); j++) {
679 final RawContactDelta rawContactDelta = rawContactDeltas.get(j);
680 vlog("parse: " + j + " rawContactDelta" + rawContactDelta);
Walter Jang363d3fd2015-09-16 10:29:07 -0700681 if (rawContactDelta == null || !rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800682 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jang363d3fd2015-09-16 10:29:07 -0700683 if (accountType == null) continue;
684 final List<DataKind> dataKinds = accountType.getSortedDataKinds();
685 final int dataKindSize = dataKinds == null ? 0 : dataKinds.size();
686 vlog("parse: " + dataKindSize + " dataKinds(s)");
687 for (int i = 0; i < dataKindSize; i++) {
688 final DataKind dataKind = dataKinds.get(i);
Walter Jang192a01c2015-09-22 15:23:55 -0700689 if (dataKind == null || !dataKind.editable) {
690 vlog("parse: " + i + " " + dataKind.mimeType + " dropped read-only");
691 continue;
692 }
693 final String mimeType = dataKind.mimeType;
694
695 // Skip psuedo mime types
696 if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
697 || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
698 vlog("parse: " + i + " " + dataKind.mimeType + " dropped pseudo type");
699 continue;
700 }
701
Walter Jang3f18d612015-10-07 16:01:05 -0700702 final KindSectionDataList kindSectionDataList =
Walter Jangcbd431d2015-10-06 13:07:30 -0700703 getOrCreateKindSectionDataList(mimeType);
Walter Jang363d3fd2015-09-16 10:29:07 -0700704 final KindSectionData kindSectionData =
705 new KindSectionData(accountType, dataKind, rawContactDelta);
706 kindSectionDataList.add(kindSectionData);
Walter Jang192a01c2015-09-22 15:23:55 -0700707
Walter Jang363d3fd2015-09-16 10:29:07 -0700708 vlog("parse: " + i + " " + dataKind.mimeType + " " +
Walter Jang228e02f2015-11-13 09:11:01 -0800709 kindSectionData.getValuesDeltas().size() + " value(s) " +
Tingting Wangd482e0c2015-11-16 10:13:29 -0800710 kindSectionData.getNonEmptyValuesDeltas().size() + " non-empty value(s) " +
Wenyi Wang42d740c2015-11-13 14:23:54 -0800711 kindSectionData.getVisibleValuesDeltas().size() +
Walter Jang228e02f2015-11-13 09:11:01 -0800712 " visible value(s)");
Walter Jangac679af2015-06-01 12:17:06 -0700713 }
714 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800715 }
716
Walter Jang3f18d612015-10-07 16:01:05 -0700717 private KindSectionDataList getOrCreateKindSectionDataList(String mimeType) {
Walter Jangcbd431d2015-10-06 13:07:30 -0700718 KindSectionDataList kindSectionDataList = mKindSectionDataMap.get(mimeType);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700719 if (kindSectionDataList == null) {
Walter Jangcbd431d2015-10-06 13:07:30 -0700720 kindSectionDataList = new KindSectionDataList();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700721 mKindSectionDataMap.put(mimeType, kindSectionDataList);
722 }
723 return kindSectionDataList;
724 }
725
Tingting Wang655ad1a2015-10-05 17:51:14 -0700726 private void addAccountInfo(RawContactDeltaList rawContactDeltas) {
Walter Jang82acd422015-10-17 14:01:27 -0700727 mAccountHeaderContainer.setVisibility(View.GONE);
Walter Jang82acd422015-10-17 14:01:27 -0700728 mRawContactContainer.setVisibility(View.GONE);
729
730 if (mPrimaryNameKindSectionData == null) return;
731 final RawContactDelta rawContactDelta =
732 mPrimaryNameKindSectionData.first.getRawContactDelta();
Walter Jang708ea9e2015-09-10 15:42:05 -0700733
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700734 final AccountDisplayInfo account =
735 mAccountDisplayInfoFactory.getAccountDisplayInfoFor(rawContactDelta);
736
Walter Jang708ea9e2015-09-10 15:42:05 -0700737 // Get the account information for the primary raw contact delta
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700738 final String accountLabel = mIsUserProfile
739 ? EditorUiUtils.getAccountHeaderLabelForMyProfile(getContext(), account)
740 : account.getNameLabel().toString();
Walter Jang708ea9e2015-09-10 15:42:05 -0700741
Walter Jang23709542015-10-22 12:50:58 -0700742 // Either the account header or selector should be shown, not both.
Walter Jang708ea9e2015-09-10 15:42:05 -0700743 final List<AccountWithDataSet> accounts =
744 AccountTypeManager.getInstance(getContext()).getAccounts(true);
Walter Jang82acd422015-10-17 14:01:27 -0700745 if (mHasNewContact && !mIsUserProfile) {
746 if (accounts.size() > 1) {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700747 addAccountSelector(rawContactDelta, accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700748 } else {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700749 addAccountHeader(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700750 }
Tingting Wang1594c6f2015-10-26 17:39:16 -0700751 } else if (mIsUserProfile || !shouldHideAccountContainer(rawContactDeltas)) {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700752 addAccountHeader(accountLabel);
Walter Jang708ea9e2015-09-10 15:42:05 -0700753 }
Walter Jang23709542015-10-22 12:50:58 -0700754
755 // The raw contact selector should only display linked raw contacts that can be edited in
756 // the full editor (i.e. they are not newly created raw contacts)
Tingting Wang2bb85d22015-10-23 13:01:09 -0700757 final RawContactAccountListAdapter adapter = new RawContactAccountListAdapter(getContext(),
758 getRawContactDeltaListForSelector(rawContactDeltas));
759 if (adapter.getCount() > 0) {
760 final String accountsSummary = getResources().getQuantityString(
761 R.plurals.compact_editor_linked_contacts_selector_title,
762 adapter.getCount(), adapter.getCount());
Walter Jang23709542015-10-22 12:50:58 -0700763 addRawContactAccountSelector(accountsSummary, adapter);
764 }
Walter Jang708ea9e2015-09-10 15:42:05 -0700765 }
766
Tingting Wang2bb85d22015-10-23 13:01:09 -0700767 private RawContactDeltaList getRawContactDeltaListForSelector(
768 RawContactDeltaList rawContactDeltas) {
769 // Sort raw contacts so google accounts come first
770 Collections.sort(rawContactDeltas, new RawContactDeltaComparator(getContext()));
771
772 final RawContactDeltaList result = new RawContactDeltaList();
773 for (RawContactDelta rawContactDelta : rawContactDeltas) {
774 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
775 // Only add raw contacts that can be opened in the editor
776 result.add(rawContactDelta);
777 }
778 }
779 // Don't return a list of size 1 that would just open the raw contact being edited
780 // in the compact editor in the full editor
781 if (result.size() == 1 && result.get(0).getRawContactAccountType(
782 getContext()).areContactsWritable()) {
783 result.clear();
784 return result;
785 }
786 return result;
787 }
788
Tingting Wang1594c6f2015-10-26 17:39:16 -0700789 // Returns true if there are multiple writable rawcontacts and no read-only ones,
790 // or there are both writable and read-only rawcontacts.
791 private boolean shouldHideAccountContainer(RawContactDeltaList rawContactDeltas) {
Tingting Wang2bb85d22015-10-23 13:01:09 -0700792 int writable = 0;
793 int readonly = 0;
794 for (RawContactDelta rawContactDelta : rawContactDeltas) {
795 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
796 if (rawContactDelta.getRawContactAccountType(getContext()).areContactsWritable()) {
797 writable++;
798 } else {
799 readonly++;
800 }
801 }
802 }
803 return (writable > 1 || (writable > 0 && readonly > 0));
804 }
805
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700806 private void addAccountHeader(String accountLabel) {
Walter Jang82acd422015-10-17 14:01:27 -0700807 mAccountHeaderContainer.setVisibility(View.VISIBLE);
808
Walter Jangbe5e1b12015-10-17 11:38:29 -0700809 // Set the account name
Walter Jangbe5e1b12015-10-17 11:38:29 -0700810 mAccountHeaderName.setVisibility(View.VISIBLE);
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700811 mAccountHeaderName.setText(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700812
813 // Set the account type
814 final String selectorTitle = getResources().getString(
815 R.string.compact_editor_account_selector_title);
816 mAccountHeaderType.setText(selectorTitle);
817
818 // Set the icon
Walter Jang82acd422015-10-17 14:01:27 -0700819 if (mPrimaryNameKindSectionData != null) {
820 final RawContactDelta rawContactDelta =
821 mPrimaryNameKindSectionData.first.getRawContactDelta();
822 if (rawContactDelta != null) {
823 final AccountType accountType =
824 rawContactDelta.getRawContactAccountType(getContext());
825 mAccountHeaderIcon.setImageDrawable(accountType.getDisplayIcon(getContext()));
826 }
827 }
Walter Jangbe5e1b12015-10-17 11:38:29 -0700828
829 // Set the content description
830 mAccountHeaderContainer.setContentDescription(
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700831 EditorUiUtils.getAccountInfoContentDescription(accountLabel,
832 selectorTitle));
Walter Jangbe5e1b12015-10-17 11:38:29 -0700833 }
834
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700835 private void addAccountSelector(final RawContactDelta rawContactDelta, CharSequence nameLabel) {
Gary Mai46cb3102016-08-10 18:14:09 -0700836 // Show save to default account.
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700837 addAccountHeader(nameLabel.toString());
Gary Mai46cb3102016-08-10 18:14:09 -0700838 // Add handlers for choosing another account to save to.
839 mAccountHeaderExpanderIcon.setVisibility(View.VISIBLE);
840 mAccountHeaderContainer.setOnClickListener(new View.OnClickListener() {
Walter Jang708ea9e2015-09-10 15:42:05 -0700841 @Override
842 public void onClick(View v) {
843 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
844 final AccountsListAdapter adapter =
845 new AccountsListAdapter(getContext(),
846 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE,
847 mPrimaryAccount);
Gary Mai46cb3102016-08-10 18:14:09 -0700848 popup.setWidth(mAccountHeaderContainer.getWidth());
849 popup.setAnchorView(mAccountHeaderContainer);
Walter Jang708ea9e2015-09-10 15:42:05 -0700850 popup.setAdapter(adapter);
851 popup.setModal(true);
852 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
853 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
854 @Override
855 public void onItemClick(AdapterView<?> parent, View view, int position,
856 long id) {
857 UiClosables.closeQuietly(popup);
858 final AccountWithDataSet newAccount = adapter.getItem(position);
859 if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
860 mListener.onRebindEditorsForNewContact(
Walter Jang82acd422015-10-17 14:01:27 -0700861 rawContactDelta,
Walter Jang708ea9e2015-09-10 15:42:05 -0700862 mPrimaryAccount,
863 newAccount);
864 }
865 }
866 });
867 popup.show();
868 }
869 });
870 }
871
Walter Jang23709542015-10-22 12:50:58 -0700872 private void addRawContactAccountSelector(String accountsSummary,
873 final RawContactAccountListAdapter adapter) {
Tingting Wang655ad1a2015-10-05 17:51:14 -0700874 mRawContactContainer.setVisibility(View.VISIBLE);
875
Tingting Wang655ad1a2015-10-05 17:51:14 -0700876 mRawContactSummary.setText(accountsSummary);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700877
878 mRawContactContainer.setOnClickListener(new View.OnClickListener() {
879 @Override
880 public void onClick(View v) {
881 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700882 popup.setWidth(mRawContactContainer.getWidth());
883 popup.setAnchorView(mRawContactContainer);
884 popup.setAdapter(adapter);
885 popup.setModal(true);
886 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
887 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
888 @Override
889 public void onItemClick(AdapterView<?> parent, View view, int position,
890 long id) {
891 UiClosables.closeQuietly(popup);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700892
893 if (mListener != null) {
Tingting Wang91cee282015-10-07 13:48:17 -0700894 final long rawContactId = adapter.getItemId(position);
895 final Uri rawContactUri = ContentUris.withAppendedId(
896 ContactsContract.RawContacts.CONTENT_URI, rawContactId);
897 final RawContactDelta rawContactDelta = adapter.getItem(position);
898 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
899 getContext());
900 final AccountType accountType = rawContactDelta.getAccountType(
901 accountTypes);
902 final boolean isReadOnly = !accountType.areContactsWritable();
903
904 mListener.onRawContactSelected(rawContactUri, rawContactId, isReadOnly);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700905 }
906 }
907 });
908 popup.show();
909 }
910 });
911 }
912
Walter Jang363d3fd2015-09-16 10:29:07 -0700913 private void addPhotoView() {
Walter Jang31a74ad2015-10-02 19:17:39 -0700914 // Get the kind section data and values delta that we will display in the photo view
Walter Jangcbd431d2015-10-06 13:07:30 -0700915 final KindSectionDataList kindSectionDataList =
916 mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
917 final Pair<KindSectionData,ValuesDelta> photoToDisplay =
918 kindSectionDataList.getEntryToDisplay(mPhotoId);
919 if (photoToDisplay == null) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700920 wlog("photo: no kind section data parsed");
Walter Jangcbd431d2015-10-06 13:07:30 -0700921 mPhotoView.setVisibility(View.GONE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700922 return;
923 }
Walter Jang06f73a12015-06-17 11:15:48 -0700924
Walter Jang31a74ad2015-10-02 19:17:39 -0700925 // Set the photo view
Walter Jangcbd431d2015-10-06 13:07:30 -0700926 mPhotoView.setPhoto(photoToDisplay.second, mMaterialPalette);
Walter Jang31a74ad2015-10-02 19:17:39 -0700927
928 // Find the raw contact ID and values delta that will be written when the photo is edited
Walter Jangda258ff2015-11-17 11:06:02 -0800929 final Pair<KindSectionData, ValuesDelta> photoToWrite = kindSectionDataList.getEntryToWrite(
Walter Jang82acd422015-10-17 14:01:27 -0700930 mPhotoId, mPrimaryAccount, mIsUserProfile);
Walter Jangda258ff2015-11-17 11:06:02 -0800931 if (photoToWrite == null) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700932 mPhotoView.setReadOnly(true);
933 return;
934 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700935 mPhotoView.setReadOnly(false);
Walter Jangda258ff2015-11-17 11:06:02 -0800936 mPhotoRawContactId = photoToWrite.first.getRawContactDelta().getRawContactId();
937 mPhotoValuesDelta = photoToWrite.second;
Walter Jang31a74ad2015-10-02 19:17:39 -0700938 }
939
Walter Jangf5dfea42015-09-16 12:30:36 -0700940 private void addKindSectionViews() {
941 // Sort the kinds
Walter Jangcbd431d2015-10-06 13:07:30 -0700942 final TreeSet<Map.Entry<String,KindSectionDataList>> entries =
Walter Jang192a01c2015-09-22 15:23:55 -0700943 new TreeSet<>(KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR);
Walter Jangf5dfea42015-09-16 12:30:36 -0700944 entries.addAll(mKindSectionDataMap.entrySet());
Walter Jang3efae4a2015-02-18 11:12:00 -0800945
Walter Jangf5dfea42015-09-16 12:30:36 -0700946 vlog("kind: " + entries.size() + " kindSection(s)");
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700947 int i = -1;
Walter Jangcbd431d2015-10-06 13:07:30 -0700948 for (Map.Entry<String, KindSectionDataList> entry : entries) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700949 i++;
950
Walter Jangf5dfea42015-09-16 12:30:36 -0700951 final String mimeType = entry.getKey();
Walter Jang82acd422015-10-17 14:01:27 -0700952
Tingting Wangf695eb32015-10-15 18:45:15 -0700953 if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang82acd422015-10-17 14:01:27 -0700954 if (mPrimaryNameKindSectionData == null) {
955 vlog("kind: " + i + " " + mimeType + " dropped");
956 continue;
Tingting Wangf695eb32015-10-15 18:45:15 -0700957 }
Walter Jang82acd422015-10-17 14:01:27 -0700958 vlog("kind: " + i + " " + mimeType + " using first entry only");
959 final KindSectionDataList kindSectionDataList = new KindSectionDataList();
960 kindSectionDataList.add(mPrimaryNameKindSectionData.first);
Walter Jangf5dfea42015-09-16 12:30:36 -0700961 final CompactKindSectionView kindSectionView = inflateKindSectionView(
Walter Jang82acd422015-10-17 14:01:27 -0700962 mKindSectionViews, kindSectionDataList, mimeType,
963 mPrimaryNameKindSectionData.second);
Walter Jangf5dfea42015-09-16 12:30:36 -0700964 mKindSectionViews.addView(kindSectionView);
Walter Jangf10ca152015-09-22 15:23:55 -0700965
966 // Keep a pointer to all the KindSectionsViews for each mimeType
967 getKindSectionViews(mimeType).add(kindSectionView);
Walter Jang82acd422015-10-17 14:01:27 -0700968 } else {
969 final KindSectionDataList kindSectionDataList = entry.getValue();
970
971 // Ignore mime types that we've already handled
972 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
973 vlog("kind: " + i + " " + mimeType + " dropped");
974 continue;
975 }
976
977 // Don't show more than one group editor on the compact editor.
978 // Groups will still be editable for each raw contact individually on the full editor.
979 if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)
980 && kindSectionDataList.size() > 1) {
981 vlog("kind: " + i + " " + mimeType + " dropped");
982 continue;
983 }
984
985 if (kindSectionDataList != null && !kindSectionDataList.isEmpty()) {
986 vlog("kind: " + i + " " + mimeType + " " + kindSectionDataList.size() +
987 " kindSectionData(s)");
988
989 final CompactKindSectionView kindSectionView = inflateKindSectionView(
990 mKindSectionViews, kindSectionDataList, mimeType,
991 /* primaryValueDelta =*/ null);
992 mKindSectionViews.addView(kindSectionView);
993
994 // Keep a pointer to all the KindSectionsViews for each mimeType
995 getKindSectionViews(mimeType).add(kindSectionView);
996 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800997 }
998 }
999 }
1000
Walter Jangf10ca152015-09-22 15:23:55 -07001001 private List<CompactKindSectionView> getKindSectionViews(String mimeType) {
1002 List<CompactKindSectionView> kindSectionViews = mKindSectionViewsMap.get(mimeType);
1003 if (kindSectionViews == null) {
1004 kindSectionViews = new ArrayList<>();
1005 mKindSectionViewsMap.put(mimeType, kindSectionViews);
1006 }
1007 return kindSectionViews;
1008 }
1009
Walter Jangf5dfea42015-09-16 12:30:36 -07001010 private CompactKindSectionView inflateKindSectionView(ViewGroup viewGroup,
Walter Jang82acd422015-10-17 14:01:27 -07001011 KindSectionDataList kindSectionDataList, String mimeType,
1012 ValuesDelta primaryValuesDelta) {
Walter Jangf5dfea42015-09-16 12:30:36 -07001013 final CompactKindSectionView kindSectionView = (CompactKindSectionView)
1014 mLayoutInflater.inflate(R.layout.compact_item_kind_section, viewGroup,
1015 /* attachToRoot =*/ false);
Walter Jang45b86d52015-10-15 15:23:16 -07001016 kindSectionView.setIsUserProfile(mIsUserProfile);
Walter Jangf5dfea42015-09-16 12:30:36 -07001017
Walter Jang192a01c2015-09-22 15:23:55 -07001018 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)
Walter Jangf5dfea42015-09-16 12:30:36 -07001019 || Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -07001020 // Phone numbers and email addresses are always displayed,
1021 // even if they are empty
Walter Jangf5dfea42015-09-16 12:30:36 -07001022 kindSectionView.setHideWhenEmpty(false);
1023 }
Walter Jangf5dfea42015-09-16 12:30:36 -07001024
Walter Jang192a01c2015-09-22 15:23:55 -07001025 // Since phone numbers and email addresses displayed even if they are empty,
1026 // they will be the only types you add new values to initially for new contacts
1027 kindSectionView.setShowOneEmptyEditor(true);
1028
Walter Jang82acd422015-10-17 14:01:27 -07001029 // Sort non-name editors so they wind up in the order we want
1030 if (!StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jang192a01c2015-09-22 15:23:55 -07001031 Collections.sort(kindSectionDataList, new EditorComparator(getContext()));
Walter Jang3e5ae0d2015-09-20 12:43:37 -07001032 }
1033
Walter Jang82acd422015-10-17 14:01:27 -07001034 kindSectionView.setState(kindSectionDataList, mViewIdGenerator, mListener,
1035 primaryValuesDelta);
Walter Jangf5dfea42015-09-16 12:30:36 -07001036
1037 return kindSectionView;
Walter Jangcab3dce2015-02-09 17:48:03 -08001038 }
1039
Walter Jange3945952015-10-27 12:44:54 -07001040 void maybeSetReadOnlyDisplayNameAsPrimary(String readOnlyDisplayName) {
1041 if (TextUtils.isEmpty(readOnlyDisplayName)) return;
1042 final CompactKindSectionView primaryNameKindSectionView = getPrimaryNameKindSectionView();
1043 if (primaryNameKindSectionView != null && primaryNameKindSectionView.isEmptyName()) {
1044 vlog("name: using read only display name as primary name");
1045 primaryNameKindSectionView.setName(readOnlyDisplayName);
1046 }
1047 }
1048
1049 private CompactKindSectionView getPrimaryNameKindSectionView() {
Walter Jang4f5594a2015-10-06 18:40:31 -07001050 final List<CompactKindSectionView> kindSectionViews
1051 = mKindSectionViewsMap.get(StructuredName.CONTENT_ITEM_TYPE);
Walter Jange3945952015-10-27 12:44:54 -07001052 return kindSectionViews == null || kindSectionViews.isEmpty()
1053 ? null : kindSectionViews.get(0);
Walter Jang4f5594a2015-10-06 18:40:31 -07001054 }
1055
Walter Jangd6753152015-10-02 09:23:13 -07001056 private void showAllFields() {
Walter Jang79658e12015-09-24 10:36:26 -07001057 // Stop hiding empty editors and allow the user to enter values for all kinds now
1058 for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
1059 final CompactKindSectionView kindSectionView =
1060 (CompactKindSectionView) mKindSectionViews.getChildAt(i);
1061 kindSectionView.setHideWhenEmpty(false);
1062 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
1063 }
1064 mIsExpanded = true;
Walter Jang79658e12015-09-24 10:36:26 -07001065
Walter Jangf5dfea42015-09-16 12:30:36 -07001066 // Hide the more fields button
1067 mMoreFields.setVisibility(View.GONE);
Walter Jangcab3dce2015-02-09 17:48:03 -08001068 }
1069
Walter Jangbf63a6d2015-05-05 09:14:35 -07001070 private static void vlog(String message) {
1071 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1072 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -08001073 }
1074 }
Walter Jang363d3fd2015-09-16 10:29:07 -07001075
1076 private static void wlog(String message) {
1077 if (Log.isLoggable(TAG, Log.WARN)) {
1078 Log.w(TAG, message);
1079 }
1080 }
1081
1082 private static void elog(String message) {
1083 Log.e(TAG, message);
1084 }
Walter Jangcab3dce2015-02-09 17:48:03 -08001085}