StructuredNameEditorView.java revision b05466a9349441e79fb12d60abdf669168b59593
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
17package com.android.contacts.editor;
18
19import com.android.contacts.model.DataKind;
20import com.android.contacts.model.EntityDelta;
21import com.android.contacts.model.EntityDelta.ValuesDelta;
22import com.android.contacts.util.NameConverter;
23
24import android.content.ContentValues;
25import android.content.Context;
26import android.net.Uri;
27import android.os.Parcel;
28import android.os.Parcelable;
29import android.provider.ContactsContract.CommonDataKinds.StructuredName;
30import android.text.TextUtils;
31import android.util.AttributeSet;
32
33import java.util.HashMap;
34import java.util.Map;
35
36/**
37 * A dedicated editor for structured name.  When the user collapses/expands
38 * the structured name, it will reparse or recompose the name, but only
39 * if the user has made changes.  This distinction will be particularly
40 * obvious if the name has a non-standard structure. Consider this structure:
41 * first name="John Doe", family name="".  As long as the user does not change
42 * the full name, expand and collapse will preserve this.  However, if the user
43 * changes "John Doe" to "Jane Doe" and then expands the view, we will reparse
44 * and show first name="Jane", family name="Doe".
45 */
46public class StructuredNameEditorView extends TextFieldsEditorView {
47
48    private ContentValues mSnapshot;
49    private boolean mChanged;
50
51    public StructuredNameEditorView(Context context) {
52        super(context);
53    }
54
55    public StructuredNameEditorView(Context context, AttributeSet attrs) {
56        super(context, attrs);
57    }
58
59    public StructuredNameEditorView(Context context, AttributeSet attrs, int defStyle) {
60        super(context, attrs, defStyle);
61    }
62
63    @Override
64    public void setValues(DataKind kind, ValuesDelta entry, EntityDelta state, boolean readOnly,
65            ViewIdGenerator vig) {
66        super.setValues(kind, entry, state, readOnly, vig);
67        if (mSnapshot == null) {
68            mSnapshot = new ContentValues(getValues().getCompleteValues());
69            mChanged = entry.isInsert();
70        } else {
71            mChanged = false;
72        }
73    }
74
75    @Override
76    public void onFieldChanged(String column, String value) {
77        if (!isFieldChanged(column, value)) {
78            return;
79        }
80        super.onFieldChanged(column, value);
81
82        mChanged = true;
83
84        // Make sure the display name and the structured name are synced
85        if (hasShortAndLongForms()) {
86            if (areOptionalFieldsVisible()) {
87                rebuildFullName(getValues());
88            } else {
89                rebuildStructuredName(getValues());
90            }
91        }
92    }
93
94    @Override
95    protected void onOptionalFieldVisibilityChange() {
96        if (hasShortAndLongForms()) {
97            if (areOptionalFieldsVisible()) {
98                switchFromFullNameToStructuredName();
99            } else {
100                switchFromStructuredNameToFullName();
101            }
102        }
103
104        super.onOptionalFieldVisibilityChange();
105    }
106
107    private void switchFromFullNameToStructuredName() {
108        ValuesDelta values = getValues();
109
110        if (!mChanged) {
111            for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
112                values.put(field, mSnapshot.getAsString(field));
113            }
114            return;
115        }
116
117        String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
118        Map<String, String> structuredNameMap = NameConverter.displayNameToStructuredName(
119                getContext(), displayName);
120        if (!structuredNameMap.isEmpty()) {
121            eraseFullName(values);
122            for (String field : structuredNameMap.keySet()) {
123                values.put(field, structuredNameMap.get(field));
124            }
125        }
126
127        mSnapshot.clear();
128        mSnapshot.putAll(values.getCompleteValues());
129        mSnapshot.put(StructuredName.DISPLAY_NAME, displayName);
130    }
131
132    private void switchFromStructuredNameToFullName() {
133        ValuesDelta values = getValues();
134
135        if (!mChanged) {
136            values.put(StructuredName.DISPLAY_NAME,
137                    mSnapshot.getAsString(StructuredName.DISPLAY_NAME));
138            return;
139        }
140
141        Map<String, String> structuredNameMap = valuesToStructuredNameMap(values);
142        String displayName = NameConverter.structuredNameToDisplayName(getContext(),
143                structuredNameMap);
144        if (!TextUtils.isEmpty(displayName)) {
145            eraseStructuredName(values);
146            values.put(StructuredName.DISPLAY_NAME, displayName);
147        }
148
149        mSnapshot.clear();
150        mSnapshot.put(StructuredName.DISPLAY_NAME, values.getAsString(StructuredName.DISPLAY_NAME));
151        for (String field : structuredNameMap.keySet()) {
152            mSnapshot.put(field, structuredNameMap.get(field));
153        }
154    }
155
156    private Map<String, String> valuesToStructuredNameMap(ValuesDelta values) {
157        Map<String, String> structuredNameMap = new HashMap<String, String>();
158        for (String key : NameConverter.STRUCTURED_NAME_FIELDS) {
159            structuredNameMap.put(key, values.getAsString(key));
160        }
161        return structuredNameMap;
162    }
163
164    private void eraseFullName(ValuesDelta values) {
165        values.putNull(StructuredName.DISPLAY_NAME);
166    }
167
168    private void rebuildFullName(ValuesDelta values) {
169        Map<String, String> structuredNameMap = valuesToStructuredNameMap(values);
170        String displayName = NameConverter.structuredNameToDisplayName(getContext(),
171                structuredNameMap);
172        values.put(StructuredName.DISPLAY_NAME, displayName);
173    }
174
175    private void eraseStructuredName(ValuesDelta values) {
176        for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
177            values.putNull(field);
178        }
179    }
180
181    private void rebuildStructuredName(ValuesDelta values) {
182        String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
183        Map<String, String> structuredNameMap = NameConverter.displayNameToStructuredName(
184                getContext(), displayName);
185        for (String field : structuredNameMap.keySet()) {
186            values.put(field, structuredNameMap.get(field));
187        }
188    }
189
190    private static void appendQueryParameter(Uri.Builder builder, String field, String value) {
191        if (!TextUtils.isEmpty(value)) {
192            builder.appendQueryParameter(field, value);
193        }
194    }
195
196    @Override
197    protected Parcelable onSaveInstanceState() {
198        SavedState state = new SavedState(super.onSaveInstanceState());
199        state.mChanged = mChanged;
200        state.mSnapshot = mSnapshot;
201        return state;
202    }
203
204    @Override
205    protected void onRestoreInstanceState(Parcelable state) {
206        SavedState ss = (SavedState) state;
207        super.onRestoreInstanceState(ss.mSuperState);
208
209        mChanged = ss.mChanged;
210        mSnapshot = ss.mSnapshot;
211    }
212
213    private static class SavedState implements Parcelable {
214        public boolean mChanged;
215        public ContentValues mSnapshot;
216        public Parcelable mSuperState;
217
218        SavedState(Parcelable superState) {
219            mSuperState = superState;
220        }
221
222        private SavedState(Parcel in) {
223            ClassLoader loader = getClass().getClassLoader();
224            mSuperState = in.readParcelable(loader);
225
226            mChanged = in.readInt() != 0;
227            mSnapshot = in.readParcelable(loader);
228        }
229
230        @Override
231        public void writeToParcel(Parcel out, int flags) {
232            out.writeParcelable(mSuperState, 0);
233
234            out.writeInt(mChanged ? 1 : 0);
235            out.writeParcelable(mSnapshot, 0);
236        }
237
238        @SuppressWarnings({"unused"})
239        public static final Parcelable.Creator<SavedState> CREATOR
240                = new Parcelable.Creator<SavedState>() {
241            @Override
242            public SavedState createFromParcel(Parcel in) {
243                return new SavedState(in);
244            }
245
246            @Override
247            public SavedState[] newArray(int size) {
248                return new SavedState[size];
249            }
250        };
251
252        @Override
253        public int describeContents() {
254            return 0;
255        }
256    }
257}
258