blob: 8c3ff88125ce1234f6caf85a2f1a318ae802218e [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 Mai98868d32016-09-14 11:55:04 -070066import com.android.contacts.common.model.dataitem.CustomDataItem;
Gary Maib6e28d22016-09-12 14:04:53 -070067import com.android.contacts.common.model.dataitem.DataKind;
68import com.android.contacts.common.util.AccountsListAdapter;
69import com.android.contacts.common.util.MaterialColorMapUtils;
70import com.android.contacts.util.UiClosables;
71
Walter Jang708774a2015-10-16 09:32:06 -070072import java.io.FileNotFoundException;
Walter Jangcab3dce2015-02-09 17:48:03 -080073import java.util.ArrayList;
Walter Jangf5dfea42015-09-16 12:30:36 -070074import java.util.Arrays;
Walter Jang192a01c2015-09-22 15:23:55 -070075import java.util.Collections;
Walter Jangf5dfea42015-09-16 12:30:36 -070076import java.util.Comparator;
Walter Jang19d985f2015-07-01 13:36:27 -070077import java.util.HashMap;
Walter Jangcab3dce2015-02-09 17:48:03 -080078import java.util.List;
Walter Jang19d985f2015-07-01 13:36:27 -070079import java.util.Map;
Gary Mai98868d32016-09-14 11:55:04 -070080import java.util.Set;
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 Jangb6ca2722015-02-20 11:10:25 -080090 /**
91 * Callbacks for hosts of {@link CompactRawContactsEditorView}s.
92 */
93 public interface Listener {
94
95 /**
Walter Jang151f3e62015-02-26 15:29:40 -080096 * Invoked when the structured name editor field has changed.
97 *
98 * @param rawContactId The raw contact ID from the underlying {@link RawContactDelta}.
99 * @param valuesDelta The values from the underlying {@link RawContactDelta}.
100 */
101 public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta);
Walter Jang708ea9e2015-09-10 15:42:05 -0700102
103 /**
104 * Invoked when the compact editor should rebind editors for a new account.
105 *
106 * @param oldState Old data being edited.
107 * @param oldAccount Old account associated with oldState.
108 * @param newAccount New account to be used.
109 */
110 public void onRebindEditorsForNewContact(RawContactDelta oldState,
111 AccountWithDataSet oldAccount, AccountWithDataSet newAccount);
Walter Jang79658e12015-09-24 10:36:26 -0700112
113 /**
114 * Invoked when no editors could be bound for the contact.
115 */
116 public void onBindEditorsFailed();
Walter Jangd6753152015-10-02 09:23:13 -0700117
118 /**
119 * Invoked after editors have been bound for the contact.
120 */
121 public void onEditorsBound();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700122
123 /**
Tingting Wang5585c6e2015-10-14 11:19:58 -0700124 * Invoked when a rawcontact from linked contacts is selected in editor.
Tingting Wang655ad1a2015-10-05 17:51:14 -0700125 */
Tingting Wang91cee282015-10-07 13:48:17 -0700126 public void onRawContactSelected(Uri uri, long rawContactId, boolean isReadOnly);
Walter Jang3f18d612015-10-07 16:01:05 -0700127
128 /**
129 * Returns the map of raw contact IDs to newly taken or selected photos that have not
130 * yet been saved to CP2.
131 */
132 public Bundle getUpdatedPhotos();
Tingting Wang655ad1a2015-10-05 17:51:14 -0700133 }
134
135 /**
136 * Used to list the account info for the given raw contacts list.
137 */
138 private static final class RawContactAccountListAdapter extends BaseAdapter {
139 private final LayoutInflater mInflater;
140 private final Context mContext;
141 private final RawContactDeltaList mRawContactDeltas;
142
143 public RawContactAccountListAdapter(Context context, RawContactDeltaList rawContactDeltas) {
144 mContext = context;
145 mRawContactDeltas = new RawContactDeltaList();
146 for (RawContactDelta rawContactDelta : rawContactDeltas) {
Walter Jang23709542015-10-22 12:50:58 -0700147 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
Tingting Wang655ad1a2015-10-05 17:51:14 -0700148 mRawContactDeltas.add(rawContactDelta);
149 }
150 }
151 mInflater = LayoutInflater.from(context);
152 }
153
154 @Override
155 public View getView(int position, View convertView, ViewGroup parent) {
156 final View resultView = convertView != null ? convertView
157 : mInflater.inflate(R.layout.account_selector_list_item, parent, false);
158
159 final RawContactDelta rawContactDelta = mRawContactDeltas.get(position);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700160
161 final TextView text1 = (TextView) resultView.findViewById(android.R.id.text1);
Walter Jang9488a762015-10-16 13:42:48 -0700162 final AccountType accountType = rawContactDelta.getRawContactAccountType(mContext);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700163 text1.setText(accountType.getDisplayLabel(mContext));
164
Tingting Wang655ad1a2015-10-05 17:51:14 -0700165 final TextView text2 = (TextView) resultView.findViewById(android.R.id.text2);
Walter Jang9488a762015-10-16 13:42:48 -0700166 final String accountName = rawContactDelta.getAccountName();
Tingting Wanga0b69402015-10-13 14:27:58 -0700167 if (TextUtils.isEmpty(accountName)) {
168 text2.setVisibility(View.GONE);
169 } else {
Walter Jang9488a762015-10-16 13:42:48 -0700170 // Truncate email addresses in the middle so we don't lose the domain
Tingting Wanga0b69402015-10-13 14:27:58 -0700171 text2.setText(accountName);
172 text2.setEllipsize(TextUtils.TruncateAt.MIDDLE);
173 }
Tingting Wang655ad1a2015-10-05 17:51:14 -0700174
175 final ImageView icon = (ImageView) resultView.findViewById(android.R.id.icon);
176 icon.setImageDrawable(accountType.getDisplayIcon(mContext));
177
178 return resultView;
179 }
180
181 @Override
182 public int getCount() {
183 return mRawContactDeltas.size();
184 }
185
186 @Override
187 public RawContactDelta getItem(int position) {
188 return mRawContactDeltas.get(position);
189 }
190
191 @Override
192 public long getItemId(int position) {
193 return getItem(position).getRawContactId();
194 }
Walter Jang151f3e62015-02-26 15:29:40 -0800195 }
196
Walter Jang192a01c2015-09-22 15:23:55 -0700197 /**
198 * Sorts kinds roughly the same as quick contacts; we diverge in the following ways:
199 * <ol>
200 * <li>All names are together at the top.</li>
201 * <li>IM is moved up after addresses</li>
202 * <li>SIP addresses are moved to below phone numbers</li>
Walter Jang79658e12015-09-24 10:36:26 -0700203 * <li>Group membership is placed at the end</li>
Walter Jang192a01c2015-09-22 15:23:55 -0700204 * </ol>
205 */
206 private static final class MimeTypeComparator implements Comparator<String> {
207
Walter Jangf5dfea42015-09-16 12:30:36 -0700208 private static final List<String> MIME_TYPE_ORDER = Arrays.asList(new String[] {
209 StructuredName.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700210 Nickname.CONTENT_ITEM_TYPE,
Walter Jang2ae21752015-09-25 11:25:38 -0700211 Organization.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700212 Phone.CONTENT_ITEM_TYPE,
213 SipAddress.CONTENT_ITEM_TYPE,
214 Email.CONTENT_ITEM_TYPE,
215 StructuredPostal.CONTENT_ITEM_TYPE,
216 Im.CONTENT_ITEM_TYPE,
217 Website.CONTENT_ITEM_TYPE,
Walter Jangf5dfea42015-09-16 12:30:36 -0700218 Event.CONTENT_ITEM_TYPE,
219 Relation.CONTENT_ITEM_TYPE,
Walter Jangf10ca152015-09-22 15:23:55 -0700220 Note.CONTENT_ITEM_TYPE,
221 GroupMembership.CONTENT_ITEM_TYPE
Walter Jangf5dfea42015-09-16 12:30:36 -0700222 });
223
224 @Override
Walter Jang192a01c2015-09-22 15:23:55 -0700225 public int compare(String mimeType1, String mimeType2) {
226 if (mimeType1 == mimeType2) return 0;
227 if (mimeType1 == null) return -1;
228 if (mimeType2 == null) return 1;
Walter Jangf5dfea42015-09-16 12:30:36 -0700229
230 int index1 = MIME_TYPE_ORDER.indexOf(mimeType1);
231 int index2 = MIME_TYPE_ORDER.indexOf(mimeType2);
232
233 // Fallback to alphabetical ordering of the mime type if both are not found
234 if (index1 < 0 && index2 < 0) return mimeType1.compareTo(mimeType2);
235 if (index1 < 0) return 1;
236 if (index2 < 0) return -1;
237
238 return index1 < index2 ? -1 : 1;
239 }
240 }
241
Walter Jang79658e12015-09-24 10:36:26 -0700242 public static class SavedState extends BaseSavedState {
243
244 public static final Parcelable.Creator<SavedState> CREATOR =
245 new Parcelable.Creator<SavedState>() {
246 public SavedState createFromParcel(Parcel in) {
247 return new SavedState(in);
248 }
249 public SavedState[] newArray(int size) {
250 return new SavedState[size];
251 }
252 };
253
254 private boolean mIsExpanded;
255
256 public SavedState(Parcelable superState) {
257 super(superState);
258 }
259
260 private SavedState(Parcel in) {
261 super(in);
262 mIsExpanded = in.readInt() != 0;
263 }
264
265 @Override
266 public void writeToParcel(Parcel out, int flags) {
267 super.writeToParcel(out, flags);
268 out.writeInt(mIsExpanded ? 1 : 0);
269 }
270 }
271
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700272 private CompactRawContactsEditorView.Listener mListener;
Walter Jangb6ca2722015-02-20 11:10:25 -0800273
Walter Jangcab3dce2015-02-09 17:48:03 -0800274 private AccountTypeManager mAccountTypeManager;
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700275 private AccountDisplayInfoFactory mAccountDisplayInfoFactory;
Walter Jangcab3dce2015-02-09 17:48:03 -0800276 private LayoutInflater mLayoutInflater;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800277
Walter Jangcab3dce2015-02-09 17:48:03 -0800278 private ViewIdGenerator mViewIdGenerator;
Walter Jangf46abd82015-02-20 16:52:04 -0800279 private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
Gary Mai98868d32016-09-14 11:55:04 -0700280 private long mAggregatePhotoId = -1;
Walter Jang708ea9e2015-09-10 15:42:05 -0700281 private boolean mHasNewContact;
282 private boolean mIsUserProfile;
283 private AccountWithDataSet mPrimaryAccount;
Walter Jang9a552372016-08-24 11:51:05 -0700284 private RawContactDeltaList mRawContactDeltas;
285 private long mRawContactIdToDisplayAlone = -1;
286 private boolean mRawContactDisplayAloneIsReadOnly;
287 private boolean mIsEditingReadOnlyRawContactWithNewContact;
Gary Mai98868d32016-09-14 11:55:04 -0700288 private KindSectionDataList mPhotoKindSectionDataList = new KindSectionDataList();
289 private Map<String, KindSectionData> mKindSectionDataMap = new HashMap<>();
290 private Set<String> mSortedMimetypes = new TreeSet<>(new MimeTypeComparator());
Walter Jangcab3dce2015-02-09 17:48:03 -0800291
Walter Jang708ea9e2015-09-10 15:42:05 -0700292 // Account header
293 private View mAccountHeaderContainer;
294 private TextView mAccountHeaderType;
295 private TextView mAccountHeaderName;
Tingting Wang91cee282015-10-07 13:48:17 -0700296 private ImageView mAccountHeaderIcon;
Gary Mai46cb3102016-08-10 18:14:09 -0700297 private ImageView mAccountHeaderExpanderIcon;
Walter Jang708ea9e2015-09-10 15:42:05 -0700298
Tingting Wang655ad1a2015-10-05 17:51:14 -0700299 // Raw contacts selector
300 private View mRawContactContainer;
301 private TextView mRawContactSummary;
Tingting Wang655ad1a2015-10-05 17:51:14 -0700302
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700303 private CompactPhotoEditorView mPhotoView;
Walter Jangf5dfea42015-09-16 12:30:36 -0700304 private ViewGroup mKindSectionViews;
Gary Mai98868d32016-09-14 11:55:04 -0700305 private Map<String, CompactKindSectionView> mKindSectionViewMap = new HashMap<>();
Walter Jangb6ca2722015-02-20 11:10:25 -0800306 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -0800307
Walter Jang79658e12015-09-24 10:36:26 -0700308 private boolean mIsExpanded;
Walter Jangcbd431d2015-10-06 13:07:30 -0700309
Walter Jang3efae4a2015-02-18 11:12:00 -0800310 private long mPhotoRawContactId;
Walter Jang31a74ad2015-10-02 19:17:39 -0700311 private ValuesDelta mPhotoValuesDelta;
Walter Jang82acd422015-10-17 14:01:27 -0700312
Walter Jangcab3dce2015-02-09 17:48:03 -0800313 public CompactRawContactsEditorView(Context context) {
314 super(context);
315 }
316
317 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
318 super(context, attrs);
319 }
320
Walter Jangb6ca2722015-02-20 11:10:25 -0800321 /**
322 * Sets the receiver for {@link CompactRawContactsEditorView} callbacks.
323 */
324 public void setListener(Listener listener) {
325 mListener = listener;
326 }
327
Walter Jangcab3dce2015-02-09 17:48:03 -0800328 @Override
329 protected void onFinishInflate() {
330 super.onFinishInflate();
331
332 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700333 mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forWritableAccounts(getContext());
Walter Jangcab3dce2015-02-09 17:48:03 -0800334 mLayoutInflater = (LayoutInflater)
335 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
336
Walter Jang708ea9e2015-09-10 15:42:05 -0700337 // Account header
Gary Mai46cb3102016-08-10 18:14:09 -0700338 mAccountHeaderContainer = findViewById(R.id.account_header_container);
Walter Jang708ea9e2015-09-10 15:42:05 -0700339 mAccountHeaderType = (TextView) findViewById(R.id.account_type);
340 mAccountHeaderName = (TextView) findViewById(R.id.account_name);
Tingting Wang91cee282015-10-07 13:48:17 -0700341 mAccountHeaderIcon = (ImageView) findViewById(R.id.account_type_icon);
Gary Mai46cb3102016-08-10 18:14:09 -0700342 mAccountHeaderExpanderIcon = (ImageView) findViewById(R.id.account_expander_icon);
Walter Jang708ea9e2015-09-10 15:42:05 -0700343
Tingting Wang655ad1a2015-10-05 17:51:14 -0700344 // Raw contacts selector
345 mRawContactContainer = findViewById(R.id.all_rawcontacts_accounts_container);
346 mRawContactSummary = (TextView) findViewById(R.id.rawcontacts_accounts_summary);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700347
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700348 mPhotoView = (CompactPhotoEditorView) findViewById(R.id.photo_editor);
Walter Jangf5dfea42015-09-16 12:30:36 -0700349 mKindSectionViews = (LinearLayout) findViewById(R.id.kind_section_views);
Walter Jangb6ca2722015-02-20 11:10:25 -0800350 mMoreFields = findViewById(R.id.more_fields);
351 mMoreFields.setOnClickListener(this);
352 }
353
Walter Jangb6ca2722015-02-20 11:10:25 -0800354 @Override
355 public void onClick(View view) {
Walter Jangf5dfea42015-09-16 12:30:36 -0700356 if (view.getId() == R.id.more_fields) {
Walter Jangd6753152015-10-02 09:23:13 -0700357 showAllFields();
Walter Jangb6ca2722015-02-20 11:10:25 -0800358 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800359 }
360
361 @Override
362 public void setEnabled(boolean enabled) {
363 super.setEnabled(enabled);
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700364 final int childCount = mKindSectionViews.getChildCount();
365 for (int i = 0; i < childCount; i++) {
366 mKindSectionViews.getChildAt(i).setEnabled(enabled);
Walter Jangcab3dce2015-02-09 17:48:03 -0800367 }
368 }
369
Walter Jang79658e12015-09-24 10:36:26 -0700370 @Override
371 public Parcelable onSaveInstanceState() {
372 final Parcelable superState = super.onSaveInstanceState();
373 final SavedState savedState = new SavedState(superState);
374 savedState.mIsExpanded = mIsExpanded;
375 return savedState;
376 }
377
378 @Override
379 public void onRestoreInstanceState(Parcelable state) {
380 if(!(state instanceof SavedState)) {
381 super.onRestoreInstanceState(state);
382 return;
383 }
384 final SavedState savedState = (SavedState) state;
385 super.onRestoreInstanceState(savedState.getSuperState());
386 mIsExpanded = savedState.mIsExpanded;
Walter Jangd6753152015-10-02 09:23:13 -0700387 if (mIsExpanded) {
388 showAllFields();
Walter Jang79658e12015-09-24 10:36:26 -0700389 }
390 }
391
Walter Jang3efae4a2015-02-18 11:12:00 -0800392 /**
Walter Jang31a74ad2015-10-02 19:17:39 -0700393 * Pass through to {@link CompactPhotoEditorView#setListener}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800394 */
Walter Jang31a74ad2015-10-02 19:17:39 -0700395 public void setPhotoListener(CompactPhotoEditorView.Listener listener) {
396 mPhotoView.setListener(listener);
Walter Jang3efae4a2015-02-18 11:12:00 -0800397 }
398
Walter Jang31a74ad2015-10-02 19:17:39 -0700399 public void removePhoto() {
Walter Jang45b86d52015-10-15 15:23:16 -0700400 mPhotoValuesDelta.setFromTemplate(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700401 mPhotoValuesDelta.put(Photo.PHOTO, (byte[]) null);
402
403 mPhotoView.removePhoto();
Walter Jang3efae4a2015-02-18 11:12:00 -0800404 }
405
406 /**
Walter Jang41b3ea12015-03-09 17:30:06 -0700407 * Pass through to {@link CompactPhotoEditorView#setFullSizedPhoto(Uri)}.
408 */
409 public void setFullSizePhoto(Uri photoUri) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700410 mPhotoView.setFullSizedPhoto(photoUri);
Walter Jang41b3ea12015-03-09 17:30:06 -0700411 }
412
Walter Jang31a74ad2015-10-02 19:17:39 -0700413 public void updatePhoto(Uri photoUri) {
Walter Jang45b86d52015-10-15 15:23:16 -0700414 mPhotoValuesDelta.setFromTemplate(false);
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700415 // Unset primary for all photos
Wenyi Wang9086fb92015-11-16 09:58:20 -0800416 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700417 // Mark the currently displayed photo as primary
418 mPhotoValuesDelta.setSuperPrimary(true);
Walter Jang31a74ad2015-10-02 19:17:39 -0700419
Wenyi Wang4c9cf3a2015-10-15 17:58:23 -0700420 // Even though high-res photos cannot be saved by passing them via
421 // an EntityDeltaList (since they cause the Bundle size limit to be
422 // exceeded), we still pass a low-res thumbnail. This simplifies
423 // code all over the place, because we don't have to test whether
424 // there is a change in EITHER the delta-list OR a changed photo...
425 // this way, there is always a change in the delta-list.
426 try {
427 final byte[] bytes = EditorUiUtils.getCompressedThumbnailBitmapBytes(
428 getContext(), photoUri);
429 if (bytes != null) {
430 mPhotoValuesDelta.setPhoto(bytes);
431 }
432 } catch (FileNotFoundException e) {
433 elog("Failed to get bitmap from photo Uri");
434 }
435
Walter Jang31a74ad2015-10-02 19:17:39 -0700436 mPhotoView.setFullSizedPhoto(photoUri);
437 }
438
Wenyi Wang9086fb92015-11-16 09:58:20 -0800439 private void unsetSuperPrimaryFromAllPhotos() {
Gary Mai98868d32016-09-14 11:55:04 -0700440 for (int i = 0; i < mPhotoKindSectionDataList.size(); i++) {
441 final KindSectionData kindSectionData = mPhotoKindSectionDataList.get(0);
442 final List<ValuesDelta> valuesDeltas = kindSectionData.getNonEmptyValuesDeltas();
443 for (int j = 0; j < valuesDeltas.size(); j++) {
444 valuesDeltas.get(j).setSuperPrimary(false);
Walter Jang3f18d612015-10-07 16:01:05 -0700445 }
446 }
447 }
448
Walter Jang41b3ea12015-03-09 17:30:06 -0700449 /**
Walter Janga5e4bb22015-02-24 13:08:16 -0800450 * Pass through to {@link CompactPhotoEditorView#isWritablePhotoSet}.
Walter Jang3efae4a2015-02-18 11:12:00 -0800451 */
452 public boolean isWritablePhotoSet() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700453 return mPhotoView.isWritablePhotoSet();
Walter Jang3efae4a2015-02-18 11:12:00 -0800454 }
455
456 /**
Walter Jang3efae4a2015-02-18 11:12:00 -0800457 * Get the raw contact ID for the CompactHeaderView photo.
458 */
Walter Jang3efae4a2015-02-18 11:12:00 -0800459 public long getPhotoRawContactId() {
460 return mPhotoRawContactId;
461 }
462
Walter Jang4f5594a2015-10-06 18:40:31 -0700463 public StructuredNameEditorView getPrimaryNameEditorView() {
Walter Jange3945952015-10-27 12:44:54 -0700464 final CompactKindSectionView primaryNameKindSectionView = getPrimaryNameKindSectionView();
465 return primaryNameKindSectionView == null
466 ? null : primaryNameKindSectionView.getPrimaryNameEditorView();
Walter Jang4f5594a2015-10-06 18:40:31 -0700467 }
468
Walter Jang31a74ad2015-10-02 19:17:39 -0700469 /**
470 * Returns a data holder for every non-default/non-empty photo from each raw contact, whether
471 * the raw contact is writable or not.
472 */
473 public ArrayList<CompactPhotoSelectionFragment.Photo> getPhotos() {
474 final ArrayList<CompactPhotoSelectionFragment.Photo> photos = new ArrayList<>();
475
Walter Jang3f18d612015-10-07 16:01:05 -0700476 final Bundle updatedPhotos = mListener == null ? null : mListener.getUpdatedPhotos();
477
Gary Mai98868d32016-09-14 11:55:04 -0700478 for (int i = 0; i < mPhotoKindSectionDataList.size(); i++) {
479 final KindSectionData kindSectionData = mPhotoKindSectionDataList.get(i);
Walter Jang31a74ad2015-10-02 19:17:39 -0700480 final AccountType accountType = kindSectionData.getAccountType();
Walter Jang228e02f2015-11-13 09:11:01 -0800481 final List<ValuesDelta> valuesDeltas = kindSectionData.getNonEmptyValuesDeltas();
482 if (valuesDeltas.isEmpty()) continue;
483 for (int j = 0; j < valuesDeltas.size(); j++) {
484 final ValuesDelta valuesDelta = valuesDeltas.get(j);
Walter Jang31a74ad2015-10-02 19:17:39 -0700485 final Bitmap bitmap = EditorUiUtils.getPhotoBitmap(valuesDelta);
486 if (bitmap == null) continue;
487
488 final CompactPhotoSelectionFragment.Photo photo =
489 new CompactPhotoSelectionFragment.Photo();
490 photo.titleRes = accountType.titleRes;
491 photo.iconRes = accountType.iconRes;
492 photo.syncAdapterPackageName = accountType.syncAdapterPackageName;
493 photo.valuesDelta = valuesDelta;
494 photo.primary = valuesDelta.isSuperPrimary();
495 photo.kindSectionDataListIndex = i;
496 photo.valuesDeltaListIndex = j;
Walter Jangda258ff2015-11-17 11:06:02 -0800497 photo.photoId = valuesDelta.getId();
Walter Jang3f18d612015-10-07 16:01:05 -0700498
499 if (updatedPhotos != null) {
500 photo.updatedPhotoUri = (Uri) updatedPhotos.get(String.valueOf(
501 kindSectionData.getRawContactDelta().getRawContactId()));
502 }
503
Wenyi Wangd375c352016-03-28 15:12:45 -0700504 final CharSequence accountTypeLabel = accountType.getDisplayLabel(getContext());
505 photo.accountType = accountTypeLabel == null ? "" : accountTypeLabel.toString();
506 final String accountName = kindSectionData.getRawContactDelta().getAccountName();
507 photo.accountName = accountName == null ? "" : accountName;
Wenyi Wangab360172015-12-15 10:35:58 -0800508
Walter Jang31a74ad2015-10-02 19:17:39 -0700509 photos.add(photo);
510 }
511 }
512
513 return photos;
514 }
515
516 /**
517 * Marks the raw contact photo given as primary for the aggregate contact and updates the
518 * UI.
519 */
520 public void setPrimaryPhoto(CompactPhotoSelectionFragment.Photo photo) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700521 // Find the values delta to mark as primary
522 if (photo.kindSectionDataListIndex < 0
Gary Mai98868d32016-09-14 11:55:04 -0700523 || photo.kindSectionDataListIndex >= mPhotoKindSectionDataList.size()) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700524 wlog("Invalid kind section data list index");
525 return;
526 }
527 final KindSectionData kindSectionData =
Gary Mai98868d32016-09-14 11:55:04 -0700528 mPhotoKindSectionDataList.get(photo.kindSectionDataListIndex);
Walter Jang228e02f2015-11-13 09:11:01 -0800529 final List<ValuesDelta> valuesDeltaList = kindSectionData.getNonEmptyValuesDeltas();
Walter Jang31a74ad2015-10-02 19:17:39 -0700530 if (photo.valuesDeltaListIndex >= valuesDeltaList.size()) {
531 wlog("Invalid values delta list index");
532 return;
533 }
Walter Jangda258ff2015-11-17 11:06:02 -0800534
535 // Update values delta
Walter Jang31a74ad2015-10-02 19:17:39 -0700536 final ValuesDelta valuesDelta = valuesDeltaList.get(photo.valuesDeltaListIndex);
537 valuesDelta.setFromTemplate(false);
Wenyi Wang9086fb92015-11-16 09:58:20 -0800538 unsetSuperPrimaryFromAllPhotos();
Wenyi Wangb8ab8612015-10-27 11:18:49 -0700539 valuesDelta.setSuperPrimary(true);
Walter Jangda258ff2015-11-17 11:06:02 -0800540
Walter Jang31a74ad2015-10-02 19:17:39 -0700541 // Update the UI
542 mPhotoView.setPhoto(valuesDelta, mMaterialPalette);
543 }
544
Walter Jangd35e5ef2015-02-24 09:18:16 -0800545 public View getAggregationAnchorView() {
Gary Mai98868d32016-09-14 11:55:04 -0700546 CompactKindSectionView nameView = mKindSectionViewMap.get(StructuredName.CONTENT_ITEM_TYPE);
547 return nameView != null ? nameView.getChildAt(0).findViewById(R.id.anchor_view) : null;
Walter Jangd35e5ef2015-02-24 09:18:16 -0800548 }
549
Walter Jangf10ca152015-09-22 15:23:55 -0700550 public void setGroupMetaData(Cursor groupMetaData) {
Gary Mai98868d32016-09-14 11:55:04 -0700551 final CompactKindSectionView groupKindSectionView =
552 mKindSectionViewMap.get(GroupMembership.CONTENT_ITEM_TYPE);
553 if (groupKindSectionView == null) {
554 return;
555 }
556 groupKindSectionView.setGroupMetaData(groupMetaData);
557 if (mIsExpanded) {
558 groupKindSectionView.setHideWhenEmpty(false);
559 groupKindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
Walter Jangf10ca152015-09-22 15:23:55 -0700560 }
561 }
562
Walter Jangf46abd82015-02-20 16:52:04 -0800563 public void setState(RawContactDeltaList rawContactDeltas,
Walter Jang06f73a12015-06-17 11:15:48 -0700564 MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
Walter Jange3945952015-10-27 12:44:54 -0700565 long photoId, boolean hasNewContact, boolean isUserProfile,
Walter Jang9a552372016-08-24 11:51:05 -0700566 AccountWithDataSet primaryAccount, long rawContactIdToDisplayAlone,
567 boolean rawContactDisplayAloneIsReadOnly,
568 boolean isEditingReadOnlyRawContactWithNewContact) {
Marcus Hagerottf04c9752016-09-08 15:22:57 -0700569
Walter Jang9a552372016-08-24 11:51:05 -0700570 mRawContactDeltas = rawContactDeltas;
571 mRawContactIdToDisplayAlone = rawContactIdToDisplayAlone;
572 mRawContactDisplayAloneIsReadOnly = rawContactDisplayAloneIsReadOnly;
573 mIsEditingReadOnlyRawContactWithNewContact = isEditingReadOnlyRawContactWithNewContact;
574
Walter Jang363d3fd2015-09-16 10:29:07 -0700575 mKindSectionDataMap.clear();
Gary Mai98868d32016-09-14 11:55:04 -0700576 mKindSectionViewMap.clear();
Walter Jangf5dfea42015-09-16 12:30:36 -0700577 mKindSectionViews.removeAllViews();
Walter Jang363d3fd2015-09-16 10:29:07 -0700578 mMoreFields.setVisibility(View.VISIBLE);
Walter Jangcab3dce2015-02-09 17:48:03 -0800579
Walter Jangf46abd82015-02-20 16:52:04 -0800580 mMaterialPalette = materialPalette;
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700581 mViewIdGenerator = viewIdGenerator;
Gary Mai98868d32016-09-14 11:55:04 -0700582 mAggregatePhotoId = photoId;
Tingting Wangf695eb32015-10-15 18:45:15 -0700583
Walter Jang708ea9e2015-09-10 15:42:05 -0700584 mHasNewContact = hasNewContact;
585 mIsUserProfile = isUserProfile;
586 mPrimaryAccount = primaryAccount;
587 if (mPrimaryAccount == null) {
588 mPrimaryAccount = ContactEditorUtils.getInstance(getContext()).getDefaultAccount();
589 }
590 vlog("state: primary " + mPrimaryAccount);
Walter Jangcab3dce2015-02-09 17:48:03 -0800591
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700592 // Parse the given raw contact deltas
593 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
594 elog("No raw contact deltas");
Walter Jang79658e12015-09-24 10:36:26 -0700595 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jangf5dfea42015-09-16 12:30:36 -0700596 return;
597 }
Walter Jangcbd431d2015-10-06 13:07:30 -0700598 parseRawContactDeltas(rawContactDeltas);
Walter Jang79658e12015-09-24 10:36:26 -0700599 if (mKindSectionDataMap.isEmpty()) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700600 elog("No kind section data parsed from RawContactDelta(s)");
Walter Jang79658e12015-09-24 10:36:26 -0700601 if (mListener != null) mListener.onBindEditorsFailed();
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700602 return;
603 }
Walter Jang82acd422015-10-17 14:01:27 -0700604
Gary Mai98868d32016-09-14 11:55:04 -0700605 final KindSectionData nameSectionData =
606 mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
607 // Ensure that a structured name and photo exists
608 if (nameSectionData != null) {
Walter Jang82acd422015-10-17 14:01:27 -0700609 final RawContactDelta rawContactDelta =
Gary Mai98868d32016-09-14 11:55:04 -0700610 nameSectionData.getRawContactDelta();
Walter Jang82acd422015-10-17 14:01:27 -0700611 RawContactModifier.ensureKindExists(
612 rawContactDelta,
613 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700614 StructuredName.CONTENT_ITEM_TYPE);
Walter Jang82acd422015-10-17 14:01:27 -0700615 RawContactModifier.ensureKindExists(
616 rawContactDelta,
617 rawContactDelta.getAccountType(mAccountTypeManager),
Walter Jangcbd431d2015-10-06 13:07:30 -0700618 Photo.CONTENT_ITEM_TYPE);
619 }
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700620
621 // Setup the view
Walter Jang363d3fd2015-09-16 10:29:07 -0700622 addPhotoView();
Walter Jang9a552372016-08-24 11:51:05 -0700623 if (isSingleReadOnlyRawContact()) {
624 // We're want to display the inputs fields for a single read only raw contact
625 addReadOnlyRawContactEditorViews();
626 // Hide the "More fields" link
627 mMoreFields.setVisibility(View.GONE);
628 } else if (mIsEditingReadOnlyRawContactWithNewContact) {
629 // A new writable raw contact was created and joined with the read only contact
630 // that the user is trying to edit.
631 setupCompactEditorNormally();
Walter Jange3945952015-10-27 12:44:54 -0700632
Walter Jang9a552372016-08-24 11:51:05 -0700633 // TODO: Hide the raw contact selector since it will just contain the read-only raw
634 // contact and clicking that will just open the exact same editor. When we clean up
635 // the whole account header, selector, and raw contact selector mess, we can prevent
636 // the selector from being displayed in a less hacky way.
637 mRawContactContainer.setVisibility(View.GONE);
638 } else if (mRawContactDeltas.size() > 1) {
639 // We're editing an aggregate composed of more than one writable raw contacts
Walter Jangd6753152015-10-02 09:23:13 -0700640
Walter Jang9a552372016-08-24 11:51:05 -0700641 // TODO: Don't render any input fields. Eventually we will show a list of account
642 // types and names but for now just show the account selector and hide the "More fields"
643 // link.
Gary Mai98868d32016-09-14 11:55:04 -0700644 addAccountInfo();
Walter Jang9a552372016-08-24 11:51:05 -0700645 mMoreFields.setVisibility(View.GONE);
646 } else {
647 setupCompactEditorNormally();
648 }
Walter Jangd6753152015-10-02 09:23:13 -0700649 if (mListener != null) mListener.onEditorsBound();
Walter Jangcab3dce2015-02-09 17:48:03 -0800650 }
651
Walter Jang9a552372016-08-24 11:51:05 -0700652 private void setupCompactEditorNormally() {
Gary Mai98868d32016-09-14 11:55:04 -0700653 addAccountInfo();
Walter Jang9a552372016-08-24 11:51:05 -0700654 addKindSectionViews();
Walter Jangcab3dce2015-02-09 17:48:03 -0800655
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700656 mMoreFields.setVisibility(hasMoreFields() ? View.VISIBLE : View.GONE);
657
Walter Jang9a552372016-08-24 11:51:05 -0700658 if (mIsExpanded) showAllFields();
659 }
660
661 private boolean isSingleReadOnlyRawContact() {
662 return mRawContactDeltas.size() == 1
663 && mRawContactDeltas.get(0).getRawContactId() == mRawContactIdToDisplayAlone
664 && mRawContactDisplayAloneIsReadOnly;
665 }
666
Walter Jangcbd431d2015-10-06 13:07:30 -0700667 private void parseRawContactDeltas(RawContactDeltaList rawContactDeltas) {
Walter Jang363d3fd2015-09-16 10:29:07 -0700668 // Build the kind section data list map
Walter Jang192a01c2015-09-22 15:23:55 -0700669 vlog("parse: " + rawContactDeltas.size() + " rawContactDelta(s)");
670 for (int j = 0; j < rawContactDeltas.size(); j++) {
671 final RawContactDelta rawContactDelta = rawContactDeltas.get(j);
672 vlog("parse: " + j + " rawContactDelta" + rawContactDelta);
Walter Jang363d3fd2015-09-16 10:29:07 -0700673 if (rawContactDelta == null || !rawContactDelta.isVisible()) continue;
Walter Jangcab3dce2015-02-09 17:48:03 -0800674 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jang363d3fd2015-09-16 10:29:07 -0700675 if (accountType == null) continue;
676 final List<DataKind> dataKinds = accountType.getSortedDataKinds();
677 final int dataKindSize = dataKinds == null ? 0 : dataKinds.size();
678 vlog("parse: " + dataKindSize + " dataKinds(s)");
679 for (int i = 0; i < dataKindSize; i++) {
680 final DataKind dataKind = dataKinds.get(i);
Walter Jang9a552372016-08-24 11:51:05 -0700681 if (dataKind == null) {
682 vlog("parse: " + i + " " + dataKind.mimeType + " dropped null data kind");
Walter Jang192a01c2015-09-22 15:23:55 -0700683 continue;
684 }
685 final String mimeType = dataKind.mimeType;
686
687 // Skip psuedo mime types
688 if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
689 || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
690 vlog("parse: " + i + " " + dataKind.mimeType + " dropped pseudo type");
691 continue;
692 }
693
Gary Mai98868d32016-09-14 11:55:04 -0700694 // Skip custom fields
695 // TODO: Handle them when we implement editing custom fields.
696 if (CustomDataItem.MIMETYPE_CUSTOM_FIELD.equals(mimeType)) {
697 vlog("parse: " + i + " " + dataKind.mimeType + " dropped custom field");
698 continue;
699 }
700
701 // Add all photo data.
702 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
703 final KindSectionData photoKindSectionData =
704 new KindSectionData(accountType, dataKind, rawContactDelta);
705 mPhotoKindSectionDataList.add(photoKindSectionData);
706 vlog("parse: " + i + " " + dataKind.mimeType + " " +
707 photoKindSectionData.getValuesDeltas().size() + " value(s) " +
708 photoKindSectionData.getNonEmptyValuesDeltas().size() +
709 " non-empty value(s) " +
710 photoKindSectionData.getVisibleValuesDeltas().size() +
711 " visible value(s)");
712 continue;
713 }
714
715 // Skip the non-writable names when we're auto creating a new writable contact.
716 if (mIsEditingReadOnlyRawContactWithNewContact
717 && !accountType.areContactsWritable()
718 && StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
719 vlog("parse: " + i + " " + dataKind.mimeType + " dropped non-writable name");
720 continue;
721 }
722
723 // Skip non-photo data that doesn't belong to the single raw contact we're editing.
724 if (mRawContactIdToDisplayAlone > 0 &&
725 !rawContactDelta.getRawContactId().equals(mRawContactIdToDisplayAlone)) {
726 continue;
727 }
728
Walter Jang363d3fd2015-09-16 10:29:07 -0700729 final KindSectionData kindSectionData =
730 new KindSectionData(accountType, dataKind, rawContactDelta);
Gary Mai98868d32016-09-14 11:55:04 -0700731 mKindSectionDataMap.put(mimeType, kindSectionData);
732 mSortedMimetypes.add(mimeType);
Walter Jang192a01c2015-09-22 15:23:55 -0700733
Walter Jang363d3fd2015-09-16 10:29:07 -0700734 vlog("parse: " + i + " " + dataKind.mimeType + " " +
Walter Jang228e02f2015-11-13 09:11:01 -0800735 kindSectionData.getValuesDeltas().size() + " value(s) " +
Tingting Wangd482e0c2015-11-16 10:13:29 -0800736 kindSectionData.getNonEmptyValuesDeltas().size() + " non-empty value(s) " +
Wenyi Wang42d740c2015-11-13 14:23:54 -0800737 kindSectionData.getVisibleValuesDeltas().size() +
Walter Jang228e02f2015-11-13 09:11:01 -0800738 " visible value(s)");
Walter Jangac679af2015-06-01 12:17:06 -0700739 }
740 }
Walter Jang3efae4a2015-02-18 11:12:00 -0800741 }
742
Walter Jang9a552372016-08-24 11:51:05 -0700743 private void addReadOnlyRawContactEditorViews() {
744 final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
745 Context.LAYOUT_INFLATER_SERVICE);
746 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
747 getContext());
748
749 for (int i = 0; i < mRawContactDeltas.size(); i++) {
750 final RawContactDelta rawContactDelta = mRawContactDeltas.get(i);
751 if (!rawContactDelta.isVisible()) continue;
752 final AccountType type = rawContactDelta.getAccountType(accountTypes);
753 if (type.areContactsWritable()) continue;
754
755 final BaseRawContactEditorView editor = (BaseRawContactEditorView) inflater.inflate(
756 R.layout.raw_contact_readonly_editor_view, mKindSectionViews, false);
757 editor.setCollapsed(false);
758 mKindSectionViews.addView(editor);
759 editor.setState(rawContactDelta, type, mViewIdGenerator, mIsUserProfile);
760 }
761 }
762
Gary Mai98868d32016-09-14 11:55:04 -0700763 private void addAccountInfo() {
Walter Jang82acd422015-10-17 14:01:27 -0700764 mAccountHeaderContainer.setVisibility(View.GONE);
Walter Jang82acd422015-10-17 14:01:27 -0700765 mRawContactContainer.setVisibility(View.GONE);
Gary Mai98868d32016-09-14 11:55:04 -0700766 final KindSectionData nameSectionData =
767 mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
768 if (nameSectionData == null) return;
Walter Jang82acd422015-10-17 14:01:27 -0700769 final RawContactDelta rawContactDelta =
Gary Mai98868d32016-09-14 11:55:04 -0700770 nameSectionData.getRawContactDelta();
Walter Jang708ea9e2015-09-10 15:42:05 -0700771
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700772 final AccountDisplayInfo account =
773 mAccountDisplayInfoFactory.getAccountDisplayInfoFor(rawContactDelta);
774
Walter Jang708ea9e2015-09-10 15:42:05 -0700775 // Get the account information for the primary raw contact delta
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700776 final String accountLabel = mIsUserProfile
777 ? EditorUiUtils.getAccountHeaderLabelForMyProfile(getContext(), account)
778 : account.getNameLabel().toString();
Walter Jang708ea9e2015-09-10 15:42:05 -0700779
Walter Jang23709542015-10-22 12:50:58 -0700780 // Either the account header or selector should be shown, not both.
Walter Jang708ea9e2015-09-10 15:42:05 -0700781 final List<AccountWithDataSet> accounts =
782 AccountTypeManager.getInstance(getContext()).getAccounts(true);
Walter Jang82acd422015-10-17 14:01:27 -0700783 if (mHasNewContact && !mIsUserProfile) {
784 if (accounts.size() > 1) {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700785 addAccountSelector(rawContactDelta, accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700786 } else {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700787 addAccountHeader(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700788 }
Gary Mai98868d32016-09-14 11:55:04 -0700789 } else if (mIsUserProfile || !shouldHideAccountContainer(mRawContactDeltas)) {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700790 addAccountHeader(accountLabel);
Walter Jang708ea9e2015-09-10 15:42:05 -0700791 }
Walter Jang23709542015-10-22 12:50:58 -0700792
793 // The raw contact selector should only display linked raw contacts that can be edited in
794 // the full editor (i.e. they are not newly created raw contacts)
Tingting Wang2bb85d22015-10-23 13:01:09 -0700795 final RawContactAccountListAdapter adapter = new RawContactAccountListAdapter(getContext(),
Gary Mai98868d32016-09-14 11:55:04 -0700796 getRawContactDeltaListForSelector(mRawContactDeltas));
Tingting Wang2bb85d22015-10-23 13:01:09 -0700797 if (adapter.getCount() > 0) {
798 final String accountsSummary = getResources().getQuantityString(
799 R.plurals.compact_editor_linked_contacts_selector_title,
800 adapter.getCount(), adapter.getCount());
Walter Jang23709542015-10-22 12:50:58 -0700801 addRawContactAccountSelector(accountsSummary, adapter);
802 }
Walter Jang708ea9e2015-09-10 15:42:05 -0700803 }
804
Tingting Wang2bb85d22015-10-23 13:01:09 -0700805 private RawContactDeltaList getRawContactDeltaListForSelector(
806 RawContactDeltaList rawContactDeltas) {
807 // Sort raw contacts so google accounts come first
808 Collections.sort(rawContactDeltas, new RawContactDeltaComparator(getContext()));
809
810 final RawContactDeltaList result = new RawContactDeltaList();
Gary Mai98868d32016-09-14 11:55:04 -0700811 for (int i = 0; i < rawContactDeltas.size(); i++) {
812 final RawContactDelta rawContactDelta = rawContactDeltas.get(i);
Tingting Wang2bb85d22015-10-23 13:01:09 -0700813 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
814 // Only add raw contacts that can be opened in the editor
815 result.add(rawContactDelta);
816 }
817 }
818 // Don't return a list of size 1 that would just open the raw contact being edited
819 // in the compact editor in the full editor
820 if (result.size() == 1 && result.get(0).getRawContactAccountType(
821 getContext()).areContactsWritable()) {
822 result.clear();
823 return result;
824 }
825 return result;
826 }
827
Tingting Wang1594c6f2015-10-26 17:39:16 -0700828 // Returns true if there are multiple writable rawcontacts and no read-only ones,
829 // or there are both writable and read-only rawcontacts.
830 private boolean shouldHideAccountContainer(RawContactDeltaList rawContactDeltas) {
Tingting Wang2bb85d22015-10-23 13:01:09 -0700831 int writable = 0;
832 int readonly = 0;
833 for (RawContactDelta rawContactDelta : rawContactDeltas) {
834 if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
835 if (rawContactDelta.getRawContactAccountType(getContext()).areContactsWritable()) {
836 writable++;
837 } else {
838 readonly++;
839 }
840 }
841 }
842 return (writable > 1 || (writable > 0 && readonly > 0));
843 }
844
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700845 private void addAccountHeader(String accountLabel) {
Walter Jang82acd422015-10-17 14:01:27 -0700846 mAccountHeaderContainer.setVisibility(View.VISIBLE);
847
Walter Jangbe5e1b12015-10-17 11:38:29 -0700848 // Set the account name
Walter Jangbe5e1b12015-10-17 11:38:29 -0700849 mAccountHeaderName.setVisibility(View.VISIBLE);
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700850 mAccountHeaderName.setText(accountLabel);
Walter Jangbe5e1b12015-10-17 11:38:29 -0700851
852 // Set the account type
853 final String selectorTitle = getResources().getString(
854 R.string.compact_editor_account_selector_title);
855 mAccountHeaderType.setText(selectorTitle);
856
857 // Set the icon
Gary Mai98868d32016-09-14 11:55:04 -0700858 final KindSectionData nameSectionData =
859 mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
860 if (nameSectionData != null) {
Walter Jang82acd422015-10-17 14:01:27 -0700861 final RawContactDelta rawContactDelta =
Gary Mai98868d32016-09-14 11:55:04 -0700862 nameSectionData.getRawContactDelta();
Walter Jang82acd422015-10-17 14:01:27 -0700863 if (rawContactDelta != null) {
864 final AccountType accountType =
865 rawContactDelta.getRawContactAccountType(getContext());
866 mAccountHeaderIcon.setImageDrawable(accountType.getDisplayIcon(getContext()));
867 }
868 }
Walter Jangbe5e1b12015-10-17 11:38:29 -0700869
870 // Set the content description
871 mAccountHeaderContainer.setContentDescription(
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700872 EditorUiUtils.getAccountInfoContentDescription(accountLabel,
873 selectorTitle));
Walter Jangbe5e1b12015-10-17 11:38:29 -0700874 }
875
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700876 private void addAccountSelector(final RawContactDelta rawContactDelta, CharSequence nameLabel) {
Gary Mai46cb3102016-08-10 18:14:09 -0700877 // Show save to default account.
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700878 addAccountHeader(nameLabel.toString());
Gary Mai46cb3102016-08-10 18:14:09 -0700879 // Add handlers for choosing another account to save to.
880 mAccountHeaderExpanderIcon.setVisibility(View.VISIBLE);
881 mAccountHeaderContainer.setOnClickListener(new View.OnClickListener() {
Walter Jang708ea9e2015-09-10 15:42:05 -0700882 @Override
883 public void onClick(View v) {
884 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
885 final AccountsListAdapter adapter =
886 new AccountsListAdapter(getContext(),
887 AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE,
888 mPrimaryAccount);
Gary Mai46cb3102016-08-10 18:14:09 -0700889 popup.setWidth(mAccountHeaderContainer.getWidth());
890 popup.setAnchorView(mAccountHeaderContainer);
Walter Jang708ea9e2015-09-10 15:42:05 -0700891 popup.setAdapter(adapter);
892 popup.setModal(true);
893 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
894 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
895 @Override
896 public void onItemClick(AdapterView<?> parent, View view, int position,
897 long id) {
898 UiClosables.closeQuietly(popup);
899 final AccountWithDataSet newAccount = adapter.getItem(position);
900 if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
901 mListener.onRebindEditorsForNewContact(
Walter Jang82acd422015-10-17 14:01:27 -0700902 rawContactDelta,
Walter Jang708ea9e2015-09-10 15:42:05 -0700903 mPrimaryAccount,
904 newAccount);
905 }
906 }
907 });
908 popup.show();
909 }
910 });
911 }
912
Walter Jang23709542015-10-22 12:50:58 -0700913 private void addRawContactAccountSelector(String accountsSummary,
914 final RawContactAccountListAdapter adapter) {
Tingting Wang655ad1a2015-10-05 17:51:14 -0700915 mRawContactContainer.setVisibility(View.VISIBLE);
916
Tingting Wang655ad1a2015-10-05 17:51:14 -0700917 mRawContactSummary.setText(accountsSummary);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700918
919 mRawContactContainer.setOnClickListener(new View.OnClickListener() {
920 @Override
921 public void onClick(View v) {
922 final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700923 popup.setWidth(mRawContactContainer.getWidth());
924 popup.setAnchorView(mRawContactContainer);
925 popup.setAdapter(adapter);
926 popup.setModal(true);
927 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
928 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
929 @Override
930 public void onItemClick(AdapterView<?> parent, View view, int position,
931 long id) {
932 UiClosables.closeQuietly(popup);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700933
934 if (mListener != null) {
Tingting Wang91cee282015-10-07 13:48:17 -0700935 final long rawContactId = adapter.getItemId(position);
936 final Uri rawContactUri = ContentUris.withAppendedId(
937 ContactsContract.RawContacts.CONTENT_URI, rawContactId);
938 final RawContactDelta rawContactDelta = adapter.getItem(position);
939 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
940 getContext());
941 final AccountType accountType = rawContactDelta.getAccountType(
942 accountTypes);
943 final boolean isReadOnly = !accountType.areContactsWritable();
944
945 mListener.onRawContactSelected(rawContactUri, rawContactId, isReadOnly);
Tingting Wang655ad1a2015-10-05 17:51:14 -0700946 }
947 }
948 });
949 popup.show();
950 }
951 });
952 }
953
Walter Jang363d3fd2015-09-16 10:29:07 -0700954 private void addPhotoView() {
Gary Mai98868d32016-09-14 11:55:04 -0700955 // Get the kind section data and values delta that we will display in the photo view. Either
956 // the aggregate photo or the photo from the raw contact that is being edited.
957 final Pair<KindSectionData, ValuesDelta> photo =
958 mPhotoKindSectionDataList.getEntryToDisplay(
959 mRawContactIdToDisplayAlone > 0
960 ? mRawContactIdToDisplayAlone
961 : mAggregatePhotoId);
962 if (photo == null) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700963 wlog("photo: no kind section data parsed");
Walter Jangcbd431d2015-10-06 13:07:30 -0700964 mPhotoView.setVisibility(View.GONE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700965 return;
Marcus Hagerottcf7f2952016-09-06 13:49:17 -0700966 } else {
967 mPhotoView.setVisibility(View.VISIBLE);
Walter Jang363d3fd2015-09-16 10:29:07 -0700968 }
Walter Jang06f73a12015-06-17 11:15:48 -0700969
Walter Jang31a74ad2015-10-02 19:17:39 -0700970 // Set the photo view
Gary Mai98868d32016-09-14 11:55:04 -0700971 mPhotoView.setPhoto(photo.second, mMaterialPalette);
Walter Jang31a74ad2015-10-02 19:17:39 -0700972
Gary Mai98868d32016-09-14 11:55:04 -0700973 // If we're showing an aggregate photo, set it to read only.
974 if (mRawContactIdToDisplayAlone < 1) {
Walter Jang31a74ad2015-10-02 19:17:39 -0700975 mPhotoView.setReadOnly(true);
976 return;
977 }
Walter Jang31a74ad2015-10-02 19:17:39 -0700978 mPhotoView.setReadOnly(false);
Gary Mai98868d32016-09-14 11:55:04 -0700979 mPhotoRawContactId = photo.first.getRawContactDelta().getRawContactId();
980 mPhotoValuesDelta = photo.second;
Walter Jang31a74ad2015-10-02 19:17:39 -0700981 }
982
Walter Jangf5dfea42015-09-16 12:30:36 -0700983 private void addKindSectionViews() {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700984 int i = -1;
Gary Mai98868d32016-09-14 11:55:04 -0700985
986 for (String mimeType : mSortedMimetypes) {
Walter Jang3e5ae0d2015-09-20 12:43:37 -0700987 i++;
Gary Mai98868d32016-09-14 11:55:04 -0700988 final CompactKindSectionView kindSectionView;
989 // TODO: Since we don't have a primary name kind anymore, refactor and collapse
990 // these two branches and the following code paths.
Tingting Wangf695eb32015-10-15 18:45:15 -0700991 if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
Gary Mai98868d32016-09-14 11:55:04 -0700992 final KindSectionData nameSectionData = mKindSectionDataMap.get(mimeType);
993 if (nameSectionData == null) {
Walter Jang82acd422015-10-17 14:01:27 -0700994 vlog("kind: " + i + " " + mimeType + " dropped");
995 continue;
Tingting Wangf695eb32015-10-15 18:45:15 -0700996 }
Walter Jang82acd422015-10-17 14:01:27 -0700997 vlog("kind: " + i + " " + mimeType + " using first entry only");
Gary Mai98868d32016-09-14 11:55:04 -0700998 kindSectionView = inflateKindSectionView(
999 mKindSectionViews, nameSectionData, mimeType,
1000 nameSectionData.getValuesDeltas().get(0));
Walter Jang82acd422015-10-17 14:01:27 -07001001 } else {
Gary Mai98868d32016-09-14 11:55:04 -07001002 final KindSectionData kindSectionData = mKindSectionDataMap.get(mimeType);
Walter Jang82acd422015-10-17 14:01:27 -07001003
1004 // Ignore mime types that we've already handled
1005 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
1006 vlog("kind: " + i + " " + mimeType + " dropped");
1007 continue;
1008 }
Gary Mai98868d32016-09-14 11:55:04 -07001009 kindSectionView = inflateKindSectionView(
1010 mKindSectionViews, kindSectionData, mimeType,
1011 /* primaryValueDelta =*/ null);
Walter Jangcab3dce2015-02-09 17:48:03 -08001012 }
Gary Mai98868d32016-09-14 11:55:04 -07001013 mKindSectionViews.addView(kindSectionView);
Walter Jangcab3dce2015-02-09 17:48:03 -08001014
Gary Mai98868d32016-09-14 11:55:04 -07001015 // Keep a pointer to the KindSectionView for each mimeType
1016 mKindSectionViewMap.put(mimeType, kindSectionView);
Walter Jangf10ca152015-09-22 15:23:55 -07001017 }
Walter Jangf10ca152015-09-22 15:23:55 -07001018 }
1019
Walter Jangf5dfea42015-09-16 12:30:36 -07001020 private CompactKindSectionView inflateKindSectionView(ViewGroup viewGroup,
Gary Mai98868d32016-09-14 11:55:04 -07001021 KindSectionData kindSectionData, String mimeType,
Walter Jang82acd422015-10-17 14:01:27 -07001022 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
Gary Mai98868d32016-09-14 11:55:04 -07001039 kindSectionView.setState(kindSectionData, mViewIdGenerator, mListener,
Walter Jang82acd422015-10-17 14:01:27 -07001040 primaryValuesDelta);
Walter Jangf5dfea42015-09-16 12:30:36 -07001041
1042 return kindSectionView;
Walter Jangcab3dce2015-02-09 17:48:03 -08001043 }
1044
Walter Jange3945952015-10-27 12:44:54 -07001045 void maybeSetReadOnlyDisplayNameAsPrimary(String readOnlyDisplayName) {
1046 if (TextUtils.isEmpty(readOnlyDisplayName)) return;
1047 final CompactKindSectionView primaryNameKindSectionView = getPrimaryNameKindSectionView();
1048 if (primaryNameKindSectionView != null && primaryNameKindSectionView.isEmptyName()) {
1049 vlog("name: using read only display name as primary name");
1050 primaryNameKindSectionView.setName(readOnlyDisplayName);
1051 }
1052 }
1053
1054 private CompactKindSectionView getPrimaryNameKindSectionView() {
Gary Mai98868d32016-09-14 11:55:04 -07001055 return mKindSectionViewMap.get(StructuredName.CONTENT_ITEM_TYPE);
Walter Jang4f5594a2015-10-06 18:40:31 -07001056 }
1057
Walter Jangd6753152015-10-02 09:23:13 -07001058 private void showAllFields() {
Walter Jang79658e12015-09-24 10:36:26 -07001059 // Stop hiding empty editors and allow the user to enter values for all kinds now
1060 for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
1061 final CompactKindSectionView kindSectionView =
1062 (CompactKindSectionView) mKindSectionViews.getChildAt(i);
1063 kindSectionView.setHideWhenEmpty(false);
1064 kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
1065 }
1066 mIsExpanded = true;
Walter Jang79658e12015-09-24 10:36:26 -07001067
Walter Jangf5dfea42015-09-16 12:30:36 -07001068 // Hide the more fields button
1069 mMoreFields.setVisibility(View.GONE);
Walter Jangcab3dce2015-02-09 17:48:03 -08001070 }
1071
Marcus Hagerottcf7f2952016-09-06 13:49:17 -07001072 private boolean hasMoreFields() {
Gary Mai98868d32016-09-14 11:55:04 -07001073 for (CompactKindSectionView section : mKindSectionViewMap.values()) {
1074 if (section.getVisibility() != View.VISIBLE) {
1075 return true;
Marcus Hagerottcf7f2952016-09-06 13:49:17 -07001076 }
1077 }
1078 return false;
1079 }
1080
Walter Jangbf63a6d2015-05-05 09:14:35 -07001081 private static void vlog(String message) {
1082 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1083 Log.v(TAG, message);
Walter Jangcab3dce2015-02-09 17:48:03 -08001084 }
1085 }
Walter Jang363d3fd2015-09-16 10:29:07 -07001086
1087 private static void wlog(String message) {
1088 if (Log.isLoggable(TAG, Log.WARN)) {
1089 Log.w(TAG, message);
1090 }
1091 }
1092
1093 private static void elog(String message) {
1094 Log.e(TAG, message);
1095 }
Walter Jangcab3dce2015-02-09 17:48:03 -08001096}