• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 
17 package com.android.contacts.editor;
18 
19 import android.content.ContentValues;
20 import android.content.Context;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
24 import android.text.TextUtils;
25 import android.util.AttributeSet;
26 
27 import com.android.contacts.common.model.RawContactDelta;
28 import com.android.contacts.common.model.ValuesDelta;
29 import com.android.contacts.common.model.dataitem.DataItem;
30 import com.android.contacts.common.model.dataitem.DataKind;
31 import com.android.contacts.common.util.NameConverter;
32 import com.android.contacts.common.model.dataitem.StructuredNameDataItem;
33 
34 import java.util.HashMap;
35 import java.util.Map;
36 
37 /**
38  * A dedicated editor for structured name.  When the user collapses/expands
39  * the structured name, it will reparse or recompose the name, but only
40  * if the user has made changes.  This distinction will be particularly
41  * obvious if the name has a non-standard structure. Consider this structure:
42  * first name="John Doe", family name="".  As long as the user does not change
43  * the full name, expand and collapse will preserve this.  However, if the user
44  * changes "John Doe" to "Jane Doe" and then expands the view, we will reparse
45  * and show first name="Jane", family name="Doe".
46  */
47 public class StructuredNameEditorView extends TextFieldsEditorView {
48 
49     private StructuredNameDataItem mSnapshot;
50     private boolean mChanged;
51 
StructuredNameEditorView(Context context)52     public StructuredNameEditorView(Context context) {
53         super(context);
54     }
55 
StructuredNameEditorView(Context context, AttributeSet attrs)56     public StructuredNameEditorView(Context context, AttributeSet attrs) {
57         super(context, attrs);
58     }
59 
StructuredNameEditorView(Context context, AttributeSet attrs, int defStyle)60     public StructuredNameEditorView(Context context, AttributeSet attrs, int defStyle) {
61         super(context, attrs, defStyle);
62     }
63 
64     @Override
setValues(DataKind kind, ValuesDelta entry, RawContactDelta state, boolean readOnly, ViewIdGenerator vig)65     public void setValues(DataKind kind, ValuesDelta entry, RawContactDelta state, boolean readOnly,
66             ViewIdGenerator vig) {
67         super.setValues(kind, entry, state, readOnly, vig);
68         if (mSnapshot == null) {
69             mSnapshot = (StructuredNameDataItem) DataItem.createFrom(
70                     new ContentValues(getValues().getCompleteValues()));
71             mChanged = entry.isInsert();
72         } else {
73             mChanged = false;
74         }
75         updateEmptiness();
76     }
77 
78     @Override
onFieldChanged(String column, String value)79     public void onFieldChanged(String column, String value) {
80         if (!isFieldChanged(column, value)) {
81             return;
82         }
83 
84         // First save the new value for the column.
85         saveValue(column, value);
86         mChanged = true;
87 
88         // Next make sure the display name and the structured name are synced
89         if (hasShortAndLongForms()) {
90             if (areOptionalFieldsVisible()) {
91                 rebuildFullName(getValues());
92             } else {
93                 rebuildStructuredName(getValues());
94             }
95         }
96 
97         // Then notify the listener, which will rely on the display and structured names to be
98         // synced (in order to provide aggregate suggestions).
99         notifyEditorListener();
100     }
101 
102     @Override
onOptionalFieldVisibilityChange()103     protected void onOptionalFieldVisibilityChange() {
104         if (hasShortAndLongForms()) {
105             if (areOptionalFieldsVisible()) {
106                 switchFromFullNameToStructuredName();
107             } else {
108                 switchFromStructuredNameToFullName();
109             }
110         }
111 
112         super.onOptionalFieldVisibilityChange();
113     }
114 
switchFromFullNameToStructuredName()115     private void switchFromFullNameToStructuredName() {
116         ValuesDelta values = getValues();
117 
118         if (!mChanged) {
119             for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
120                 values.put(field, mSnapshot.getContentValues().getAsString(field));
121             }
122             return;
123         }
124 
125         String displayName = values.getDisplayName();
126         Map<String, String> structuredNameMap = NameConverter.displayNameToStructuredName(
127                 getContext(), displayName);
128         if (!structuredNameMap.isEmpty()) {
129             eraseFullName(values);
130             for (String field : structuredNameMap.keySet()) {
131                 values.put(field, structuredNameMap.get(field));
132             }
133         }
134 
135         mSnapshot.getContentValues().clear();
136         mSnapshot.getContentValues().putAll(values.getCompleteValues());
137         mSnapshot.setDisplayName(displayName);
138     }
139 
switchFromStructuredNameToFullName()140     private void switchFromStructuredNameToFullName() {
141         ValuesDelta values = getValues();
142 
143         if (!mChanged) {
144             values.setDisplayName(mSnapshot.getDisplayName());
145             return;
146         }
147 
148         Map<String, String> structuredNameMap = valuesToStructuredNameMap(values);
149         String displayName = NameConverter.structuredNameToDisplayName(getContext(),
150                 structuredNameMap);
151         if (!TextUtils.isEmpty(displayName)) {
152             eraseStructuredName(values);
153             values.put(StructuredName.DISPLAY_NAME, displayName);
154         }
155 
156         mSnapshot.getContentValues().clear();
157         mSnapshot.setDisplayName(values.getDisplayName());
158         mSnapshot.setMimeType(StructuredName.CONTENT_ITEM_TYPE);
159         for (String field : structuredNameMap.keySet()) {
160             mSnapshot.getContentValues().put(field, structuredNameMap.get(field));
161         }
162     }
163 
valuesToStructuredNameMap(ValuesDelta values)164     private Map<String, String> valuesToStructuredNameMap(ValuesDelta values) {
165         Map<String, String> structuredNameMap = new HashMap<String, String>();
166         for (String key : NameConverter.STRUCTURED_NAME_FIELDS) {
167             structuredNameMap.put(key, values.getAsString(key));
168         }
169         return structuredNameMap;
170     }
171 
eraseFullName(ValuesDelta values)172     private void eraseFullName(ValuesDelta values) {
173         values.setDisplayName(null);
174     }
175 
rebuildFullName(ValuesDelta values)176     private void rebuildFullName(ValuesDelta values) {
177         Map<String, String> structuredNameMap = valuesToStructuredNameMap(values);
178         String displayName = NameConverter.structuredNameToDisplayName(getContext(),
179                 structuredNameMap);
180         values.setDisplayName(displayName);
181     }
182 
eraseStructuredName(ValuesDelta values)183     private void eraseStructuredName(ValuesDelta values) {
184         for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
185             values.putNull(field);
186         }
187     }
188 
rebuildStructuredName(ValuesDelta values)189     private void rebuildStructuredName(ValuesDelta values) {
190         String displayName = values.getDisplayName();
191         Map<String, String> structuredNameMap = NameConverter.displayNameToStructuredName(
192                 getContext(), displayName);
193         for (String field : structuredNameMap.keySet()) {
194             values.put(field, structuredNameMap.get(field));
195         }
196     }
197 
198     /**
199      * Set the display name onto the text field directly.  This does not affect the underlying
200      * data structure so it is similar to the user typing the value in on the field directly.
201      *
202      * @param name The name to set on the text field.
203      */
setDisplayName(String name)204     public void setDisplayName(String name) {
205         // For now, assume the first text field is the name.
206         // TODO: Find a better way to get a hold of the name field.
207         super.setValue(0, name);
208     }
209 
210     /**
211      * Returns the display name currently displayed in the editor.
212      */
getDisplayName()213     public String getDisplayName() {
214         final ValuesDelta valuesDelta = getValues();
215         if (hasShortAndLongForms()) {
216             if (areOptionalFieldsVisible()) {
217                 final Map<String, String> structuredNameMap = valuesToStructuredNameMap(valuesDelta);
218                 final String displayName = NameConverter.structuredNameToDisplayName(
219                         getContext(), structuredNameMap);
220                 if (!TextUtils.isEmpty(displayName)) {
221                     return displayName;
222                 }
223             } else {
224                 final String displayName = valuesDelta.getDisplayName();
225                 if (!TextUtils.isEmpty(displayName)) {
226                     return displayName;
227                 }
228             }
229         }
230         return valuesDelta.getDisplayName();
231     }
232 
233     @Override
isEmpty()234     public boolean isEmpty() {
235         return TextUtils.isEmpty(getDisplayName());
236     }
237 
238     @Override
onSaveInstanceState()239     protected Parcelable onSaveInstanceState() {
240         SavedState state = new SavedState(super.onSaveInstanceState());
241         state.mChanged = mChanged;
242         state.mSnapshot = mSnapshot.getContentValues();
243         return state;
244     }
245 
246     @Override
onRestoreInstanceState(Parcelable state)247     protected void onRestoreInstanceState(Parcelable state) {
248         SavedState ss = (SavedState) state;
249         super.onRestoreInstanceState(ss.mSuperState);
250 
251         mChanged = ss.mChanged;
252         mSnapshot = (StructuredNameDataItem) DataItem.createFrom(ss.mSnapshot);
253     }
254 
255     private static class SavedState implements Parcelable {
256         public boolean mChanged;
257         public ContentValues mSnapshot;
258         public Parcelable mSuperState;
259 
SavedState(Parcelable superState)260         SavedState(Parcelable superState) {
261             mSuperState = superState;
262         }
263 
SavedState(Parcel in)264         private SavedState(Parcel in) {
265             ClassLoader loader = getClass().getClassLoader();
266             mSuperState = in.readParcelable(loader);
267 
268             mChanged = in.readInt() != 0;
269             mSnapshot = in.readParcelable(loader);
270         }
271 
272         @Override
writeToParcel(Parcel out, int flags)273         public void writeToParcel(Parcel out, int flags) {
274             out.writeParcelable(mSuperState, 0);
275 
276             out.writeInt(mChanged ? 1 : 0);
277             out.writeParcelable(mSnapshot, 0);
278         }
279 
280         @SuppressWarnings({"unused"})
281         public static final Parcelable.Creator<SavedState> CREATOR
282                 = new Parcelable.Creator<SavedState>() {
283             @Override
284             public SavedState createFromParcel(Parcel in) {
285                 return new SavedState(in);
286             }
287 
288             @Override
289             public SavedState[] newArray(int size) {
290                 return new SavedState[size];
291             }
292         };
293 
294         @Override
describeContents()295         public int describeContents() {
296             return 0;
297         }
298     }
299 }
300