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 com.android.contacts.model.DataKind; 20 import com.android.contacts.model.EntityDelta; 21 import com.android.contacts.model.EntityDelta.ValuesDelta; 22 import com.android.contacts.util.NameConverter; 23 24 import android.content.ContentValues; 25 import android.content.Context; 26 import android.net.Uri; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 30 import android.text.TextUtils; 31 import android.util.AttributeSet; 32 33 import java.util.HashMap; 34 import 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 */ 46 public 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