blob: cbe2ee461cb8e799dcd67ab49df347abc5084dc6 [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;
23import com.android.contacts.common.model.ValuesDelta;
24import com.android.contacts.common.model.account.AccountType;
25import com.android.contacts.common.model.account.AccountType.EditField;
26import com.android.contacts.common.model.dataitem.DataKind;
27
28import android.content.Context;
29import android.provider.ContactsContract.CommonDataKinds.Email;
30import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
31import android.provider.ContactsContract.CommonDataKinds.Phone;
32import android.provider.ContactsContract.CommonDataKinds.Photo;
33import android.provider.ContactsContract.CommonDataKinds.Nickname;
34import android.provider.ContactsContract.CommonDataKinds.StructuredName;
35import android.text.TextUtils;
36import android.util.AttributeSet;
37import android.util.Log;
38import android.view.LayoutInflater;
Walter Jangb1c87622015-02-13 17:51:38 -080039import android.view.View;
Walter Jangcab3dce2015-02-09 17:48:03 -080040import android.view.ViewGroup;
41import android.widget.LinearLayout;
42
43import java.util.ArrayList;
44import java.util.List;
45
46/**
47 * View to display information from multiple {@link RawContactDelta}s grouped together
48 * (e.g. all the phone numbers from a {@link com.android.contacts.common.model.Contact} together.
49 */
50public class CompactRawContactsEditorView extends LinearLayout {
51
52 private static final String TAG = "CompactEditorView";
53
54 private AccountTypeManager mAccountTypeManager;
55 private LayoutInflater mLayoutInflater;
56 private ViewIdGenerator mViewIdGenerator;
57
58 private ViewGroup mNames;
Walter Jangb1c87622015-02-13 17:51:38 -080059 private ViewGroup mPhoneticNames;
60 private ViewGroup mNicknames;
Walter Jangcab3dce2015-02-09 17:48:03 -080061 private ViewGroup mPhoneNumbers;
62 private ViewGroup mEmails;
63 private ViewGroup mOther;
64
65 public CompactRawContactsEditorView(Context context) {
66 super(context);
67 }
68
69 public CompactRawContactsEditorView(Context context, AttributeSet attrs) {
70 super(context, attrs);
71 }
72
73 @Override
74 protected void onFinishInflate() {
75 super.onFinishInflate();
76
77 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
78 mLayoutInflater = (LayoutInflater)
79 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
80
81 mNames = (LinearLayout) findViewById(R.id.names);
Walter Jangb1c87622015-02-13 17:51:38 -080082 mPhoneticNames = (LinearLayout) findViewById(R.id.phonetic_names);
83 mNicknames = (LinearLayout) findViewById(R.id.nicknames);
Walter Jangcab3dce2015-02-09 17:48:03 -080084 mPhoneNumbers = (LinearLayout) findViewById(R.id.phone_numbers);
85 mEmails = (LinearLayout) findViewById(R.id.emails);
86 mOther = (LinearLayout) findViewById(R.id.other);
87 }
88
89 @Override
90 public void setEnabled(boolean enabled) {
91 super.setEnabled(enabled);
92 setEnabled(enabled, mNames);
Walter Jangb1c87622015-02-13 17:51:38 -080093 setEnabled(enabled, mPhoneticNames);
94 setEnabled(enabled, mNicknames);
Walter Jangcab3dce2015-02-09 17:48:03 -080095 setEnabled(enabled, mPhoneNumbers);
96 setEnabled(enabled, mEmails);
97 setEnabled(enabled, mOther);
98 }
99
100 private void setEnabled(boolean enabled, ViewGroup viewGroup) {
101 if (viewGroup != null) {
102 final int childCount = viewGroup.getChildCount();
103 for (int i = 0; i < childCount; i++) {
104 viewGroup.getChildAt(i).setEnabled(enabled);
105 }
106 }
107 }
108
109 public void setState(RawContactDeltaList rawContactDeltas, ViewIdGenerator viewIdGenerator) {
110 mNames.removeAllViews();
Walter Jangb1c87622015-02-13 17:51:38 -0800111 mPhoneticNames.removeAllViews();
112 mNicknames.removeAllViews();
Walter Jangcab3dce2015-02-09 17:48:03 -0800113 mPhoneNumbers.removeAllViews();
114 mEmails.removeAllViews();
115 mOther.removeAllViews();
116
117 if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
118 return;
119 }
120
121 mViewIdGenerator = viewIdGenerator;
122
123 setId(mViewIdGenerator.getId(rawContactDeltas.get(0), /* dataKind =*/ null,
124 /* valuesDelta =*/ null, ViewIdGenerator.NO_VIEW_INDEX));
125
126 addEditorViews(rawContactDeltas);
Walter Jangb1c87622015-02-13 17:51:38 -0800127 removeExtraEmptyStructuredNames();
Walter Jangcab3dce2015-02-09 17:48:03 -0800128 }
129
130 private void addEditorViews(RawContactDeltaList rawContactDeltas) {
131 for (RawContactDelta rawContactDelta : rawContactDeltas) {
132 if (!rawContactDelta.isVisible()) {
133 continue;
134 }
135 final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
136 for (DataKind dataKind : accountType.getSortedDataKinds()) {
137 if (!dataKind.editable) {
138 continue;
139 }
140 final String mimeType = dataKind.mimeType;
141 log(Log.VERBOSE, mimeType + " " + dataKind.fieldList.size() + " field(s)");
142 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)
143 || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
144 // Photos are handled separately and group membership is not supported
145 continue;
146 } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
147 final ValuesDelta valuesDelta = rawContactDelta.getPrimaryEntry(mimeType);
Walter Jangcab3dce2015-02-09 17:48:03 -0800148 if (valuesDelta != null) {
149 mNames.addView(inflateStructuredNameEditorView(
150 mNames, accountType, valuesDelta, rawContactDelta));
151 }
152 } else if (DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
153 // Use the StructuredName mime type to get values
154 if (hasNonEmptyPrimaryValuesDelta(
155 rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind)) {
156 final ValuesDelta valuesDelta = rawContactDelta.getPrimaryEntry(
157 StructuredName.CONTENT_ITEM_TYPE);
Walter Jangb1c87622015-02-13 17:51:38 -0800158 mPhoneticNames.addView(inflatePhoneticNameEditorView(
159 mPhoneticNames, accountType, valuesDelta, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800160 }
161 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
162 if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
Walter Jangb1c87622015-02-13 17:51:38 -0800163 mNicknames.addView(inflateNicknameEditorView(
164 mNicknames, dataKind, rawContactDelta));
Walter Jangcab3dce2015-02-09 17:48:03 -0800165 }
166 } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
167 if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
168 mPhoneNumbers.addView(inflateKindSectionView(
169 mPhoneNumbers, dataKind, rawContactDelta));
170 }
171 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
172 if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
173 mEmails.addView(inflateKindSectionView(
174 mEmails, dataKind, rawContactDelta));
175 }
176 } else if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
177 mOther.addView(inflateKindSectionView(
178 mOther, dataKind, rawContactDelta));
179 }
180 }
181 }
182 }
183
Walter Jangb1c87622015-02-13 17:51:38 -0800184 private void removeExtraEmptyStructuredNames() {
185 // If there is one (or less) structured names, leave it whether it is empty or not
186 if (mNames.getChildCount() <= 1) {
187 return;
188 }
189 // Determine if there are any non-empty names
190 boolean hasAtLeastOneNonEmptyName = false;
191 for (int i = 0; i < mNames.getChildCount(); i++) {
192 final StructuredNameEditorView childView =
193 (StructuredNameEditorView) mNames.getChildAt(i);
194 if (!childView.isEmpty()) {
195 hasAtLeastOneNonEmptyName = true;
196 break;
197 }
198 }
199 if (hasAtLeastOneNonEmptyName) {
200 // There is at least one non-empty name, remove all the empty ones
201 for (int i = 0; i < mNames.getChildCount(); i++) {
202 final StructuredNameEditorView childView =
203 (StructuredNameEditorView) mNames.getChildAt(i);
204 if (childView.isEmpty()) {
205 childView.setVisibility(View.GONE);
206 }
207 }
208 } else {
209 // There is no non-empty name, keep the first empty view and remove the rest
210 for (int i = 1; i < mNames.getChildCount(); i++) {
211 final StructuredNameEditorView childView =
212 (StructuredNameEditorView) mNames.getChildAt(i);
213 childView.setVisibility(View.GONE);
214 }
215 }
216 }
217
Walter Jangcab3dce2015-02-09 17:48:03 -0800218 private static boolean hasNonEmptyValuesDelta(RawContactDelta rawContactDelta,
219 String mimeType, DataKind dataKind) {
220 return !getNonEmptyValuesDeltas(rawContactDelta, mimeType, dataKind).isEmpty();
221 }
222
223 private static List<ValuesDelta> getNonEmptyValuesDeltas(RawContactDelta rawContactDelta,
224 String mimeType, DataKind dataKind) {
225 final List<ValuesDelta> result = new ArrayList<>();
226 if (rawContactDelta == null) {
227 log(Log.VERBOSE, "Null RawContactDelta");
228 return result;
229 }
230 if (!rawContactDelta.hasMimeEntries(mimeType)) {
231 log(Log.VERBOSE, "No ValueDeltas");
232 return result;
233 }
234 for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(mimeType)) {
235 if (valuesDelta == null) {
236 log(Log.VERBOSE, "Null valuesDelta");
237 }
238 for (EditField editField : dataKind.fieldList) {
239 final String column = editField.column;
240 final String value = valuesDelta == null ? null : valuesDelta.getAsString(column);
241 log(Log.VERBOSE, "Field " + column + " empty=" + TextUtils.isEmpty(value) +
242 " value=" + value);
243 if (!TextUtils.isEmpty(value)) {
244 result.add(valuesDelta);
245 }
246 }
247 }
248 return result;
249 }
250
251 private static boolean hasNonEmptyPrimaryValuesDelta(RawContactDelta rawContactDelta,
252 String mimeType, DataKind dataKind) {
253 final ValuesDelta valuesDelta = rawContactDelta.getPrimaryEntry(mimeType);
254 if (valuesDelta == null) {
255 return false;
256 }
257 for (EditField editField : dataKind.fieldList) {
258 final String column = editField.column;
259 final String value = valuesDelta == null ? null : valuesDelta.getAsString(column);
260 log(Log.VERBOSE, "Field (primary) " + column + " empty=" + TextUtils.isEmpty(value) +
261 " value=" + value);
262 if (!TextUtils.isEmpty(value)) {
263 return true;
264 }
265 }
266 return false;
267 }
268
269 private StructuredNameEditorView inflateStructuredNameEditorView(ViewGroup viewGroup,
270 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta) {
271 final StructuredNameEditorView result = (StructuredNameEditorView) mLayoutInflater.inflate(
272 R.layout.structured_name_editor_view, viewGroup, /* attachToRoot =*/ false);
273 result.setValues(
274 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME),
275 valuesDelta,
276 rawContactDelta,
277 /* readOnly =*/ false,
278 mViewIdGenerator);
279 return result;
280 }
281
282 private PhoneticNameEditorView inflatePhoneticNameEditorView(ViewGroup viewGroup,
283 AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta) {
284 final PhoneticNameEditorView result = (PhoneticNameEditorView) mLayoutInflater.inflate(
285 R.layout.phonetic_name_editor_view, viewGroup, /* attachToRoot =*/ false);
286 result.setValues(
287 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME),
288 valuesDelta,
289 rawContactDelta,
290 /* readOnly =*/ false,
291 mViewIdGenerator);
292 return result;
293 }
294
Walter Jangb1c87622015-02-13 17:51:38 -0800295 private KindSectionView inflateNicknameEditorView(ViewGroup viewGroup, DataKind dataKind,
296 RawContactDelta rawContactDelta) {
297 final KindSectionView result = (KindSectionView) mLayoutInflater.inflate(
298 R.layout.item_kind_section, viewGroup, /* attachToRoot =*/ false);
299 result.setState(
300 dataKind,
Walter Jangcab3dce2015-02-09 17:48:03 -0800301 rawContactDelta,
302 /* readOnly =*/ false,
303 mViewIdGenerator);
304 return result;
305 }
306
307 private KindSectionView inflateKindSectionView(ViewGroup viewGroup, DataKind dataKind,
308 RawContactDelta rawContactDelta) {
309 final KindSectionView result = (KindSectionView) mLayoutInflater.inflate(
310 R.layout.item_kind_section, viewGroup, /* attachToRoot =*/ false);
311 result.setState(
312 dataKind,
313 rawContactDelta,
314 /* readOnly =*/ false,
315 mViewIdGenerator);
316 return result;
317 }
318
319 private static void log(int level, String message) {
320 log(TAG, level, message);
321 }
322
323 private static void log(String tag, int level, String message) {
324 if (Log.isLoggable(tag, level)) {
325 switch (level) {
326 case Log.VERBOSE:
327 Log.v(tag, message);
328 break;
329 case Log.DEBUG:
330 Log.d(tag, message);
331 break;
332 case Log.INFO:
333 Log.i(tag, message);
334 break;
335 case Log.WARN:
336 Log.w(tag, message);
337 break;
338 case Log.ERROR:
339 Log.e(tag, message);
340 break;
341 default:
342 Log.v(tag, message);
343 break;
344 }
345 }
346 }
347}