blob: 363fe8b7c7aabbd54fac2ff217cd4516caeb5a16 [file] [log] [blame]
Walter Jangcab3dce2015-02-09 17:48:03 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.contacts.editor;
18
19import com.android.contacts.R;
20import com.android.contacts.common.model.AccountTypeManager;
21import com.android.contacts.common.model.RawContactDelta;
22import com.android.contacts.common.model.RawContactDeltaList;
Walter Jange720fde2015-02-17 10:54:14 -080023import com.android.contacts.common.model.RawContactModifier;
Walter Jangcab3dce2015-02-09 17:48:03 -080024import com.android.contacts.common.model.ValuesDelta;
25import com.android.contacts.common.model.account.AccountType;
26import com.android.contacts.common.model.account.AccountType.EditField;
27import com.android.contacts.common.model.dataitem.DataKind;
28
29import android.content.Context;
30import android.provider.ContactsContract.CommonDataKinds.Email;
31import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
32import android.provider.ContactsContract.CommonDataKinds.Phone;
33import android.provider.ContactsContract.CommonDataKinds.Photo;
34import android.provider.ContactsContract.CommonDataKinds.Nickname;
35import android.provider.ContactsContract.CommonDataKinds.StructuredName;
36import android.text.TextUtils;
37import android.util.AttributeSet;
38import android.util.Log;
39import android.view.LayoutInflater;
Walter Jangb1c87622015-02-13 17:51:38 -080040import android.view.View;
Walter Jangcab3dce2015-02-09 17:48:03 -080041import android.view.ViewGroup;
42import android.widget.LinearLayout;
43
44import java.util.ArrayList;
45import java.util.List;
46
47/**
48 * View to display information from multiple {@link RawContactDelta}s grouped together
49 * (e.g. all the phone numbers from a {@link com.android.contacts.common.model.Contact} together.
50 */
Walter Jangb6ca2722015-02-20 11:10:25 -080051public class CompactRawContactsEditorView extends LinearLayout implements View.OnClickListener {
Walter Jangcab3dce2015-02-09 17:48:03 -080052
53 private static final String TAG = "CompactEditorView";
54
Walter Jangb6ca2722015-02-20 11:10:25 -080055 /**
56 * Callbacks for hosts of {@link CompactRawContactsEditorView}s.
57 */
58 public interface Listener {
59
60 /**
61 * Invoked when the compact editor should be expanded to show all fields.
62 */
63 public void onExpandEditor();
64 }
65
66 private Listener mListener;
67
Walter Jangcab3dce2015-02-09 17:48:03 -080068 private AccountTypeManager mAccountTypeManager;
69 private LayoutInflater mLayoutInflater;
70 private ViewIdGenerator mViewIdGenerator;
71
72 private ViewGroup mNames;
Walter Jangb1c87622015-02-13 17:51:38 -080073 private ViewGroup mPhoneticNames;
74 private ViewGroup mNicknames;
Walter Jangcab3dce2015-02-09 17:48:03 -080075 private ViewGroup mPhoneNumbers;
76 private ViewGroup mEmails;
77 private ViewGroup mOther;
Walter Jangb6ca2722015-02-20 11:10:25 -080078 private View mMoreFields;
Walter Jangcab3dce2015-02-09 17:48:03 -080079
80 public CompactRawContactsEditorView(Context context) {
81 super(context);
82 }
83
84 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
85 super(context, attrs);
86 }
87
Walter Jangb6ca2722015-02-20 11:10:25 -080088 /**
89 * Sets the receiver for {@link CompactRawContactsEditorView} callbacks.
90 */
91 public void setListener(Listener listener) {
92 mListener = listener;
93 }
94
Walter Jangcab3dce2015-02-09 17:48:03 -080095 @Override
96 protected void onFinishInflate() {
97 super.onFinishInflate();
98
99 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
100 mLayoutInflater = (LayoutInflater)
101 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
102
103 mNames = (LinearLayout) findViewById(R.id.names);
Walter Jangb1c87622015-02-13 17:51:38 -0800104 mPhoneticNames = (LinearLayout) findViewById(R.id.phonetic_names);
105 mNicknames = (LinearLayout) findViewById(R.id.nicknames);
Walter Jangcab3dce2015-02-09 17:48:03 -0800106 mPhoneNumbers = (LinearLayout) findViewById(R.id.phone_numbers);
107 mEmails = (LinearLayout) findViewById(R.id.emails);
108 mOther = (LinearLayout) findViewById(R.id.other);
Walter Jangb6ca2722015-02-20 11:10:25 -0800109 mMoreFields = findViewById(R.id.more_fields);
110 mMoreFields.setOnClickListener(this);
111 }
112
113
114 @Override
115 public void onClick(View view) {
116 if (view.getId() == R.id.more_fields && mListener != null ) {
117 mListener.onExpandEditor();
118 }
Walter Jangcab3dce2015-02-09 17:48:03 -0800119 }
120
121 @Override
122 public void setEnabled(boolean enabled) {
123 super.setEnabled(enabled);
124 setEnabled(enabled, mNames);
Walter Jangb1c87622015-02-13 17:51:38 -0800125 setEnabled(enabled, mPhoneticNames);
126 setEnabled(enabled, mNicknames);
Walter Jangcab3dce2015-02-09 17:48:03 -0800127 setEnabled(enabled, mPhoneNumbers);
128 setEnabled(enabled, mEmails);
129 setEnabled(enabled, mOther);
130 }
131
132 private void setEnabled(boolean enabled, ViewGroup viewGroup) {
133 if (viewGroup != null) {
134 final int childCount = viewGroup.getChildCount();
135 for (int i = 0; i < childCount; i++) {
136 viewGroup.getChildAt(i).setEnabled(enabled);
137 }
138 }
139 }
140
141 public void setState(RawContactDeltaList rawContactDeltas, ViewIdGenerator viewIdGenerator) {
142 mNames.removeAllViews();
Walter Jangb1c87622015-02-13 17:51:38 -0800143 mPhoneticNames.removeAllViews();
144 mNicknames.removeAllViews();
Walter Jangcab3dce2015-02-09 17:48:03 -0800145 mPhoneNumbers.removeAllViews();
146 mEmails.removeAllViews();
147 mOther.removeAllViews();
148
149 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
150 return;
151 }
152
153 mViewIdGenerator = viewIdGenerator;
Walter Jangcab3dce2015-02-09 17:48:03 -0800154 setId(mViewIdGenerator.getId(rawContactDeltas.get(0), /* dataKind =*/ null,
155 /* valuesDelta =*/ null, ViewIdGenerator.NO_VIEW_INDEX));
156
Walter Jange720fde2015-02-17 10:54:14 -0800157 addEditorViews(rawContactDeltas, viewIdGenerator);
158 removeExtraEmptyTextFields(mNames);
159 removeExtraEmptyTextFields(mPhoneNumbers);
160 removeExtraEmptyTextFields(mEmails);
Walter Jangcab3dce2015-02-09 17:48:03 -0800161 }
162
Walter Jange720fde2015-02-17 10:54:14 -0800163 private void addEditorViews(RawContactDeltaList rawContactDeltas,
164 ViewIdGenerator viewIdGenerator) {
Walter Jangcab3dce2015-02-09 17:48:03 -0800165 for (RawContactDelta rawContactDelta : rawContactDeltas) {
166 if (!rawContactDelta.isVisible()) {
167 continue;
168 }
Walter Jange720fde2015-02-17 10:54:14 -0800169 setId(viewIdGenerator.getId(
170 rawContactDelta, null, null, ViewIdGenerator.NO_VIEW_INDEX));
171
Walter Jangcab3dce2015-02-09 17:48:03 -0800172 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
Walter Jange720fde2015-02-17 10:54:14 -0800173
174 // Make sure we have a StructuredName
175 RawContactModifier.ensureKindExists(
176 rawContactDelta, accountType, StructuredName.CONTENT_ITEM_TYPE);
177
Walter Jangcab3dce2015-02-09 17:48:03 -0800178 for (DataKind dataKind : accountType.getSortedDataKinds()) {
179 if (!dataKind.editable) {
180 continue;
181 }
182 final String mimeType = dataKind.mimeType;
183 log(Log.VERBOSE, mimeType + " " + dataKind.fieldList.size() + " field(s)");
184 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)
185 || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
186 // Photos are handled separately and group membership is not supported
187 continue;
188 } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
189 final ValuesDelta valuesDelta = rawContactDelta.getPrimaryEntry(mimeType);
Walter Jangcab3dce2015-02-09 17:48:03 -0800190 if (valuesDelta != null) {
191 mNames.addView(inflateStructuredNameEditorView(
192 mNames, accountType, valuesDelta, rawContactDelta));
193 }
194 } else if (DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
195 // Use the StructuredName mime type to get values
196 if (hasNonEmptyPrimaryValuesDelta(
197 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind)) {
198 final ValuesDelta valuesDelta = rawContactDelta.getPrimaryEntry(
199 StructuredName.CONTENT_ITEM_TYPE);
Walter Jangb1c87622015-02-13 17:51:38 -0800200 mPhoneticNames.addView(inflatePhoneticNameEditorView(
201 mPhoneticNames, accountType, valuesDelta, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800202 }
203 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
204 if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
Walter Jangb1c87622015-02-13 17:51:38 -0800205 mNicknames.addView(inflateNicknameEditorView(
206 mNicknames, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800207 }
208 } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jange720fde2015-02-17 10:54:14 -0800209 mPhoneNumbers.addView(inflateKindSectionView(
210 mPhoneNumbers, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800211 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
Walter Jange720fde2015-02-17 10:54:14 -0800212 mEmails.addView(inflateKindSectionView(
213 mEmails, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800214 } else if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
215 mOther.addView(inflateKindSectionView(
216 mOther, dataKind, rawContactDelta));
217 }
218 }
219 }
220 }
221
Walter Jange720fde2015-02-17 10:54:14 -0800222 // TODO: avoid inflating extra views and deleting them
223 private void removeExtraEmptyTextFields(ViewGroup viewGroup) {
224 // If there is one (or less) editors, leave it whether it is empty or not
225 if (viewGroup.getChildCount() <= 1) {
Walter Jangb1c87622015-02-13 17:51:38 -0800226 return;
227 }
Walter Jange720fde2015-02-17 10:54:14 -0800228 // Determine if there are any non-empty editors
229 boolean hasAtLeastOneNonEmptyEditorView = false;
230 for (int i = 0; i < viewGroup.getChildCount(); i++) {
231 if (!isEmptyEditorView(viewGroup.getChildAt(i))) {
232 hasAtLeastOneNonEmptyEditorView = true;
Walter Jangb1c87622015-02-13 17:51:38 -0800233 break;
234 }
235 }
Walter Jange720fde2015-02-17 10:54:14 -0800236 if (hasAtLeastOneNonEmptyEditorView) {
237 // There is at least one non-empty editor, remove all the empty ones
238 for (int i = 0; i < viewGroup.getChildCount(); i++) {
239 if (isEmptyEditorView(viewGroup.getChildAt(i))) {
240 viewGroup.getChildAt(i).setVisibility(View.GONE);
Walter Jangb1c87622015-02-13 17:51:38 -0800241 }
242 }
243 } else {
Walter Jange720fde2015-02-17 10:54:14 -0800244 // There is no non-empty editor, keep the first empty view and remove the rest
245 for (int i = 1; i < viewGroup.getChildCount(); i++) {
246 viewGroup.getChildAt(i).setVisibility(View.GONE);
Walter Jangb1c87622015-02-13 17:51:38 -0800247 }
248 }
249 }
250
Walter Jange720fde2015-02-17 10:54:14 -0800251 // TODO: remove this after KindSectionView is rewritten
252 private static boolean isEmptyEditorView(View view) {
253 if (view instanceof TextFieldsEditorView) {
254 final TextFieldsEditorView textFieldsEditorView = (TextFieldsEditorView) view;
255 return textFieldsEditorView.isEmpty();
256 }
257 if (view instanceof KindSectionView) {
258 final KindSectionView kindSectionView = (KindSectionView) view;
259 return kindSectionView.hasEmptyEditor();
260 }
261 return false;
262 }
263
Walter Jangcab3dce2015-02-09 17:48:03 -0800264 private static boolean hasNonEmptyValuesDelta(RawContactDelta rawContactDelta,
265 String mimeType, DataKind dataKind) {
266 return !getNonEmptyValuesDeltas(rawContactDelta, mimeType, dataKind).isEmpty();
267 }
268
269 private static List<ValuesDelta> getNonEmptyValuesDeltas(RawContactDelta rawContactDelta,
270 String mimeType, DataKind dataKind) {
271 final List<ValuesDelta> result = new ArrayList<>();
272 if (rawContactDelta == null) {
273 log(Log.VERBOSE, "Null RawContactDelta");
274 return result;
275 }
276 if (!rawContactDelta.hasMimeEntries(mimeType)) {
277 log(Log.VERBOSE, "No ValueDeltas");
278 return result;
279 }
280 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(mimeType)) {
281 if (valuesDelta == null) {
282 log(Log.VERBOSE, "Null valuesDelta");
283 }
284 for (EditField editField : dataKind.fieldList) {
285 final String column = editField.column;
286 final String value = valuesDelta == null ? null : valuesDelta.getAsString(column);
287 log(Log.VERBOSE, "Field " + column + " empty=" + TextUtils.isEmpty(value) +
288 " value=" + value);
289 if (!TextUtils.isEmpty(value)) {
290 result.add(valuesDelta);
291 }
292 }
293 }
294 return result;
295 }
296
297 private static boolean hasNonEmptyPrimaryValuesDelta(RawContactDelta rawContactDelta,
298 String mimeType, DataKind dataKind) {
299 final ValuesDelta valuesDelta = rawContactDelta.getPrimaryEntry(mimeType);
300 if (valuesDelta == null) {
301 return false;
302 }
303 for (EditField editField : dataKind.fieldList) {
304 final String column = editField.column;
305 final String value = valuesDelta == null ? null : valuesDelta.getAsString(column);
306 log(Log.VERBOSE, "Field (primary) " + column + " empty=" + TextUtils.isEmpty(value) +
307 " value=" + value);
308 if (!TextUtils.isEmpty(value)) {
309 return true;
310 }
311 }
312 return false;
313 }
314
315 private StructuredNameEditorView inflateStructuredNameEditorView(ViewGroup viewGroup,
316 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta) {
317 final StructuredNameEditorView result = (StructuredNameEditorView) mLayoutInflater.inflate(
318 R.layout.structured_name_editor_view, viewGroup, /* attachToRoot =*/ false);
319 result.setValues(
320 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME),
321 valuesDelta,
322 rawContactDelta,
323 /* readOnly =*/ false,
324 mViewIdGenerator);
325 return result;
326 }
327
328 private PhoneticNameEditorView inflatePhoneticNameEditorView(ViewGroup viewGroup,
329 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta) {
330 final PhoneticNameEditorView result = (PhoneticNameEditorView) mLayoutInflater.inflate(
331 R.layout.phonetic_name_editor_view, viewGroup, /* attachToRoot =*/ false);
332 result.setValues(
333 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME),
334 valuesDelta,
335 rawContactDelta,
336 /* readOnly =*/ false,
337 mViewIdGenerator);
338 return result;
339 }
340
Walter Jangb1c87622015-02-13 17:51:38 -0800341 private KindSectionView inflateNicknameEditorView(ViewGroup viewGroup, DataKind dataKind,
342 RawContactDelta rawContactDelta) {
343 final KindSectionView result = (KindSectionView) mLayoutInflater.inflate(
344 R.layout.item_kind_section, viewGroup, /* attachToRoot =*/ false);
345 result.setState(
346 dataKind,
Walter Jangcab3dce2015-02-09 17:48:03 -0800347 rawContactDelta,
348 /* readOnly =*/ false,
349 mViewIdGenerator);
350 return result;
351 }
352
353 private KindSectionView inflateKindSectionView(ViewGroup viewGroup, DataKind dataKind,
354 RawContactDelta rawContactDelta) {
355 final KindSectionView result = (KindSectionView) mLayoutInflater.inflate(
356 R.layout.item_kind_section, viewGroup, /* attachToRoot =*/ false);
357 result.setState(
358 dataKind,
359 rawContactDelta,
360 /* readOnly =*/ false,
361 mViewIdGenerator);
362 return result;
363 }
364
365 private static void log(int level, String message) {
366 log(TAG, level, message);
367 }
368
369 private static void log(String tag, int level, String message) {
370 if (Log.isLoggable(tag, level)) {
371 switch (level) {
372 case Log.VERBOSE:
373 Log.v(tag, message);
374 break;
375 case Log.DEBUG:
376 Log.d(tag, message);
377 break;
378 case Log.INFO:
379 Log.i(tag, message);
380 break;
381 case Log.WARN:
382 Log.w(tag, message);
383 break;
384 case Log.ERROR:
385 Log.e(tag, message);
386 break;
387 default:
388 Log.v(tag, message);
389 break;
390 }
391 }
392 }
393}