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 android.content.Context;
20import android.provider.ContactsContract.CommonDataKinds.StructuredName;
21import android.text.TextUtils;
22import android.util.AttributeSet;
23
24import com.android.contacts.model.RawContactDelta;
25import com.android.contacts.model.RawContactDelta.ValuesDelta;
26import com.android.contacts.model.dataitem.DataKind;
27import com.android.contacts.model.dataitem.StructuredNameDataItem;
28
29/**
30 * A dedicated editor for phonetic name. It is similar to {@link StructuredNameEditorView}.
31 */
32public class PhoneticNameEditorView extends TextFieldsEditorView {
33
34    private static class PhoneticValuesDelta extends ValuesDelta {
35        private ValuesDelta mValues;
36        private String mPhoneticName;
37
38        public PhoneticValuesDelta(ValuesDelta values) {
39            mValues = values;
40            buildPhoneticName();
41        }
42
43        @Override
44        public void put(String key, String value) {
45            if (key.equals(DataKind.PSEUDO_COLUMN_PHONETIC_NAME)) {
46                mPhoneticName = value;
47                parsePhoneticName(value);
48            } else {
49                mValues.put(key, value);
50                buildPhoneticName();
51            }
52        }
53
54        @Override
55        public String getAsString(String key) {
56            if (key.equals(DataKind.PSEUDO_COLUMN_PHONETIC_NAME)) {
57                return mPhoneticName;
58            } else {
59                return mValues.getAsString(key);
60            }
61        }
62
63        private void parsePhoneticName(String value) {
64            StructuredNameDataItem dataItem = PhoneticNameEditorView.parsePhoneticName(value, null);
65            mValues.setPhoneticFamilyName(dataItem.getPhoneticFamilyName());
66            mValues.setPhoneticMiddleName(dataItem.getPhoneticMiddleName());
67            mValues.setPhoneticGivenName(dataItem.getPhoneticGivenName());
68        }
69
70        private void buildPhoneticName() {
71            String family = mValues.getPhoneticFamilyName();
72            String middle = mValues.getPhoneticMiddleName();
73            String given = mValues.getPhoneticGivenName();
74            mPhoneticName = PhoneticNameEditorView.buildPhoneticName(family, middle, given);
75        }
76
77        @Override
78        public Long getId() {
79            return mValues.getId();
80        }
81
82        @Override
83        public boolean isVisible() {
84            return mValues.isVisible();
85        }
86    }
87
88    /**
89     * Parses phonetic name and returns parsed data (family, middle, given) as ContentValues.
90     * Parsed data should be {@link StructuredName#PHONETIC_FAMILY_NAME},
91     * {@link StructuredName#PHONETIC_MIDDLE_NAME}, and
92     * {@link StructuredName#PHONETIC_GIVEN_NAME}.
93     * If this method cannot parse given phoneticName, null values will be stored.
94     *
95     * @param phoneticName Phonetic name to be parsed
96     * @param values ContentValues to be used for storing data. If null, new instance will be
97     * created.
98     * @return ContentValues with parsed data. Those data can be null.
99     */
100    public static StructuredNameDataItem parsePhoneticName(String phoneticName,
101            StructuredNameDataItem item) {
102        String family = null;
103        String middle = null;
104        String given = null;
105
106        if (!TextUtils.isEmpty(phoneticName)) {
107            String[] strings = phoneticName.split(" ", 3);
108            switch (strings.length) {
109                case 1:
110                    family = strings[0];
111                    break;
112                case 2:
113                    family = strings[0];
114                    given = strings[1];
115                    break;
116                case 3:
117                    family = strings[0];
118                    middle = strings[1];
119                    given = strings[2];
120                    break;
121            }
122        }
123
124        if (item == null) {
125            item = new StructuredNameDataItem();
126        }
127        item.setPhoneticFamilyName(family);
128        item.setPhoneticMiddleName(middle);
129        item.setPhoneticGivenName(given);
130        return item;
131    }
132
133    /**
134     * Constructs and returns a phonetic full name from given parts.
135     */
136    public static String buildPhoneticName(String family, String middle, String given) {
137        if (!TextUtils.isEmpty(family) || !TextUtils.isEmpty(middle)
138                || !TextUtils.isEmpty(given)) {
139            StringBuilder sb = new StringBuilder();
140            if (!TextUtils.isEmpty(family)) {
141                sb.append(family.trim()).append(' ');
142            }
143            if (!TextUtils.isEmpty(middle)) {
144                sb.append(middle.trim()).append(' ');
145            }
146            if (!TextUtils.isEmpty(given)) {
147                sb.append(given.trim()).append(' ');
148            }
149            sb.setLength(sb.length() - 1);  // Yank the last space
150            return sb.toString();
151        } else {
152            return null;
153        }
154    }
155
156    public static boolean isUnstructuredPhoneticNameColumn(String column) {
157        return DataKind.PSEUDO_COLUMN_PHONETIC_NAME.equals(column);
158    }
159
160    public PhoneticNameEditorView(Context context) {
161        super(context);
162    }
163
164    public PhoneticNameEditorView(Context context, AttributeSet attrs) {
165        super(context, attrs);
166    }
167
168    public PhoneticNameEditorView(Context context, AttributeSet attrs, int defStyle) {
169        super(context, attrs, defStyle);
170    }
171
172    @Override
173    public void setValues(DataKind kind, ValuesDelta entry, RawContactDelta state, boolean readOnly,
174            ViewIdGenerator vig) {
175        if (!(entry instanceof PhoneticValuesDelta)) {
176            entry = new PhoneticValuesDelta(entry);
177        }
178        super.setValues(kind, entry, state, readOnly, vig);
179    }
180
181    @Override
182    public void onFieldChanged(String column, String value) {
183        if (!isFieldChanged(column, value)) {
184            return;
185        }
186
187        if (hasShortAndLongForms()) {
188            PhoneticValuesDelta entry = (PhoneticValuesDelta) getEntry();
189
190            // Determine whether the user is modifying the structured or unstructured phonetic
191            // name field. See a similar approach in {@link StructuredNameEditor#onFieldChanged}.
192            // This is because on device rotation, a hidden TextView's onRestoreInstanceState() will
193            // be called and incorrectly restore a null value for the hidden field, which ultimately
194            // modifies the underlying phonetic name. Hence, ignore onFieldChanged() update requests
195            // from fields that aren't visible.
196            boolean isEditingUnstructuredPhoneticName = !areOptionalFieldsVisible();
197
198            if (isEditingUnstructuredPhoneticName == isUnstructuredPhoneticNameColumn(column)) {
199                // Call into the superclass to update the field and rebuild the underlying
200                // phonetic name.
201                super.onFieldChanged(column, value);
202            }
203        } else {
204            // All fields are always visible, so we don't have to worry about blocking updates
205            // from onRestoreInstanceState() from hidden fields. Always call into the superclass
206            // to update the field and rebuild the underlying phonetic name.
207            super.onFieldChanged(column, value);
208        }
209    }
210
211    public boolean hasData() {
212        ValuesDelta entry = getEntry();
213
214        String family = entry.getPhoneticFamilyName();
215        String middle = entry.getPhoneticMiddleName();
216        String given = entry.getPhoneticGivenName();
217
218        return !TextUtils.isEmpty(family) || !TextUtils.isEmpty(middle)
219                || !TextUtils.isEmpty(given);
220    }
221}
222